Bug 1885489 - Part 9: Add SnapshotIterator::readObject(). r=iain
[gecko.git] / toolkit / crashreporter / LoadLibraryRemote.cpp
blobd0422edd08e912af352e3c8e7617eb051724df2c
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #ifndef __GNUC__
6 // disable warnings about pointer <-> DWORD conversions
7 # pragma warning(disable : 4311 4312)
8 #endif
10 #ifdef _WIN64
11 # define POINTER_TYPE ULONGLONG
12 #else
13 # define POINTER_TYPE DWORD
14 #endif
16 #include <windows.h>
17 #include <winnt.h>
18 #include <stdlib.h>
19 #ifdef DEBUG_OUTPUT
20 # include <stdio.h>
21 #endif
23 #include "nsWindowsHelpers.h"
25 typedef const unsigned char* FileView;
27 template <>
28 class nsAutoRefTraits<FileView> {
29 public:
30 typedef FileView RawRef;
31 static FileView Void() { return nullptr; }
33 static void Release(RawRef aView) {
34 if (nullptr != aView) UnmapViewOfFile(aView);
38 #ifndef IMAGE_SIZEOF_BASE_RELOCATION
39 // Vista SDKs no longer define IMAGE_SIZEOF_BASE_RELOCATION!?
40 # define IMAGE_SIZEOF_BASE_RELOCATION (sizeof(IMAGE_BASE_RELOCATION))
41 #endif
43 #include "LoadLibraryRemote.h"
45 typedef struct {
46 PIMAGE_NT_HEADERS headers;
47 unsigned char* localCodeBase;
48 unsigned char* remoteCodeBase;
49 HMODULE* modules;
50 int numModules;
51 } MEMORYMODULE, *PMEMORYMODULE;
53 typedef BOOL(WINAPI* DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason,
54 LPVOID lpReserved);
56 #define GET_HEADER_DICTIONARY(module, idx) \
57 &(module)->headers->OptionalHeader.DataDirectory[idx]
59 #ifdef DEBUG_OUTPUT
60 static void OutputLastError(const char* msg) {
61 char* tmp;
62 char* tmpmsg;
63 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
64 FORMAT_MESSAGE_IGNORE_INSERTS,
65 nullptr, GetLastError(),
66 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&tmp, 0,
67 nullptr);
68 tmpmsg = (char*)LocalAlloc(LPTR, strlen(msg) + strlen(tmp) + 3);
69 sprintf(tmpmsg, "%s: %s", msg, tmp);
70 OutputDebugStringA(tmpmsg);
71 LocalFree(tmpmsg);
72 LocalFree(tmp);
74 #endif
76 static void CopySections(const unsigned char* data,
77 PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module) {
78 int i;
79 unsigned char* codeBase = module->localCodeBase;
80 unsigned char* dest;
81 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
82 for (i = 0; i < module->headers->FileHeader.NumberOfSections;
83 i++, section++) {
84 dest = codeBase + section->VirtualAddress;
85 memset(dest, 0, section->Misc.VirtualSize);
86 if (section->SizeOfRawData) {
87 memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData);
89 // section->Misc.PhysicalAddress = (POINTER_TYPE) module->remoteCodeBase +
90 // section->VirtualAddress;
94 static bool CopyRegion(HANDLE hRemoteProcess, void* remoteAddress,
95 void* localAddress, DWORD size, DWORD protect) {
96 if (size > 0) {
97 // Copy the data from local->remote and set the memory protection
98 if (!VirtualAllocEx(hRemoteProcess, remoteAddress, size, MEM_COMMIT,
99 PAGE_READWRITE))
100 return false;
102 if (!WriteProcessMemory(hRemoteProcess, remoteAddress, localAddress, size,
103 nullptr)) {
104 #ifdef DEBUG_OUTPUT
105 OutputLastError("Error writing remote memory.\n");
106 #endif
107 return false;
110 DWORD oldProtect;
111 if (VirtualProtectEx(hRemoteProcess, remoteAddress, size, protect,
112 &oldProtect) == 0) {
113 #ifdef DEBUG_OUTPUT
114 OutputLastError("Error protecting memory page");
115 #endif
116 return false;
119 return true;
121 // Protection flags for memory pages (Executable, Readable, Writeable)
122 static int ProtectionFlags[2][2][2] = {
124 // not executable
125 {PAGE_NOACCESS, PAGE_WRITECOPY},
126 {PAGE_READONLY, PAGE_READWRITE},
129 // executable
130 {PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY},
131 {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE},
135 static bool FinalizeSections(PMEMORYMODULE module, HANDLE hRemoteProcess) {
136 #ifdef DEBUG_OUTPUT
137 fprintf(stderr, "Finalizing sections: local base %p, remote base %p\n",
138 module->localCodeBase, module->remoteCodeBase);
139 #endif
141 int i;
142 int numSections = module->headers->FileHeader.NumberOfSections;
144 if (numSections < 1) return false;
146 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
148 // Copy any data before the first section (i.e. the image header)
149 if (!CopyRegion(hRemoteProcess, module->remoteCodeBase, module->localCodeBase,
150 section->VirtualAddress, PAGE_READONLY))
151 return false;
153 // loop through all sections and change access flags
154 for (i = 0; i < numSections; i++, section++) {
155 DWORD protect, size;
156 int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
157 int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0;
158 int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0;
160 // determine protection flags based on characteristics
161 protect = ProtectionFlags[executable][readable][writeable];
162 if (section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) {
163 protect |= PAGE_NOCACHE;
166 void* remoteAddress = module->remoteCodeBase + section->VirtualAddress;
167 void* localAddress = module->localCodeBase + section->VirtualAddress;
169 // determine size of region
170 size = section->Misc.VirtualSize;
171 #ifdef DEBUG_OUTPUT
172 fprintf(stderr,
173 "Copying section %s to %p, size %x, executable %i readable %i "
174 "writeable %i\n",
175 section->Name, remoteAddress, size, executable, readable,
176 writeable);
177 #endif
178 if (!CopyRegion(hRemoteProcess, remoteAddress, localAddress, size, protect))
179 return false;
181 return true;
184 static void PerformBaseRelocation(PMEMORYMODULE module, SIZE_T delta) {
185 DWORD i;
186 unsigned char* codeBase = module->localCodeBase;
188 PIMAGE_DATA_DIRECTORY directory =
189 GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC);
190 if (directory->Size > 0) {
191 PIMAGE_BASE_RELOCATION relocation =
192 (PIMAGE_BASE_RELOCATION)(codeBase + directory->VirtualAddress);
193 for (; relocation->VirtualAddress > 0;) {
194 unsigned char* dest = codeBase + relocation->VirtualAddress;
195 unsigned short* relInfo = (unsigned short*)((unsigned char*)relocation +
196 IMAGE_SIZEOF_BASE_RELOCATION);
197 for (i = 0;
198 i < ((relocation->SizeOfBlock - IMAGE_SIZEOF_BASE_RELOCATION) / 2);
199 i++, relInfo++) {
200 DWORD* patchAddrHL;
201 #ifdef _WIN64
202 ULONGLONG* patchAddr64;
203 #endif
204 int type, offset;
206 // the upper 4 bits define the type of relocation
207 type = *relInfo >> 12;
208 // the lower 12 bits define the offset
209 offset = *relInfo & 0xfff;
211 switch (type) {
212 case IMAGE_REL_BASED_ABSOLUTE:
213 // skip relocation
214 break;
216 case IMAGE_REL_BASED_HIGHLOW:
217 // change complete 32 bit address
218 patchAddrHL = (DWORD*)(dest + offset);
219 *patchAddrHL += delta;
220 break;
222 #ifdef _WIN64
223 case IMAGE_REL_BASED_DIR64:
224 patchAddr64 = (ULONGLONG*)(dest + offset);
225 *patchAddr64 += delta;
226 break;
227 #endif
229 default:
230 // printf("Unknown relocation: %d\n", type);
231 break;
235 // advance to next relocation block
236 relocation = (PIMAGE_BASE_RELOCATION)(((char*)relocation) +
237 relocation->SizeOfBlock);
242 static int BuildImportTable(PMEMORYMODULE module) {
243 int result = 1;
244 unsigned char* codeBase = module->localCodeBase;
246 PIMAGE_DATA_DIRECTORY directory =
247 GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT);
248 if (directory->Size > 0) {
249 PIMAGE_IMPORT_DESCRIPTOR importDesc =
250 (PIMAGE_IMPORT_DESCRIPTOR)(codeBase + directory->VirtualAddress);
251 PIMAGE_IMPORT_DESCRIPTOR importEnd =
252 (PIMAGE_IMPORT_DESCRIPTOR)(codeBase + directory->VirtualAddress +
253 directory->Size);
255 for (; importDesc < importEnd && importDesc->Name; importDesc++) {
256 POINTER_TYPE* thunkRef;
257 FARPROC* funcRef;
258 HMODULE handle = GetModuleHandleA((LPCSTR)(codeBase + importDesc->Name));
259 if (handle == nullptr) {
260 #if DEBUG_OUTPUT
261 OutputLastError("Can't load library");
262 #endif
263 result = 0;
264 break;
267 module->modules = (HMODULE*)realloc(
268 module->modules, (module->numModules + 1) * (sizeof(HMODULE)));
269 if (module->modules == nullptr) {
270 result = 0;
271 break;
274 module->modules[module->numModules++] = handle;
275 if (importDesc->OriginalFirstThunk) {
276 thunkRef = (POINTER_TYPE*)(codeBase + importDesc->OriginalFirstThunk);
277 funcRef = (FARPROC*)(codeBase + importDesc->FirstThunk);
278 } else {
279 // no hint table
280 thunkRef = (POINTER_TYPE*)(codeBase + importDesc->FirstThunk);
281 funcRef = (FARPROC*)(codeBase + importDesc->FirstThunk);
283 for (; *thunkRef; thunkRef++, funcRef++) {
284 if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) {
285 *funcRef =
286 (FARPROC)GetProcAddress(handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef));
287 } else {
288 PIMAGE_IMPORT_BY_NAME thunkData =
289 (PIMAGE_IMPORT_BY_NAME)(codeBase + (*thunkRef));
290 *funcRef = (FARPROC)GetProcAddress(handle, (LPCSTR)&thunkData->Name);
292 if (*funcRef == 0) {
293 result = 0;
294 break;
298 if (!result) {
299 break;
304 return result;
307 static void* MemoryGetProcAddress(PMEMORYMODULE module, const char* name);
309 void* LoadRemoteLibraryAndGetAddress(HANDLE hRemoteProcess,
310 const WCHAR* library, const char* symbol) {
311 // Map the DLL into memory
312 nsAutoHandle hLibrary(CreateFile(library, GENERIC_READ, FILE_SHARE_READ,
313 nullptr, OPEN_EXISTING,
314 FILE_ATTRIBUTE_NORMAL, nullptr));
315 if (INVALID_HANDLE_VALUE == hLibrary) {
316 #if DEBUG_OUTPUT
317 OutputLastError("Couldn't CreateFile the library.\n");
318 #endif
319 return nullptr;
322 nsAutoHandle hMapping(
323 CreateFileMapping(hLibrary, nullptr, PAGE_READONLY, 0, 0, nullptr));
324 if (!hMapping) {
325 #if DEBUG_OUTPUT
326 OutputLastError("Couldn't CreateFileMapping.\n");
327 #endif
328 return nullptr;
331 nsAutoRef<FileView> data(
332 (const unsigned char*)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0));
333 if (!data) {
334 #if DEBUG_OUTPUT
335 OutputLastError("Couldn't MapViewOfFile.\n");
336 #endif
337 return nullptr;
340 SIZE_T locationDelta;
342 PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)data.get();
343 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
344 #if DEBUG_OUTPUT
345 OutputDebugStringA("Not a valid executable file.\n");
346 #endif
347 return nullptr;
350 PIMAGE_NT_HEADERS old_header =
351 (PIMAGE_NT_HEADERS)(data + dos_header->e_lfanew);
352 if (old_header->Signature != IMAGE_NT_SIGNATURE) {
353 #if DEBUG_OUTPUT
354 OutputDebugStringA("No PE header found.\n");
355 #endif
356 return nullptr;
359 // reserve memory for image of library in this process and the target process
360 unsigned char* localCode = (unsigned char*)VirtualAlloc(
361 nullptr, old_header->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT,
362 PAGE_READWRITE);
363 if (!localCode) {
364 #if DEBUG_OUTPUT
365 OutputLastError("Can't reserve local memory.");
366 #endif
369 unsigned char* remoteCode = (unsigned char*)VirtualAllocEx(
370 hRemoteProcess, nullptr, old_header->OptionalHeader.SizeOfImage,
371 MEM_RESERVE, PAGE_EXECUTE_READ);
372 if (!remoteCode) {
373 #if DEBUG_OUTPUT
374 OutputLastError("Can't reserve remote memory.");
375 #endif
378 MEMORYMODULE result;
379 result.localCodeBase = localCode;
380 result.remoteCodeBase = remoteCode;
381 result.numModules = 0;
382 result.modules = nullptr;
384 // copy PE header to code
385 memcpy(localCode, dos_header,
386 dos_header->e_lfanew + old_header->OptionalHeader.SizeOfHeaders);
387 result.headers =
388 reinterpret_cast<PIMAGE_NT_HEADERS>(localCode + dos_header->e_lfanew);
390 // update position
391 result.headers->OptionalHeader.ImageBase = (POINTER_TYPE)remoteCode;
393 // copy sections from DLL file block to new memory location
394 CopySections(data, old_header, &result);
396 // adjust base address of imported data
397 locationDelta = (SIZE_T)(remoteCode - old_header->OptionalHeader.ImageBase);
398 if (locationDelta != 0) {
399 PerformBaseRelocation(&result, locationDelta);
402 // load required dlls and adjust function table of imports
403 if (!BuildImportTable(&result)) {
404 return nullptr;
407 // mark memory pages depending on section headers and release
408 // sections that are marked as "discardable"
409 if (!FinalizeSections(&result, hRemoteProcess)) {
410 return nullptr;
413 return MemoryGetProcAddress(&result, symbol);
416 static void* MemoryGetProcAddress(PMEMORYMODULE module, const char* name) {
417 unsigned char* localCodeBase = module->localCodeBase;
418 int idx = -1;
419 DWORD i, *nameRef;
420 WORD* ordinal;
421 PIMAGE_EXPORT_DIRECTORY exports;
422 PIMAGE_DATA_DIRECTORY directory =
423 GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXPORT);
424 if (directory->Size == 0) {
425 // no export table found
426 return nullptr;
429 exports =
430 (PIMAGE_EXPORT_DIRECTORY)(localCodeBase + directory->VirtualAddress);
431 if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) {
432 // DLL doesn't export anything
433 return nullptr;
436 // search function name in list of exported names
437 nameRef = (DWORD*)(localCodeBase + exports->AddressOfNames);
438 ordinal = (WORD*)(localCodeBase + exports->AddressOfNameOrdinals);
439 for (i = 0; i < exports->NumberOfNames; i++, nameRef++, ordinal++) {
440 if (stricmp(name, (const char*)(localCodeBase + (*nameRef))) == 0) {
441 idx = *ordinal;
442 break;
446 if (idx == -1) {
447 // exported symbol not found
448 return nullptr;
451 if ((DWORD)idx > exports->NumberOfFunctions) {
452 // name <-> ordinal number don't match
453 return nullptr;
456 // AddressOfFunctions contains the RVAs to the "real" functions
457 return module->remoteCodeBase +
458 (*(DWORD*)(localCodeBase + exports->AddressOfFunctions + (idx * 4)));