| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203 |
- /*
- * Memory DLL loading code
- * Version 0.0.4
- *
- * Copyright (c) 2004-2015 by Joachim Bauch / [email protected]
- * http://www.joachim-bauch.de
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 2.0 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is MemoryModule.c
- *
- * The Initial Developer of the Original Code is Joachim Bauch.
- *
- * Portions created by Joachim Bauch are Copyright (C) 2004-2015
- * Joachim Bauch. All Rights Reserved.
- *
- *
- * THeller: Added binary search in MemoryGetProcAddress function
- * (#define USE_BINARY_SEARCH to enable it). This gives a very large
- * speedup for libraries that exports lots of functions.
- *
- * These portions are Copyright (C) 2013 Thomas Heller.
- */
- #include <windows.h>
- #include <winnt.h>
- #include <stddef.h>
- #include <tchar.h>
- #ifdef DEBUG_OUTPUT
- #include <stdio.h>
- #endif
- #if _MSC_VER
- // Disable warning about data -> function pointer conversion
- #pragma warning(disable:4055)
- // C4244: conversion from 'uintptr_t' to 'DWORD', possible loss of data.
- #pragma warning(error: 4244)
- // C4267: conversion from 'size_t' to 'int', possible loss of data.
- #pragma warning(error: 4267)
- #define inline __inline
- #endif
- #ifndef IMAGE_SIZEOF_BASE_RELOCATION
- // Vista SDKs no longer define IMAGE_SIZEOF_BASE_RELOCATION!?
- #define IMAGE_SIZEOF_BASE_RELOCATION (sizeof(IMAGE_BASE_RELOCATION))
- #endif
- #ifdef _WIN64
- #define HOST_MACHINE IMAGE_FILE_MACHINE_AMD64
- #else
- #define HOST_MACHINE IMAGE_FILE_MACHINE_I386
- #endif
- #include "MemoryModule.h"
- struct ExportNameEntry {
- LPCSTR name;
- WORD idx;
- };
- typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);
- typedef int (WINAPI *ExeEntryProc)(void);
- #ifdef _WIN64
- typedef struct POINTER_LIST {
- struct POINTER_LIST *next;
- void *address;
- } POINTER_LIST;
- #endif
- typedef struct {
- PIMAGE_NT_HEADERS headers;
- unsigned char *codeBase;
- HCUSTOMMODULE *modules;
- int numModules;
- BOOL initialized;
- BOOL isDLL;
- BOOL isRelocated;
- CustomAllocFunc alloc;
- CustomFreeFunc free;
- CustomLoadLibraryFunc loadLibrary;
- CustomGetProcAddressFunc getProcAddress;
- CustomFreeLibraryFunc freeLibrary;
- struct ExportNameEntry *nameExportsTable;
- void *userdata;
- ExeEntryProc exeEntry;
- DWORD pageSize;
- #ifdef _WIN64
- POINTER_LIST *blockedMemory;
- #endif
- } MEMORYMODULE, *PMEMORYMODULE;
- typedef struct {
- LPVOID address;
- LPVOID alignedAddress;
- SIZE_T size;
- DWORD characteristics;
- BOOL last;
- } SECTIONFINALIZEDATA, *PSECTIONFINALIZEDATA;
- #define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx]
- static inline uintptr_t
- AlignValueDown(uintptr_t value, uintptr_t alignment) {
- return value & ~(alignment - 1);
- }
- static inline LPVOID
- AlignAddressDown(LPVOID address, uintptr_t alignment) {
- return (LPVOID) AlignValueDown((uintptr_t) address, alignment);
- }
- static inline size_t
- AlignValueUp(size_t value, size_t alignment) {
- return (value + alignment - 1) & ~(alignment - 1);
- }
- static inline void*
- OffsetPointer(void* data, ptrdiff_t offset) {
- return (void*) ((uintptr_t) data + offset);
- }
- static inline void
- OutputLastError(const char *msg)
- {
- #ifndef DEBUG_OUTPUT
- UNREFERENCED_PARAMETER(msg);
- #else
- LPVOID tmp;
- char *tmpmsg;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&tmp, 0, NULL);
- tmpmsg = (char *)LocalAlloc(LPTR, strlen(msg) + strlen(tmp) + 3);
- sprintf(tmpmsg, "%s: %s", msg, tmp);
- OutputDebugString(tmpmsg);
- LocalFree(tmpmsg);
- LocalFree(tmp);
- #endif
- }
- #ifdef _WIN64
- static void
- FreePointerList(POINTER_LIST *head, CustomFreeFunc freeMemory, void *userdata)
- {
- POINTER_LIST *node = head;
- while (node) {
- POINTER_LIST *next;
- freeMemory(node->address, 0, MEM_RELEASE, userdata);
- next = node->next;
- free(node);
- node = next;
- }
- }
- #endif
- static BOOL
- CheckSize(size_t size, size_t expected) {
- if (size < expected) {
- SetLastError(ERROR_INVALID_DATA);
- return FALSE;
- }
- return TRUE;
- }
- static BOOL
- CopySections(const unsigned char *data, size_t size, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module)
- {
- int i, section_size;
- unsigned char *codeBase = module->codeBase;
- unsigned char *dest;
- PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
- for (i=0; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
- if (section->SizeOfRawData == 0) {
- // section doesn't contain data in the dll itself, but may define
- // uninitialized data
- section_size = old_headers->OptionalHeader.SectionAlignment;
- if (section_size > 0) {
- dest = (unsigned char *)module->alloc(codeBase + section->VirtualAddress,
- section_size,
- MEM_COMMIT,
- PAGE_READWRITE,
- module->userdata);
- if (dest == NULL) {
- return FALSE;
- }
- // Always use position from file to support alignments smaller
- // than page size (allocation above will align to page size).
- dest = codeBase + section->VirtualAddress;
- // NOTE: On 64bit systems we truncate to 32bit here but expand
- // again later when "PhysicalAddress" is used.
- section->Misc.PhysicalAddress = (DWORD) ((uintptr_t) dest & 0xffffffff);
- memset(dest, 0, section_size);
- }
- // section is empty
- continue;
- }
- if (!CheckSize(size, section->PointerToRawData + section->SizeOfRawData)) {
- return FALSE;
- }
- // commit memory block and copy data from dll
- dest = (unsigned char *)module->alloc(codeBase + section->VirtualAddress,
- section->SizeOfRawData,
- MEM_COMMIT,
- PAGE_READWRITE,
- module->userdata);
- if (dest == NULL) {
- return FALSE;
- }
- // Always use position from file to support alignments smaller
- // than page size (allocation above will align to page size).
- dest = codeBase + section->VirtualAddress;
- memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData);
- // NOTE: On 64bit systems we truncate to 32bit here but expand
- // again later when "PhysicalAddress" is used.
- section->Misc.PhysicalAddress = (DWORD) ((uintptr_t) dest & 0xffffffff);
- }
- return TRUE;
- }
- // Protection flags for memory pages (Executable, Readable, Writeable)
- static int ProtectionFlags[2][2][2] = {
- {
- // not executable
- {PAGE_NOACCESS, PAGE_WRITECOPY},
- {PAGE_READONLY, PAGE_READWRITE},
- }, {
- // executable
- {PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY},
- {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE},
- },
- };
- static SIZE_T
- GetRealSectionSize(PMEMORYMODULE module, PIMAGE_SECTION_HEADER section) {
- DWORD size = section->SizeOfRawData;
- if (size == 0) {
- if (section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
- size = module->headers->OptionalHeader.SizeOfInitializedData;
- } else if (section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
- size = module->headers->OptionalHeader.SizeOfUninitializedData;
- }
- }
- return (SIZE_T) size;
- }
- static BOOL
- FinalizeSection(PMEMORYMODULE module, PSECTIONFINALIZEDATA sectionData) {
- DWORD protect, oldProtect;
- BOOL executable;
- BOOL readable;
- BOOL writeable;
- if (sectionData->size == 0) {
- return TRUE;
- }
- if (sectionData->characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
- // section is not needed any more and can safely be freed
- if (sectionData->address == sectionData->alignedAddress &&
- (sectionData->last ||
- module->headers->OptionalHeader.SectionAlignment == module->pageSize ||
- (sectionData->size % module->pageSize) == 0)
- ) {
- // Only allowed to decommit whole pages
- module->free(sectionData->address, sectionData->size, MEM_DECOMMIT, module->userdata);
- }
- return TRUE;
- }
- // determine protection flags based on characteristics
- executable = (sectionData->characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
- readable = (sectionData->characteristics & IMAGE_SCN_MEM_READ) != 0;
- writeable = (sectionData->characteristics & IMAGE_SCN_MEM_WRITE) != 0;
- protect = ProtectionFlags[executable][readable][writeable];
- if (sectionData->characteristics & IMAGE_SCN_MEM_NOT_CACHED) {
- protect |= PAGE_NOCACHE;
- }
- // change memory access flags
- if (VirtualProtect(sectionData->address, sectionData->size, protect, &oldProtect) == 0) {
- OutputLastError("Error protecting memory page");
- return FALSE;
- }
- return TRUE;
- }
- static BOOL
- FinalizeSections(PMEMORYMODULE module)
- {
- int i;
- PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
- #ifdef _WIN64
- // "PhysicalAddress" might have been truncated to 32bit above, expand to
- // 64bits again.
- uintptr_t imageOffset = ((uintptr_t) module->headers->OptionalHeader.ImageBase & 0xffffffff00000000);
- #else
- static const uintptr_t imageOffset = 0;
- #endif
- SECTIONFINALIZEDATA sectionData;
- sectionData.address = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
- sectionData.alignedAddress = AlignAddressDown(sectionData.address, module->pageSize);
- sectionData.size = GetRealSectionSize(module, section);
- sectionData.characteristics = section->Characteristics;
- sectionData.last = FALSE;
- section++;
- // loop through all sections and change access flags
- for (i=1; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
- LPVOID sectionAddress = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
- LPVOID alignedAddress = AlignAddressDown(sectionAddress, module->pageSize);
- SIZE_T sectionSize = GetRealSectionSize(module, section);
- // Combine access flags of all sections that share a page
- // TODO(fancycode): We currently share flags of a trailing large section
- // with the page of a first small section. This should be optimized.
- if (sectionData.alignedAddress == alignedAddress || (uintptr_t) sectionData.address + sectionData.size > (uintptr_t) alignedAddress) {
- // Section shares page with previous
- if ((section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) {
- sectionData.characteristics = (sectionData.characteristics | section->Characteristics) & ~IMAGE_SCN_MEM_DISCARDABLE;
- } else {
- sectionData.characteristics |= section->Characteristics;
- }
- sectionData.size = (((uintptr_t)sectionAddress) + ((uintptr_t) sectionSize)) - (uintptr_t) sectionData.address;
- continue;
- }
- if (!FinalizeSection(module, §ionData)) {
- return FALSE;
- }
- sectionData.address = sectionAddress;
- sectionData.alignedAddress = alignedAddress;
- sectionData.size = sectionSize;
- sectionData.characteristics = section->Characteristics;
- }
- sectionData.last = TRUE;
- if (!FinalizeSection(module, §ionData)) {
- return FALSE;
- }
- return TRUE;
- }
- static BOOL
- ExecuteTLS(PMEMORYMODULE module)
- {
- unsigned char *codeBase = module->codeBase;
- PIMAGE_TLS_DIRECTORY tls;
- PIMAGE_TLS_CALLBACK* callback;
- PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_TLS);
- if (directory->VirtualAddress == 0) {
- return TRUE;
- }
- tls = (PIMAGE_TLS_DIRECTORY) (codeBase + directory->VirtualAddress);
- callback = (PIMAGE_TLS_CALLBACK *) tls->AddressOfCallBacks;
- if (callback) {
- while (*callback) {
- (*callback)((LPVOID) codeBase, DLL_PROCESS_ATTACH, NULL);
- callback++;
- }
- }
- return TRUE;
- }
- static BOOL
- PerformBaseRelocation(PMEMORYMODULE module, ptrdiff_t delta)
- {
- unsigned char *codeBase = module->codeBase;
- PIMAGE_BASE_RELOCATION relocation;
- PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC);
- if (directory->Size == 0) {
- return (delta == 0);
- }
- relocation = (PIMAGE_BASE_RELOCATION) (codeBase + directory->VirtualAddress);
- for (; relocation->VirtualAddress > 0; ) {
- DWORD i;
- unsigned char *dest = codeBase + relocation->VirtualAddress;
- unsigned short *relInfo = (unsigned short*) OffsetPointer(relocation, IMAGE_SIZEOF_BASE_RELOCATION);
- for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) {
- // the upper 4 bits define the type of relocation
- int type = *relInfo >> 12;
- // the lower 12 bits define the offset
- int offset = *relInfo & 0xfff;
- switch (type)
- {
- case IMAGE_REL_BASED_ABSOLUTE:
- // skip relocation
- break;
- case IMAGE_REL_BASED_HIGHLOW:
- // change complete 32 bit address
- {
- DWORD *patchAddrHL = (DWORD *) (dest + offset);
- *patchAddrHL += (DWORD) delta;
- }
- break;
- #ifdef _WIN64
- case IMAGE_REL_BASED_DIR64:
- {
- ULONGLONG *patchAddr64 = (ULONGLONG *) (dest + offset);
- *patchAddr64 += (ULONGLONG) delta;
- }
- break;
- #endif
- default:
- //printf("Unknown relocation: %d\n", type);
- break;
- }
- }
- // advance to next relocation block
- relocation = (PIMAGE_BASE_RELOCATION) OffsetPointer(relocation, relocation->SizeOfBlock);
- }
- return TRUE;
- }
- static BOOL
- BuildImportTable(PMEMORYMODULE module)
- {
- unsigned char *codeBase = module->codeBase;
- PIMAGE_IMPORT_DESCRIPTOR importDesc;
- BOOL result = TRUE;
- PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT);
- if (directory->Size == 0) {
- return TRUE;
- }
- importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress);
- for (; !IsBadReadPtr(importDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR)) && importDesc->Name; importDesc++) {
- uintptr_t *thunkRef;
- FARPROC *funcRef;
- HCUSTOMMODULE *tmp;
- HCUSTOMMODULE handle = module->loadLibrary((LPCSTR) (codeBase + importDesc->Name), module->userdata);
- if (handle == NULL) {
- SetLastError(ERROR_MOD_NOT_FOUND);
- result = FALSE;
- break;
- }
- tmp = (HCUSTOMMODULE *) realloc(module->modules, (module->numModules+1)*(sizeof(HCUSTOMMODULE)));
- if (tmp == NULL) {
- module->freeLibrary(handle, module->userdata);
- SetLastError(ERROR_OUTOFMEMORY);
- result = FALSE;
- break;
- }
- module->modules = tmp;
- module->modules[module->numModules++] = handle;
- if (importDesc->OriginalFirstThunk) {
- thunkRef = (uintptr_t *) (codeBase + importDesc->OriginalFirstThunk);
- funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk);
- } else {
- // no hint table
- thunkRef = (uintptr_t *) (codeBase + importDesc->FirstThunk);
- funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk);
- }
- for (; *thunkRef; thunkRef++, funcRef++) {
- if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) {
- *funcRef = module->getProcAddress(handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef), module->userdata);
- } else {
- PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME) (codeBase + (*thunkRef));
- *funcRef = module->getProcAddress(handle, (LPCSTR)&thunkData->Name, module->userdata);
- }
- if (*funcRef == 0) {
- result = FALSE;
- break;
- }
- }
- if (!result) {
- module->freeLibrary(handle, module->userdata);
- SetLastError(ERROR_PROC_NOT_FOUND);
- break;
- }
- }
- return result;
- }
- LPVOID MemoryDefaultAlloc(LPVOID address, SIZE_T size, DWORD allocationType, DWORD protect, void* userdata)
- {
- UNREFERENCED_PARAMETER(userdata);
- return VirtualAlloc(address, size, allocationType, protect);
- }
- BOOL MemoryDefaultFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType, void* userdata)
- {
- UNREFERENCED_PARAMETER(userdata);
- return VirtualFree(lpAddress, dwSize, dwFreeType);
- }
- HCUSTOMMODULE MemoryDefaultLoadLibrary(LPCSTR filename, void *userdata)
- {
- HMODULE result;
- UNREFERENCED_PARAMETER(userdata);
- result = LoadLibraryA(filename);
- if (result == NULL) {
- return NULL;
- }
- return (HCUSTOMMODULE) result;
- }
- FARPROC MemoryDefaultGetProcAddress(HCUSTOMMODULE module, LPCSTR name, void *userdata)
- {
- UNREFERENCED_PARAMETER(userdata);
- return (FARPROC) GetProcAddress((HMODULE) module, name);
- }
- void MemoryDefaultFreeLibrary(HCUSTOMMODULE module, void *userdata)
- {
- UNREFERENCED_PARAMETER(userdata);
- FreeLibrary((HMODULE) module);
- }
- HMEMORYMODULE MemoryLoadLibrary(const void *data, size_t size)
- {
- return MemoryLoadLibraryEx(data, size, MemoryDefaultAlloc, MemoryDefaultFree, MemoryDefaultLoadLibrary, MemoryDefaultGetProcAddress, MemoryDefaultFreeLibrary, NULL);
- }
- HMEMORYMODULE MemoryLoadLibraryEx(const void *data, size_t size,
- CustomAllocFunc allocMemory,
- CustomFreeFunc freeMemory,
- CustomLoadLibraryFunc loadLibrary,
- CustomGetProcAddressFunc getProcAddress,
- CustomFreeLibraryFunc freeLibrary,
- void *userdata)
- {
- PMEMORYMODULE result = NULL;
- PIMAGE_DOS_HEADER dos_header;
- PIMAGE_NT_HEADERS old_header;
- unsigned char *code, *headers;
- ptrdiff_t locationDelta;
- SYSTEM_INFO sysInfo;
- PIMAGE_SECTION_HEADER section;
- DWORD i;
- size_t optionalSectionSize;
- size_t lastSectionEnd = 0;
- size_t alignedImageSize;
- #ifdef _WIN64
- POINTER_LIST *blockedMemory = NULL;
- #endif
- if (!CheckSize(size, sizeof(IMAGE_DOS_HEADER))) {
- return NULL;
- }
- dos_header = (PIMAGE_DOS_HEADER)data;
- if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
- SetLastError(ERROR_BAD_EXE_FORMAT);
- return NULL;
- }
- if (!CheckSize(size, dos_header->e_lfanew + sizeof(IMAGE_NT_HEADERS))) {
- return NULL;
- }
- old_header = (PIMAGE_NT_HEADERS)&((const unsigned char *)(data))[dos_header->e_lfanew];
- if (old_header->Signature != IMAGE_NT_SIGNATURE) {
- SetLastError(ERROR_BAD_EXE_FORMAT);
- return NULL;
- }
- if (old_header->FileHeader.Machine != HOST_MACHINE) {
- SetLastError(ERROR_BAD_EXE_FORMAT);
- return NULL;
- }
- if (old_header->OptionalHeader.SectionAlignment & 1) {
- // Only support section alignments that are a multiple of 2
- SetLastError(ERROR_BAD_EXE_FORMAT);
- return NULL;
- }
- section = IMAGE_FIRST_SECTION(old_header);
- optionalSectionSize = old_header->OptionalHeader.SectionAlignment;
- for (i=0; i<old_header->FileHeader.NumberOfSections; i++, section++) {
- size_t endOfSection;
- if (section->SizeOfRawData == 0) {
- // Section without data in the DLL
- endOfSection = section->VirtualAddress + optionalSectionSize;
- } else {
- endOfSection = section->VirtualAddress + section->SizeOfRawData;
- }
- if (endOfSection > lastSectionEnd) {
- lastSectionEnd = endOfSection;
- }
- }
- GetNativeSystemInfo(&sysInfo);
- alignedImageSize = AlignValueUp(old_header->OptionalHeader.SizeOfImage, sysInfo.dwPageSize);
- if (alignedImageSize != AlignValueUp(lastSectionEnd, sysInfo.dwPageSize)) {
- SetLastError(ERROR_BAD_EXE_FORMAT);
- return NULL;
- }
- // reserve memory for image of library
- // XXX: is it correct to commit the complete memory region at once?
- // calling DllEntry raises an exception if we don't...
- code = (unsigned char *)allocMemory((LPVOID)(old_header->OptionalHeader.ImageBase),
- alignedImageSize,
- MEM_RESERVE | MEM_COMMIT,
- PAGE_READWRITE,
- userdata);
- if (code == NULL) {
- // try to allocate memory at arbitrary position
- code = (unsigned char *)allocMemory(NULL,
- alignedImageSize,
- MEM_RESERVE | MEM_COMMIT,
- PAGE_READWRITE,
- userdata);
- if (code == NULL) {
- SetLastError(ERROR_OUTOFMEMORY);
- return NULL;
- }
- }
- #ifdef _WIN64
- // Memory block may not span 4 GB boundaries.
- while ((((uintptr_t) code) >> 32) < (((uintptr_t) (code + alignedImageSize)) >> 32)) {
- POINTER_LIST *node = (POINTER_LIST*) malloc(sizeof(POINTER_LIST));
- if (!node) {
- freeMemory(code, 0, MEM_RELEASE, userdata);
- FreePointerList(blockedMemory, freeMemory, userdata);
- SetLastError(ERROR_OUTOFMEMORY);
- return NULL;
- }
- node->next = blockedMemory;
- node->address = code;
- blockedMemory = node;
- code = (unsigned char *)allocMemory(NULL,
- alignedImageSize,
- MEM_RESERVE | MEM_COMMIT,
- PAGE_READWRITE,
- userdata);
- if (code == NULL) {
- FreePointerList(blockedMemory, freeMemory, userdata);
- SetLastError(ERROR_OUTOFMEMORY);
- return NULL;
- }
- }
- #endif
- result = (PMEMORYMODULE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MEMORYMODULE));
- if (result == NULL) {
- freeMemory(code, 0, MEM_RELEASE, userdata);
- #ifdef _WIN64
- FreePointerList(blockedMemory, freeMemory, userdata);
- #endif
- SetLastError(ERROR_OUTOFMEMORY);
- return NULL;
- }
- result->codeBase = code;
- result->isDLL = (old_header->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0;
- result->alloc = allocMemory;
- result->free = freeMemory;
- result->loadLibrary = loadLibrary;
- result->getProcAddress = getProcAddress;
- result->freeLibrary = freeLibrary;
- result->userdata = userdata;
- result->pageSize = sysInfo.dwPageSize;
- #ifdef _WIN64
- result->blockedMemory = blockedMemory;
- #endif
- if (!CheckSize(size, old_header->OptionalHeader.SizeOfHeaders)) {
- goto error;
- }
- // commit memory for headers
- headers = (unsigned char *)allocMemory(code,
- old_header->OptionalHeader.SizeOfHeaders,
- MEM_COMMIT,
- PAGE_READWRITE,
- userdata);
- // copy PE header to code
- memcpy(headers, dos_header, old_header->OptionalHeader.SizeOfHeaders);
- result->headers = (PIMAGE_NT_HEADERS)&((const unsigned char *)(headers))[dos_header->e_lfanew];
- // update position
- result->headers->OptionalHeader.ImageBase = (uintptr_t)code;
- // copy sections from DLL file block to new memory location
- if (!CopySections((const unsigned char *) data, size, old_header, result)) {
- goto error;
- }
- // adjust base address of imported data
- locationDelta = (ptrdiff_t)(result->headers->OptionalHeader.ImageBase - old_header->OptionalHeader.ImageBase);
- if (locationDelta != 0) {
- result->isRelocated = PerformBaseRelocation(result, locationDelta);
- } else {
- result->isRelocated = TRUE;
- }
- // load required dlls and adjust function table of imports
- if (!BuildImportTable(result)) {
- goto error;
- }
- // mark memory pages depending on section headers and release
- // sections that are marked as "discardable"
- if (!FinalizeSections(result)) {
- goto error;
- }
- // TLS callbacks are executed BEFORE the main loading
- if (!ExecuteTLS(result)) {
- goto error;
- }
- // get entry point of loaded library
- if (result->headers->OptionalHeader.AddressOfEntryPoint != 0) {
- if (result->isDLL) {
- DllEntryProc DllEntry = (DllEntryProc)(LPVOID)(code + result->headers->OptionalHeader.AddressOfEntryPoint);
- // notify library about attaching to process
- BOOL successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0);
- if (!successfull) {
- SetLastError(ERROR_DLL_INIT_FAILED);
- goto error;
- }
- result->initialized = TRUE;
- } else {
- result->exeEntry = (ExeEntryProc)(LPVOID)(code + result->headers->OptionalHeader.AddressOfEntryPoint);
- }
- } else {
- result->exeEntry = NULL;
- }
- return (HMEMORYMODULE)result;
- error:
- // cleanup
- MemoryFreeLibrary(result);
- return NULL;
- }
- static int _compare(const void *a, const void *b)
- {
- const struct ExportNameEntry *p1 = (const struct ExportNameEntry*) a;
- const struct ExportNameEntry *p2 = (const struct ExportNameEntry*) b;
- return strcmp(p1->name, p2->name);
- }
- static int _find(const void *a, const void *b)
- {
- LPCSTR *name = (LPCSTR *) a;
- const struct ExportNameEntry *p = (const struct ExportNameEntry*) b;
- return strcmp(*name, p->name);
- }
- FARPROC MemoryGetProcAddress(HMEMORYMODULE mod, LPCSTR name)
- {
- PMEMORYMODULE module = (PMEMORYMODULE)mod;
- unsigned char *codeBase = module->codeBase;
- DWORD idx = 0;
- PIMAGE_EXPORT_DIRECTORY exports;
- PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXPORT);
- if (directory->Size == 0) {
- // no export table found
- SetLastError(ERROR_PROC_NOT_FOUND);
- return NULL;
- }
- exports = (PIMAGE_EXPORT_DIRECTORY) (codeBase + directory->VirtualAddress);
- if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) {
- // DLL doesn't export anything
- SetLastError(ERROR_PROC_NOT_FOUND);
- return NULL;
- }
- if (HIWORD(name) == 0) {
- // load function by ordinal value
- if (LOWORD(name) < exports->Base) {
- SetLastError(ERROR_PROC_NOT_FOUND);
- return NULL;
- }
- idx = LOWORD(name) - exports->Base;
- } else if (!exports->NumberOfNames) {
- SetLastError(ERROR_PROC_NOT_FOUND);
- return NULL;
- } else {
- const struct ExportNameEntry *found;
- // Lazily build name table and sort it by names
- if (!module->nameExportsTable) {
- DWORD i;
- DWORD *nameRef = (DWORD *) (codeBase + exports->AddressOfNames);
- WORD *ordinal = (WORD *) (codeBase + exports->AddressOfNameOrdinals);
- struct ExportNameEntry *entry = (struct ExportNameEntry*) malloc(exports->NumberOfNames * sizeof(struct ExportNameEntry));
- module->nameExportsTable = entry;
- if (!entry) {
- SetLastError(ERROR_OUTOFMEMORY);
- return NULL;
- }
- for (i=0; i<exports->NumberOfNames; i++, nameRef++, ordinal++, entry++) {
- entry->name = (const char *) (codeBase + (*nameRef));
- entry->idx = *ordinal;
- }
- qsort(module->nameExportsTable,
- exports->NumberOfNames,
- sizeof(struct ExportNameEntry), _compare);
- }
- // search function name in list of exported names with binary search
- found = (const struct ExportNameEntry*) bsearch(&name,
- module->nameExportsTable,
- exports->NumberOfNames,
- sizeof(struct ExportNameEntry), _find);
- if (!found) {
- // exported symbol not found
- SetLastError(ERROR_PROC_NOT_FOUND);
- return NULL;
- }
- idx = found->idx;
- }
- if (idx > exports->NumberOfFunctions) {
- // name <-> ordinal number don't match
- SetLastError(ERROR_PROC_NOT_FOUND);
- return NULL;
- }
- // AddressOfFunctions contains the RVAs to the "real" functions
- return (FARPROC)(LPVOID)(codeBase + (*(DWORD *) (codeBase + exports->AddressOfFunctions + (idx*4))));
- }
- void MemoryFreeLibrary(HMEMORYMODULE mod)
- {
- PMEMORYMODULE module = (PMEMORYMODULE)mod;
- if (module == NULL) {
- return;
- }
- if (module->initialized) {
- // notify library about detaching from process
- DllEntryProc DllEntry = (DllEntryProc)(LPVOID)(module->codeBase + module->headers->OptionalHeader.AddressOfEntryPoint);
- (*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0);
- }
- free(module->nameExportsTable);
- if (module->modules != NULL) {
- // free previously opened libraries
- int i;
- for (i=0; i<module->numModules; i++) {
- if (module->modules[i] != NULL) {
- module->freeLibrary(module->modules[i], module->userdata);
- }
- }
- free(module->modules);
- }
- if (module->codeBase != NULL) {
- // release memory of library
- module->free(module->codeBase, 0, MEM_RELEASE, module->userdata);
- }
- #ifdef _WIN64
- FreePointerList(module->blockedMemory, module->free, module->userdata);
- #endif
- HeapFree(GetProcessHeap(), 0, module);
- }
- int MemoryCallEntryPoint(HMEMORYMODULE mod)
- {
- PMEMORYMODULE module = (PMEMORYMODULE)mod;
- if (module == NULL || module->isDLL || module->exeEntry == NULL || !module->isRelocated) {
- return -1;
- }
- return module->exeEntry();
- }
- #define DEFAULT_LANGUAGE MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)
- HMEMORYRSRC MemoryFindResource(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type)
- {
- return MemoryFindResourceEx(module, name, type, DEFAULT_LANGUAGE);
- }
- static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntry(
- void *root,
- PIMAGE_RESOURCE_DIRECTORY resources,
- LPCTSTR key)
- {
- PIMAGE_RESOURCE_DIRECTORY_ENTRY entries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (resources + 1);
- PIMAGE_RESOURCE_DIRECTORY_ENTRY result = NULL;
- DWORD start;
- DWORD end;
- DWORD middle;
- if (!IS_INTRESOURCE(key) && key[0] == TEXT('#')) {
- // special case: resource id given as string
- TCHAR *endpos = NULL;
- long int tmpkey = (WORD) _tcstol((TCHAR *) &key[1], &endpos, 10);
- if (tmpkey <= 0xffff && lstrlen(endpos) == 0) {
- key = MAKEINTRESOURCE(tmpkey);
- }
- }
- // entries are stored as ordered list of named entries,
- // followed by an ordered list of id entries - we can do
- // a binary search to find faster...
- if (IS_INTRESOURCE(key)) {
- WORD check = (WORD) (uintptr_t) key;
- start = resources->NumberOfNamedEntries;
- end = start + resources->NumberOfIdEntries;
- while (end > start) {
- WORD entryName;
- middle = (start + end) >> 1;
- entryName = (WORD) entries[middle].Name;
- if (check < entryName) {
- end = (end != middle ? middle : middle-1);
- } else if (check > entryName) {
- start = (start != middle ? middle : middle+1);
- } else {
- result = &entries[middle];
- break;
- }
- }
- } else {
- LPCWSTR searchKey;
- size_t searchKeyLen = _tcslen(key);
- #if defined(UNICODE)
- searchKey = key;
- #else
- // Resource names are always stored using 16bit characters, need to
- // convert string we search for.
- #define MAX_LOCAL_KEY_LENGTH 2048
- // In most cases resource names are short, so optimize for that by
- // using a pre-allocated array.
- wchar_t _searchKeySpace[MAX_LOCAL_KEY_LENGTH+1];
- LPWSTR _searchKey;
- if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) {
- size_t _searchKeySize = (searchKeyLen + 1) * sizeof(wchar_t);
- _searchKey = (LPWSTR) malloc(_searchKeySize);
- if (_searchKey == NULL) {
- SetLastError(ERROR_OUTOFMEMORY);
- return NULL;
- }
- } else {
- _searchKey = &_searchKeySpace[0];
- }
- mbstowcs(_searchKey, key, searchKeyLen);
- _searchKey[searchKeyLen] = 0;
- searchKey = _searchKey;
- #endif
- start = 0;
- end = resources->NumberOfNamedEntries;
- while (end > start) {
- int cmp;
- PIMAGE_RESOURCE_DIR_STRING_U resourceString;
- middle = (start + end) >> 1;
- resourceString = (PIMAGE_RESOURCE_DIR_STRING_U) OffsetPointer(root, entries[middle].Name & 0x7FFFFFFF);
- cmp = _wcsnicmp(searchKey, resourceString->NameString, resourceString->Length);
- if (cmp == 0) {
- // Handle partial match
- if (searchKeyLen > resourceString->Length) {
- cmp = 1;
- } else if (searchKeyLen < resourceString->Length) {
- cmp = -1;
- }
- }
- if (cmp < 0) {
- end = (middle != end ? middle : middle-1);
- } else if (cmp > 0) {
- start = (middle != start ? middle : middle+1);
- } else {
- result = &entries[middle];
- break;
- }
- }
- #if !defined(UNICODE)
- if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) {
- free(_searchKey);
- }
- #undef MAX_LOCAL_KEY_LENGTH
- #endif
- }
- return result;
- }
- HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type, WORD language)
- {
- unsigned char *codeBase = ((PMEMORYMODULE) module)->codeBase;
- PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE) module, IMAGE_DIRECTORY_ENTRY_RESOURCE);
- PIMAGE_RESOURCE_DIRECTORY rootResources;
- PIMAGE_RESOURCE_DIRECTORY nameResources;
- PIMAGE_RESOURCE_DIRECTORY typeResources;
- PIMAGE_RESOURCE_DIRECTORY_ENTRY foundType;
- PIMAGE_RESOURCE_DIRECTORY_ENTRY foundName;
- PIMAGE_RESOURCE_DIRECTORY_ENTRY foundLanguage;
- if (directory->Size == 0) {
- // no resource table found
- SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
- return NULL;
- }
- if (language == DEFAULT_LANGUAGE) {
- // use language from current thread
- language = LANGIDFROMLCID(GetThreadLocale());
- }
- // resources are stored as three-level tree
- // - first node is the type
- // - second node is the name
- // - third node is the language
- rootResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress);
- foundType = _MemorySearchResourceEntry(rootResources, rootResources, type);
- if (foundType == NULL) {
- SetLastError(ERROR_RESOURCE_TYPE_NOT_FOUND);
- return NULL;
- }
- typeResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundType->OffsetToData & 0x7fffffff));
- foundName = _MemorySearchResourceEntry(rootResources, typeResources, name);
- if (foundName == NULL) {
- SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND);
- return NULL;
- }
- nameResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundName->OffsetToData & 0x7fffffff));
- foundLanguage = _MemorySearchResourceEntry(rootResources, nameResources, (LPCTSTR) (uintptr_t) language);
- if (foundLanguage == NULL) {
- // requested language not found, use first available
- if (nameResources->NumberOfIdEntries == 0) {
- SetLastError(ERROR_RESOURCE_LANG_NOT_FOUND);
- return NULL;
- }
- foundLanguage = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (nameResources + 1);
- }
- return (codeBase + directory->VirtualAddress + (foundLanguage->OffsetToData & 0x7fffffff));
- }
- DWORD MemorySizeofResource(HMEMORYMODULE module, HMEMORYRSRC resource)
- {
- PIMAGE_RESOURCE_DATA_ENTRY entry;
- UNREFERENCED_PARAMETER(module);
- entry = (PIMAGE_RESOURCE_DATA_ENTRY) resource;
- if (entry == NULL) {
- return 0;
- }
- return entry->Size;
- }
- LPVOID MemoryLoadResource(HMEMORYMODULE module, HMEMORYRSRC resource)
- {
- unsigned char *codeBase = ((PMEMORYMODULE) module)->codeBase;
- PIMAGE_RESOURCE_DATA_ENTRY entry = (PIMAGE_RESOURCE_DATA_ENTRY) resource;
- if (entry == NULL) {
- return NULL;
- }
- return codeBase + entry->OffsetToData;
- }
- int
- MemoryLoadString(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize)
- {
- return MemoryLoadStringEx(module, id, buffer, maxsize, DEFAULT_LANGUAGE);
- }
- int
- MemoryLoadStringEx(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize, WORD language)
- {
- HMEMORYRSRC resource;
- PIMAGE_RESOURCE_DIR_STRING_U data;
- DWORD size;
- if (maxsize == 0) {
- return 0;
- }
- resource = MemoryFindResourceEx(module, MAKEINTRESOURCE((id >> 4) + 1), RT_STRING, language);
- if (resource == NULL) {
- buffer[0] = 0;
- return 0;
- }
- data = (PIMAGE_RESOURCE_DIR_STRING_U) MemoryLoadResource(module, resource);
- id = id & 0x0f;
- while (id--) {
- data = (PIMAGE_RESOURCE_DIR_STRING_U) OffsetPointer(data, (data->Length + 1) * sizeof(WCHAR));
- }
- if (data->Length == 0) {
- SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND);
- buffer[0] = 0;
- return 0;
- }
- size = data->Length;
- if (size >= (DWORD) maxsize) {
- size = maxsize;
- } else {
- buffer[size] = 0;
- }
- #pragma warning(disable:4996)
- #if defined(UNICODE)
- wcsncpy(buffer, data->NameString, size);
- #else
- wcstombs(buffer, data->NameString, size);
- #endif
- return size;
- }
- #ifdef TESTSUITE
- #include <stdio.h>
- #ifndef PRIxPTR
- #ifdef _WIN64
- #define PRIxPTR "I64x"
- #else
- #define PRIxPTR "x"
- #endif
- #endif
- static const uintptr_t AlignValueDownTests[][3] = {
- {16, 16, 16},
- {17, 16, 16},
- {32, 16, 32},
- {33, 16, 32},
- #ifdef _WIN64
- {0x12345678abcd1000, 0x1000, 0x12345678abcd1000},
- {0x12345678abcd101f, 0x1000, 0x12345678abcd1000},
- #endif
- {0, 0, 0},
- };
- static const uintptr_t AlignValueUpTests[][3] = {
- {16, 16, 16},
- {17, 16, 32},
- {32, 16, 32},
- {33, 16, 48},
- #ifdef _WIN64
- {0x12345678abcd1000, 0x1000, 0x12345678abcd1000},
- {0x12345678abcd101f, 0x1000, 0x12345678abcd2000},
- #endif
- {0, 0, 0},
- };
- BOOL MemoryModuleTestsuite() {
- BOOL success = TRUE;
- size_t idx;
- for (idx = 0; AlignValueDownTests[idx][0]; ++idx) {
- const uintptr_t* tests = AlignValueDownTests[idx];
- uintptr_t value = AlignValueDown(tests[0], tests[1]);
- if (value != tests[2]) {
- printf("AlignValueDown failed for 0x%" PRIxPTR "/0x%" PRIxPTR ": expected 0x%" PRIxPTR ", got 0x%" PRIxPTR "\n",
- tests[0], tests[1], tests[2], value);
- success = FALSE;
- }
- }
- for (idx = 0; AlignValueDownTests[idx][0]; ++idx) {
- const uintptr_t* tests = AlignValueUpTests[idx];
- uintptr_t value = AlignValueUp(tests[0], tests[1]);
- if (value != tests[2]) {
- printf("AlignValueUp failed for 0x%" PRIxPTR "/0x%" PRIxPTR ": expected 0x%" PRIxPTR ", got 0x%" PRIxPTR "\n",
- tests[0], tests[1], tests[2], value);
- success = FALSE;
- }
- }
- if (success) {
- printf("OK\n");
- }
- return success;
- }
- #endif
|