michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef __GNUC__ michael@0: // disable warnings about pointer <-> DWORD conversions michael@0: #pragma warning( disable : 4311 4312 ) michael@0: #endif michael@0: michael@0: #ifdef _WIN64 michael@0: #define POINTER_TYPE ULONGLONG michael@0: #else michael@0: #define POINTER_TYPE DWORD michael@0: #endif michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #ifdef DEBUG_OUTPUT michael@0: #include michael@0: #endif michael@0: michael@0: #include "nsWindowsHelpers.h" michael@0: michael@0: typedef const unsigned char* FileView; michael@0: michael@0: template<> michael@0: class nsAutoRefTraits michael@0: { michael@0: public: michael@0: typedef FileView RawRef; michael@0: static FileView Void() michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: static void Release(RawRef aView) michael@0: { michael@0: if (nullptr != aView) michael@0: UnmapViewOfFile(aView); michael@0: } michael@0: }; michael@0: michael@0: #ifndef IMAGE_SIZEOF_BASE_RELOCATION michael@0: // Vista SDKs no longer define IMAGE_SIZEOF_BASE_RELOCATION!? michael@0: #define IMAGE_SIZEOF_BASE_RELOCATION (sizeof(IMAGE_BASE_RELOCATION)) michael@0: #endif michael@0: michael@0: #include "LoadLibraryRemote.h" michael@0: michael@0: typedef struct { michael@0: PIMAGE_NT_HEADERS headers; michael@0: unsigned char *localCodeBase; michael@0: unsigned char *remoteCodeBase; michael@0: HMODULE *modules; michael@0: int numModules; michael@0: } MEMORYMODULE, *PMEMORYMODULE; michael@0: michael@0: typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); michael@0: michael@0: #define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx] michael@0: michael@0: #ifdef DEBUG_OUTPUT michael@0: static void michael@0: OutputLastError(const char *msg) michael@0: { michael@0: char* tmp; michael@0: char *tmpmsg; michael@0: FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, michael@0: nullptr, GetLastError(), michael@0: MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), michael@0: (LPSTR) &tmp, 0, nullptr); michael@0: tmpmsg = (char *)LocalAlloc(LPTR, strlen(msg) + strlen(tmp) + 3); michael@0: sprintf(tmpmsg, "%s: %s", msg, tmp); michael@0: OutputDebugStringA(tmpmsg); michael@0: LocalFree(tmpmsg); michael@0: LocalFree(tmp); michael@0: } michael@0: #endif michael@0: michael@0: static void michael@0: CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module) michael@0: { michael@0: int i; michael@0: unsigned char *codeBase = module->localCodeBase; michael@0: unsigned char *dest; michael@0: PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); michael@0: for (i=0; iheaders->FileHeader.NumberOfSections; i++, section++) { michael@0: dest = codeBase + section->VirtualAddress; michael@0: memset(dest, 0, section->Misc.VirtualSize); michael@0: if (section->SizeOfRawData) { michael@0: memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData); michael@0: } michael@0: // section->Misc.PhysicalAddress = (POINTER_TYPE) module->remoteCodeBase + section->VirtualAddress; michael@0: } michael@0: } michael@0: michael@0: // Protection flags for memory pages (Executable, Readable, Writeable) michael@0: static int ProtectionFlags[2][2][2] = { michael@0: { michael@0: // not executable michael@0: {PAGE_NOACCESS, PAGE_WRITECOPY}, michael@0: {PAGE_READONLY, PAGE_READWRITE}, michael@0: }, { michael@0: // executable michael@0: {PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY}, michael@0: {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE}, michael@0: }, michael@0: }; michael@0: michael@0: static bool michael@0: FinalizeSections(PMEMORYMODULE module, HANDLE hRemoteProcess) michael@0: { michael@0: #ifdef DEBUG_OUTPUT michael@0: fprintf(stderr, "Finalizing sections: local base %p, remote base %p\n", michael@0: module->localCodeBase, module->remoteCodeBase); michael@0: #endif michael@0: michael@0: int i; michael@0: PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); michael@0: michael@0: // loop through all sections and change access flags michael@0: for (i=0; iheaders->FileHeader.NumberOfSections; i++, section++) { michael@0: DWORD protect, oldProtect, size; michael@0: int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; michael@0: int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0; michael@0: int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0; michael@0: michael@0: // determine protection flags based on characteristics michael@0: protect = ProtectionFlags[executable][readable][writeable]; michael@0: if (section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) { michael@0: protect |= PAGE_NOCACHE; michael@0: } michael@0: michael@0: // determine size of region michael@0: size = section->Misc.VirtualSize; michael@0: if (size > 0) { michael@0: void* remoteAddress = module->remoteCodeBase + section->VirtualAddress; michael@0: void* localAddress = module->localCodeBase + section->VirtualAddress; michael@0: michael@0: #ifdef DEBUG_OUTPUT michael@0: fprintf(stderr, "Copying section %s to %p, size %x, executable %i readable %i writeable %i\n", michael@0: section->Name, remoteAddress, size, executable, readable, writeable); michael@0: #endif michael@0: michael@0: // Copy the data from local->remote and set the memory protection michael@0: if (!VirtualAllocEx(hRemoteProcess, remoteAddress, size, MEM_COMMIT, PAGE_READWRITE)) michael@0: return false; michael@0: michael@0: if (!WriteProcessMemory(hRemoteProcess, michael@0: remoteAddress, michael@0: localAddress, michael@0: size, michael@0: nullptr)) { michael@0: #ifdef DEBUG_OUTPUT michael@0: OutputLastError("Error writing remote memory.\n"); michael@0: #endif michael@0: return false; michael@0: } michael@0: michael@0: if (VirtualProtectEx(hRemoteProcess, remoteAddress, size, protect, &oldProtect) == 0) { michael@0: #ifdef DEBUG_OUTPUT michael@0: OutputLastError("Error protecting memory page"); michael@0: #endif michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: PerformBaseRelocation(PMEMORYMODULE module, SIZE_T delta) michael@0: { michael@0: DWORD i; michael@0: unsigned char *codeBase = module->localCodeBase; michael@0: michael@0: PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC); michael@0: if (directory->Size > 0) { michael@0: PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION) (codeBase + directory->VirtualAddress); michael@0: for (; relocation->VirtualAddress > 0; ) { michael@0: unsigned char *dest = codeBase + relocation->VirtualAddress; michael@0: unsigned short *relInfo = (unsigned short *)((unsigned char *)relocation + IMAGE_SIZEOF_BASE_RELOCATION); michael@0: for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) { michael@0: DWORD *patchAddrHL; michael@0: #ifdef _WIN64 michael@0: ULONGLONG *patchAddr64; michael@0: #endif michael@0: int type, offset; michael@0: michael@0: // the upper 4 bits define the type of relocation michael@0: type = *relInfo >> 12; michael@0: // the lower 12 bits define the offset michael@0: offset = *relInfo & 0xfff; michael@0: michael@0: switch (type) michael@0: { michael@0: case IMAGE_REL_BASED_ABSOLUTE: michael@0: // skip relocation michael@0: break; michael@0: michael@0: case IMAGE_REL_BASED_HIGHLOW: michael@0: // change complete 32 bit address michael@0: patchAddrHL = (DWORD *) (dest + offset); michael@0: *patchAddrHL += delta; michael@0: break; michael@0: michael@0: #ifdef _WIN64 michael@0: case IMAGE_REL_BASED_DIR64: michael@0: patchAddr64 = (ULONGLONG *) (dest + offset); michael@0: *patchAddr64 += delta; michael@0: break; michael@0: #endif michael@0: michael@0: default: michael@0: //printf("Unknown relocation: %d\n", type); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // advance to next relocation block michael@0: relocation = (PIMAGE_BASE_RELOCATION) (((char *) relocation) + relocation->SizeOfBlock); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static int michael@0: BuildImportTable(PMEMORYMODULE module) michael@0: { michael@0: int result=1; michael@0: unsigned char *codeBase = module->localCodeBase; michael@0: michael@0: PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT); michael@0: if (directory->Size > 0) { michael@0: PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress); michael@0: PIMAGE_IMPORT_DESCRIPTOR importEnd = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress + directory->Size); michael@0: michael@0: for (; importDesc < importEnd && importDesc->Name; importDesc++) { michael@0: POINTER_TYPE *thunkRef; michael@0: FARPROC *funcRef; michael@0: HMODULE handle = GetModuleHandleA((LPCSTR) (codeBase + importDesc->Name)); michael@0: if (handle == nullptr) { michael@0: #if DEBUG_OUTPUT michael@0: OutputLastError("Can't load library"); michael@0: #endif michael@0: result = 0; michael@0: break; michael@0: } michael@0: michael@0: module->modules = (HMODULE *)realloc(module->modules, (module->numModules+1)*(sizeof(HMODULE))); michael@0: if (module->modules == nullptr) { michael@0: result = 0; michael@0: break; michael@0: } michael@0: michael@0: module->modules[module->numModules++] = handle; michael@0: if (importDesc->OriginalFirstThunk) { michael@0: thunkRef = (POINTER_TYPE *) (codeBase + importDesc->OriginalFirstThunk); michael@0: funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk); michael@0: } else { michael@0: // no hint table michael@0: thunkRef = (POINTER_TYPE *) (codeBase + importDesc->FirstThunk); michael@0: funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk); michael@0: } michael@0: for (; *thunkRef; thunkRef++, funcRef++) { michael@0: if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) { michael@0: *funcRef = (FARPROC)GetProcAddress(handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef)); michael@0: } else { michael@0: PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME) (codeBase + (*thunkRef)); michael@0: *funcRef = (FARPROC)GetProcAddress(handle, (LPCSTR)&thunkData->Name); michael@0: } michael@0: if (*funcRef == 0) { michael@0: result = 0; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!result) { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: static void* MemoryGetProcAddress(PMEMORYMODULE module, const char *name); michael@0: michael@0: void* LoadRemoteLibraryAndGetAddress(HANDLE hRemoteProcess, michael@0: const WCHAR* library, michael@0: const char* symbol) michael@0: { michael@0: // Map the DLL into memory michael@0: nsAutoHandle hLibrary( michael@0: CreateFile(library, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, michael@0: FILE_ATTRIBUTE_NORMAL, nullptr)); michael@0: if (INVALID_HANDLE_VALUE == hLibrary) { michael@0: #if DEBUG_OUTPUT michael@0: OutputLastError("Couldn't CreateFile the library.\n"); michael@0: #endif michael@0: return nullptr; michael@0: } michael@0: michael@0: nsAutoHandle hMapping( michael@0: CreateFileMapping(hLibrary, nullptr, PAGE_READONLY, 0, 0, nullptr)); michael@0: if (!hMapping) { michael@0: #if DEBUG_OUTPUT michael@0: OutputLastError("Couldn't CreateFileMapping.\n"); michael@0: #endif michael@0: return nullptr; michael@0: } michael@0: michael@0: nsAutoRef data( michael@0: (const unsigned char*) MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0)); michael@0: if (!data) { michael@0: #if DEBUG_OUTPUT michael@0: OutputLastError("Couldn't MapViewOfFile.\n"); michael@0: #endif michael@0: return nullptr; michael@0: } michael@0: michael@0: SIZE_T locationDelta; michael@0: michael@0: PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)data.get(); michael@0: if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) { michael@0: #if DEBUG_OUTPUT michael@0: OutputDebugStringA("Not a valid executable file.\n"); michael@0: #endif michael@0: return nullptr; michael@0: } michael@0: michael@0: PIMAGE_NT_HEADERS old_header = (PIMAGE_NT_HEADERS)(data + dos_header->e_lfanew); michael@0: if (old_header->Signature != IMAGE_NT_SIGNATURE) { michael@0: #if DEBUG_OUTPUT michael@0: OutputDebugStringA("No PE header found.\n"); michael@0: #endif michael@0: return nullptr; michael@0: } michael@0: michael@0: // reserve memory for image of library in this process and the target process michael@0: unsigned char* localCode = (unsigned char*) VirtualAlloc(nullptr, michael@0: old_header->OptionalHeader.SizeOfImage, michael@0: MEM_RESERVE | MEM_COMMIT, michael@0: PAGE_READWRITE); michael@0: if (!localCode) { michael@0: #if DEBUG_OUTPUT michael@0: OutputLastError("Can't reserve local memory."); michael@0: #endif michael@0: } michael@0: michael@0: unsigned char* remoteCode = (unsigned char*) VirtualAllocEx(hRemoteProcess, nullptr, michael@0: old_header->OptionalHeader.SizeOfImage, michael@0: MEM_RESERVE, michael@0: PAGE_EXECUTE_READ); michael@0: if (!remoteCode) { michael@0: #if DEBUG_OUTPUT michael@0: OutputLastError("Can't reserve remote memory."); michael@0: #endif michael@0: } michael@0: michael@0: MEMORYMODULE result; michael@0: result.localCodeBase = localCode; michael@0: result.remoteCodeBase = remoteCode; michael@0: result.numModules = 0; michael@0: result.modules = nullptr; michael@0: michael@0: // copy PE header to code michael@0: memcpy(localCode, dos_header, dos_header->e_lfanew + old_header->OptionalHeader.SizeOfHeaders); michael@0: result.headers = reinterpret_cast(localCode + dos_header->e_lfanew); michael@0: michael@0: // update position michael@0: result.headers->OptionalHeader.ImageBase = (POINTER_TYPE)remoteCode; michael@0: michael@0: // copy sections from DLL file block to new memory location michael@0: CopySections(data, old_header, &result); michael@0: michael@0: // adjust base address of imported data michael@0: locationDelta = (SIZE_T)(remoteCode - old_header->OptionalHeader.ImageBase); michael@0: if (locationDelta != 0) { michael@0: PerformBaseRelocation(&result, locationDelta); michael@0: } michael@0: michael@0: // load required dlls and adjust function table of imports michael@0: if (!BuildImportTable(&result)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // mark memory pages depending on section headers and release michael@0: // sections that are marked as "discardable" michael@0: if (!FinalizeSections(&result, hRemoteProcess)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return MemoryGetProcAddress(&result, symbol); michael@0: } michael@0: michael@0: static void* MemoryGetProcAddress(PMEMORYMODULE module, const char *name) michael@0: { michael@0: unsigned char *localCodeBase = module->localCodeBase; michael@0: int idx=-1; michael@0: DWORD i, *nameRef; michael@0: WORD *ordinal; michael@0: PIMAGE_EXPORT_DIRECTORY exports; michael@0: PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXPORT); michael@0: if (directory->Size == 0) { michael@0: // no export table found michael@0: return nullptr; michael@0: } michael@0: michael@0: exports = (PIMAGE_EXPORT_DIRECTORY) (localCodeBase + directory->VirtualAddress); michael@0: if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) { michael@0: // DLL doesn't export anything michael@0: return nullptr; michael@0: } michael@0: michael@0: // search function name in list of exported names michael@0: nameRef = (DWORD *) (localCodeBase + exports->AddressOfNames); michael@0: ordinal = (WORD *) (localCodeBase + exports->AddressOfNameOrdinals); michael@0: for (i=0; iNumberOfNames; i++, nameRef++, ordinal++) { michael@0: if (stricmp(name, (const char *) (localCodeBase + (*nameRef))) == 0) { michael@0: idx = *ordinal; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (idx == -1) { michael@0: // exported symbol not found michael@0: return nullptr; michael@0: } michael@0: michael@0: if ((DWORD)idx > exports->NumberOfFunctions) { michael@0: // name <-> ordinal number don't match michael@0: return nullptr; michael@0: } michael@0: michael@0: // AddressOfFunctions contains the RVAs to the "real" functions michael@0: return module->remoteCodeBase + (*(DWORD *) (localCodeBase + exports->AddressOfFunctions + (idx*4))); michael@0: }