1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/crashreporter/LoadLibraryRemote.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,438 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#ifndef __GNUC__ 1.9 +// disable warnings about pointer <-> DWORD conversions 1.10 +#pragma warning( disable : 4311 4312 ) 1.11 +#endif 1.12 + 1.13 +#ifdef _WIN64 1.14 +#define POINTER_TYPE ULONGLONG 1.15 +#else 1.16 +#define POINTER_TYPE DWORD 1.17 +#endif 1.18 + 1.19 +#include <windows.h> 1.20 +#include <winnt.h> 1.21 +#include <stdlib.h> 1.22 +#ifdef DEBUG_OUTPUT 1.23 +#include <stdio.h> 1.24 +#endif 1.25 + 1.26 +#include "nsWindowsHelpers.h" 1.27 + 1.28 +typedef const unsigned char* FileView; 1.29 + 1.30 +template<> 1.31 +class nsAutoRefTraits<FileView> 1.32 +{ 1.33 +public: 1.34 + typedef FileView RawRef; 1.35 + static FileView Void() 1.36 + { 1.37 + return nullptr; 1.38 + } 1.39 + 1.40 + static void Release(RawRef aView) 1.41 + { 1.42 + if (nullptr != aView) 1.43 + UnmapViewOfFile(aView); 1.44 + } 1.45 +}; 1.46 + 1.47 +#ifndef IMAGE_SIZEOF_BASE_RELOCATION 1.48 +// Vista SDKs no longer define IMAGE_SIZEOF_BASE_RELOCATION!? 1.49 +#define IMAGE_SIZEOF_BASE_RELOCATION (sizeof(IMAGE_BASE_RELOCATION)) 1.50 +#endif 1.51 + 1.52 +#include "LoadLibraryRemote.h" 1.53 + 1.54 +typedef struct { 1.55 + PIMAGE_NT_HEADERS headers; 1.56 + unsigned char *localCodeBase; 1.57 + unsigned char *remoteCodeBase; 1.58 + HMODULE *modules; 1.59 + int numModules; 1.60 +} MEMORYMODULE, *PMEMORYMODULE; 1.61 + 1.62 +typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); 1.63 + 1.64 +#define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx] 1.65 + 1.66 +#ifdef DEBUG_OUTPUT 1.67 +static void 1.68 +OutputLastError(const char *msg) 1.69 +{ 1.70 + char* tmp; 1.71 + char *tmpmsg; 1.72 + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 1.73 + nullptr, GetLastError(), 1.74 + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 1.75 + (LPSTR) &tmp, 0, nullptr); 1.76 + tmpmsg = (char *)LocalAlloc(LPTR, strlen(msg) + strlen(tmp) + 3); 1.77 + sprintf(tmpmsg, "%s: %s", msg, tmp); 1.78 + OutputDebugStringA(tmpmsg); 1.79 + LocalFree(tmpmsg); 1.80 + LocalFree(tmp); 1.81 +} 1.82 +#endif 1.83 + 1.84 +static void 1.85 +CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module) 1.86 +{ 1.87 + int i; 1.88 + unsigned char *codeBase = module->localCodeBase; 1.89 + unsigned char *dest; 1.90 + PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); 1.91 + for (i=0; i<module->headers->FileHeader.NumberOfSections; i++, section++) { 1.92 + dest = codeBase + section->VirtualAddress; 1.93 + memset(dest, 0, section->Misc.VirtualSize); 1.94 + if (section->SizeOfRawData) { 1.95 + memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData); 1.96 + } 1.97 + // section->Misc.PhysicalAddress = (POINTER_TYPE) module->remoteCodeBase + section->VirtualAddress; 1.98 + } 1.99 +} 1.100 + 1.101 +// Protection flags for memory pages (Executable, Readable, Writeable) 1.102 +static int ProtectionFlags[2][2][2] = { 1.103 + { 1.104 + // not executable 1.105 + {PAGE_NOACCESS, PAGE_WRITECOPY}, 1.106 + {PAGE_READONLY, PAGE_READWRITE}, 1.107 + }, { 1.108 + // executable 1.109 + {PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY}, 1.110 + {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE}, 1.111 + }, 1.112 +}; 1.113 + 1.114 +static bool 1.115 +FinalizeSections(PMEMORYMODULE module, HANDLE hRemoteProcess) 1.116 +{ 1.117 +#ifdef DEBUG_OUTPUT 1.118 + fprintf(stderr, "Finalizing sections: local base %p, remote base %p\n", 1.119 + module->localCodeBase, module->remoteCodeBase); 1.120 +#endif 1.121 + 1.122 + int i; 1.123 + PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); 1.124 + 1.125 + // loop through all sections and change access flags 1.126 + for (i=0; i<module->headers->FileHeader.NumberOfSections; i++, section++) { 1.127 + DWORD protect, oldProtect, size; 1.128 + int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; 1.129 + int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0; 1.130 + int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0; 1.131 + 1.132 + // determine protection flags based on characteristics 1.133 + protect = ProtectionFlags[executable][readable][writeable]; 1.134 + if (section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) { 1.135 + protect |= PAGE_NOCACHE; 1.136 + } 1.137 + 1.138 + // determine size of region 1.139 + size = section->Misc.VirtualSize; 1.140 + if (size > 0) { 1.141 + void* remoteAddress = module->remoteCodeBase + section->VirtualAddress; 1.142 + void* localAddress = module->localCodeBase + section->VirtualAddress; 1.143 + 1.144 +#ifdef DEBUG_OUTPUT 1.145 + fprintf(stderr, "Copying section %s to %p, size %x, executable %i readable %i writeable %i\n", 1.146 + section->Name, remoteAddress, size, executable, readable, writeable); 1.147 +#endif 1.148 + 1.149 + // Copy the data from local->remote and set the memory protection 1.150 + if (!VirtualAllocEx(hRemoteProcess, remoteAddress, size, MEM_COMMIT, PAGE_READWRITE)) 1.151 + return false; 1.152 + 1.153 + if (!WriteProcessMemory(hRemoteProcess, 1.154 + remoteAddress, 1.155 + localAddress, 1.156 + size, 1.157 + nullptr)) { 1.158 +#ifdef DEBUG_OUTPUT 1.159 + OutputLastError("Error writing remote memory.\n"); 1.160 +#endif 1.161 + return false; 1.162 + } 1.163 + 1.164 + if (VirtualProtectEx(hRemoteProcess, remoteAddress, size, protect, &oldProtect) == 0) { 1.165 +#ifdef DEBUG_OUTPUT 1.166 + OutputLastError("Error protecting memory page"); 1.167 +#endif 1.168 + return false; 1.169 + } 1.170 + } 1.171 + } 1.172 + return true; 1.173 +} 1.174 + 1.175 +static void 1.176 +PerformBaseRelocation(PMEMORYMODULE module, SIZE_T delta) 1.177 +{ 1.178 + DWORD i; 1.179 + unsigned char *codeBase = module->localCodeBase; 1.180 + 1.181 + PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC); 1.182 + if (directory->Size > 0) { 1.183 + PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION) (codeBase + directory->VirtualAddress); 1.184 + for (; relocation->VirtualAddress > 0; ) { 1.185 + unsigned char *dest = codeBase + relocation->VirtualAddress; 1.186 + unsigned short *relInfo = (unsigned short *)((unsigned char *)relocation + IMAGE_SIZEOF_BASE_RELOCATION); 1.187 + for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) { 1.188 + DWORD *patchAddrHL; 1.189 +#ifdef _WIN64 1.190 + ULONGLONG *patchAddr64; 1.191 +#endif 1.192 + int type, offset; 1.193 + 1.194 + // the upper 4 bits define the type of relocation 1.195 + type = *relInfo >> 12; 1.196 + // the lower 12 bits define the offset 1.197 + offset = *relInfo & 0xfff; 1.198 + 1.199 + switch (type) 1.200 + { 1.201 + case IMAGE_REL_BASED_ABSOLUTE: 1.202 + // skip relocation 1.203 + break; 1.204 + 1.205 + case IMAGE_REL_BASED_HIGHLOW: 1.206 + // change complete 32 bit address 1.207 + patchAddrHL = (DWORD *) (dest + offset); 1.208 + *patchAddrHL += delta; 1.209 + break; 1.210 + 1.211 +#ifdef _WIN64 1.212 + case IMAGE_REL_BASED_DIR64: 1.213 + patchAddr64 = (ULONGLONG *) (dest + offset); 1.214 + *patchAddr64 += delta; 1.215 + break; 1.216 +#endif 1.217 + 1.218 + default: 1.219 + //printf("Unknown relocation: %d\n", type); 1.220 + break; 1.221 + } 1.222 + } 1.223 + 1.224 + // advance to next relocation block 1.225 + relocation = (PIMAGE_BASE_RELOCATION) (((char *) relocation) + relocation->SizeOfBlock); 1.226 + } 1.227 + } 1.228 +} 1.229 + 1.230 +static int 1.231 +BuildImportTable(PMEMORYMODULE module) 1.232 +{ 1.233 + int result=1; 1.234 + unsigned char *codeBase = module->localCodeBase; 1.235 + 1.236 + PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT); 1.237 + if (directory->Size > 0) { 1.238 + PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress); 1.239 + PIMAGE_IMPORT_DESCRIPTOR importEnd = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress + directory->Size); 1.240 + 1.241 + for (; importDesc < importEnd && importDesc->Name; importDesc++) { 1.242 + POINTER_TYPE *thunkRef; 1.243 + FARPROC *funcRef; 1.244 + HMODULE handle = GetModuleHandleA((LPCSTR) (codeBase + importDesc->Name)); 1.245 + if (handle == nullptr) { 1.246 +#if DEBUG_OUTPUT 1.247 + OutputLastError("Can't load library"); 1.248 +#endif 1.249 + result = 0; 1.250 + break; 1.251 + } 1.252 + 1.253 + module->modules = (HMODULE *)realloc(module->modules, (module->numModules+1)*(sizeof(HMODULE))); 1.254 + if (module->modules == nullptr) { 1.255 + result = 0; 1.256 + break; 1.257 + } 1.258 + 1.259 + module->modules[module->numModules++] = handle; 1.260 + if (importDesc->OriginalFirstThunk) { 1.261 + thunkRef = (POINTER_TYPE *) (codeBase + importDesc->OriginalFirstThunk); 1.262 + funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk); 1.263 + } else { 1.264 + // no hint table 1.265 + thunkRef = (POINTER_TYPE *) (codeBase + importDesc->FirstThunk); 1.266 + funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk); 1.267 + } 1.268 + for (; *thunkRef; thunkRef++, funcRef++) { 1.269 + if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) { 1.270 + *funcRef = (FARPROC)GetProcAddress(handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef)); 1.271 + } else { 1.272 + PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME) (codeBase + (*thunkRef)); 1.273 + *funcRef = (FARPROC)GetProcAddress(handle, (LPCSTR)&thunkData->Name); 1.274 + } 1.275 + if (*funcRef == 0) { 1.276 + result = 0; 1.277 + break; 1.278 + } 1.279 + } 1.280 + 1.281 + if (!result) { 1.282 + break; 1.283 + } 1.284 + } 1.285 + } 1.286 + 1.287 + return result; 1.288 +} 1.289 + 1.290 +static void* MemoryGetProcAddress(PMEMORYMODULE module, const char *name); 1.291 + 1.292 +void* LoadRemoteLibraryAndGetAddress(HANDLE hRemoteProcess, 1.293 + const WCHAR* library, 1.294 + const char* symbol) 1.295 +{ 1.296 + // Map the DLL into memory 1.297 + nsAutoHandle hLibrary( 1.298 + CreateFile(library, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 1.299 + FILE_ATTRIBUTE_NORMAL, nullptr)); 1.300 + if (INVALID_HANDLE_VALUE == hLibrary) { 1.301 +#if DEBUG_OUTPUT 1.302 + OutputLastError("Couldn't CreateFile the library.\n"); 1.303 +#endif 1.304 + return nullptr; 1.305 + } 1.306 + 1.307 + nsAutoHandle hMapping( 1.308 + CreateFileMapping(hLibrary, nullptr, PAGE_READONLY, 0, 0, nullptr)); 1.309 + if (!hMapping) { 1.310 +#if DEBUG_OUTPUT 1.311 + OutputLastError("Couldn't CreateFileMapping.\n"); 1.312 +#endif 1.313 + return nullptr; 1.314 + } 1.315 + 1.316 + nsAutoRef<FileView> data( 1.317 + (const unsigned char*) MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0)); 1.318 + if (!data) { 1.319 +#if DEBUG_OUTPUT 1.320 + OutputLastError("Couldn't MapViewOfFile.\n"); 1.321 +#endif 1.322 + return nullptr; 1.323 + } 1.324 + 1.325 + SIZE_T locationDelta; 1.326 + 1.327 + PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)data.get(); 1.328 + if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) { 1.329 +#if DEBUG_OUTPUT 1.330 + OutputDebugStringA("Not a valid executable file.\n"); 1.331 +#endif 1.332 + return nullptr; 1.333 + } 1.334 + 1.335 + PIMAGE_NT_HEADERS old_header = (PIMAGE_NT_HEADERS)(data + dos_header->e_lfanew); 1.336 + if (old_header->Signature != IMAGE_NT_SIGNATURE) { 1.337 +#if DEBUG_OUTPUT 1.338 + OutputDebugStringA("No PE header found.\n"); 1.339 +#endif 1.340 + return nullptr; 1.341 + } 1.342 + 1.343 + // reserve memory for image of library in this process and the target process 1.344 + unsigned char* localCode = (unsigned char*) VirtualAlloc(nullptr, 1.345 + old_header->OptionalHeader.SizeOfImage, 1.346 + MEM_RESERVE | MEM_COMMIT, 1.347 + PAGE_READWRITE); 1.348 + if (!localCode) { 1.349 +#if DEBUG_OUTPUT 1.350 + OutputLastError("Can't reserve local memory."); 1.351 +#endif 1.352 + } 1.353 + 1.354 + unsigned char* remoteCode = (unsigned char*) VirtualAllocEx(hRemoteProcess, nullptr, 1.355 + old_header->OptionalHeader.SizeOfImage, 1.356 + MEM_RESERVE, 1.357 + PAGE_EXECUTE_READ); 1.358 + if (!remoteCode) { 1.359 +#if DEBUG_OUTPUT 1.360 + OutputLastError("Can't reserve remote memory."); 1.361 +#endif 1.362 + } 1.363 + 1.364 + MEMORYMODULE result; 1.365 + result.localCodeBase = localCode; 1.366 + result.remoteCodeBase = remoteCode; 1.367 + result.numModules = 0; 1.368 + result.modules = nullptr; 1.369 + 1.370 + // copy PE header to code 1.371 + memcpy(localCode, dos_header, dos_header->e_lfanew + old_header->OptionalHeader.SizeOfHeaders); 1.372 + result.headers = reinterpret_cast<PIMAGE_NT_HEADERS>(localCode + dos_header->e_lfanew); 1.373 + 1.374 + // update position 1.375 + result.headers->OptionalHeader.ImageBase = (POINTER_TYPE)remoteCode; 1.376 + 1.377 + // copy sections from DLL file block to new memory location 1.378 + CopySections(data, old_header, &result); 1.379 + 1.380 + // adjust base address of imported data 1.381 + locationDelta = (SIZE_T)(remoteCode - old_header->OptionalHeader.ImageBase); 1.382 + if (locationDelta != 0) { 1.383 + PerformBaseRelocation(&result, locationDelta); 1.384 + } 1.385 + 1.386 + // load required dlls and adjust function table of imports 1.387 + if (!BuildImportTable(&result)) { 1.388 + return nullptr; 1.389 + } 1.390 + 1.391 + // mark memory pages depending on section headers and release 1.392 + // sections that are marked as "discardable" 1.393 + if (!FinalizeSections(&result, hRemoteProcess)) { 1.394 + return nullptr; 1.395 + } 1.396 + 1.397 + return MemoryGetProcAddress(&result, symbol); 1.398 +} 1.399 + 1.400 +static void* MemoryGetProcAddress(PMEMORYMODULE module, const char *name) 1.401 +{ 1.402 + unsigned char *localCodeBase = module->localCodeBase; 1.403 + int idx=-1; 1.404 + DWORD i, *nameRef; 1.405 + WORD *ordinal; 1.406 + PIMAGE_EXPORT_DIRECTORY exports; 1.407 + PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXPORT); 1.408 + if (directory->Size == 0) { 1.409 + // no export table found 1.410 + return nullptr; 1.411 + } 1.412 + 1.413 + exports = (PIMAGE_EXPORT_DIRECTORY) (localCodeBase + directory->VirtualAddress); 1.414 + if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) { 1.415 + // DLL doesn't export anything 1.416 + return nullptr; 1.417 + } 1.418 + 1.419 + // search function name in list of exported names 1.420 + nameRef = (DWORD *) (localCodeBase + exports->AddressOfNames); 1.421 + ordinal = (WORD *) (localCodeBase + exports->AddressOfNameOrdinals); 1.422 + for (i=0; i<exports->NumberOfNames; i++, nameRef++, ordinal++) { 1.423 + if (stricmp(name, (const char *) (localCodeBase + (*nameRef))) == 0) { 1.424 + idx = *ordinal; 1.425 + break; 1.426 + } 1.427 + } 1.428 + 1.429 + if (idx == -1) { 1.430 + // exported symbol not found 1.431 + return nullptr; 1.432 + } 1.433 + 1.434 + if ((DWORD)idx > exports->NumberOfFunctions) { 1.435 + // name <-> ordinal number don't match 1.436 + return nullptr; 1.437 + } 1.438 + 1.439 + // AddressOfFunctions contains the RVAs to the "real" functions 1.440 + return module->remoteCodeBase + (*(DWORD *) (localCodeBase + exports->AddressOfFunctions + (idx*4))); 1.441 +}