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; 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; } } GetSystemInfo(&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; } } result = (PMEMORYMODULE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MEMORYMODULE)); if (result == NULL) { freeMemory(code, 0, MEM_RELEASE, userdata); 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; 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; }
HMEMORYMODULE MemoryLoadLibraryEx(const void *data, size_t size, 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; if (size && !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 (size && !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; } #ifdef _WIN64 if (old_header->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) { #else if (old_header->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) { #endif 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 = ALIGN_VALUE_UP(old_header->OptionalHeader.SizeOfImage, sysInfo.dwPageSize); if (alignedImageSize < ALIGN_VALUE_UP(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 *)VirtualAlloc((LPVOID)(old_header->OptionalHeader.ImageBase), alignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (code == NULL) { // try to allocate memory at arbitrary position code = (unsigned char *)VirtualAlloc(NULL, alignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (code == NULL) { SetLastError(ERROR_OUTOFMEMORY); return NULL; } } result = (PMEMORYMODULE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MEMORYMODULE)); if (result == NULL) { VirtualFree(code, 0, MEM_RELEASE); SetLastError(ERROR_OUTOFMEMORY); return NULL; } result->codeBase = code; result->isDLL = (old_header->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0; result->loadLibrary = loadLibrary; result->getProcAddress = getProcAddress; result->freeLibrary = freeLibrary; result->userdata = userdata; result->pageSize = sysInfo.dwPageSize; if (size && !CheckSize(size, old_header->OptionalHeader.SizeOfHeaders)) { goto error; } // commit memory for headers headers = (unsigned char *)VirtualAlloc(code, old_header->OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE); // 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 (old_header->OptionalHeader.ImageBase != (DWORD)g_hInstance && !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); PCRITICAL_SECTION aLoaderLock; // So no other module can be loaded, expecially due to hooked _RtlPcToFileHeader #ifdef _M_IX86 // compiles for x86 aLoaderLock = *(PCRITICAL_SECTION*)(__readfsdword(0x30) + 0xA0); //PEB->LoaderLock #elif _M_AMD64 // compiles for x64 aLoaderLock = *(PCRITICAL_SECTION*)(__readgsqword(0x60) + 0x110); //PEB->LoaderLock //0x60 because offset is doubled in 64bit #endif HANDLE hHeap = NULL; // set start and end of memory for our module so HookRtlPcToFileHeader can report properly currentModuleStart = result->codeBase; currentModuleEnd = result->codeBase + result->headers->OptionalHeader.SizeOfImage; if (!_RtlPcToFileHeader) _RtlPcToFileHeader = (MyRtlPcToFileHeader)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlPcToFileHeader"); EnterCriticalSection(aLoaderLock); PHOOK_ENTRY pHook = MinHookEnable(_RtlPcToFileHeader, &HookRtlPcToFileHeader, &hHeap); // notify library about attaching to process BOOL successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, result); // Disable hook if it was enabled before if (pHook) { MinHookDisable(pHook); HeapFree(hHeap, 0, pHook); HeapDestroy(hHeap); } LeaveCriticalSection(aLoaderLock); 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; } FARPROC MemoryGetProcAddress(HMEMORYMODULE module, LPCSTR name) { unsigned char *codeBase = ((PMEMORYMODULE)module)->codeBase; DWORD idx = 0; PIMAGE_EXPORT_DIRECTORY exports; PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE)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 { // search function name in list of exported names DWORD i; DWORD *nameRef = (DWORD *) (codeBase + exports->AddressOfNames); WORD *ordinal = (WORD *) (codeBase + exports->AddressOfNameOrdinals); BOOL found = FALSE; for (i=0; i<exports->NumberOfNames; i++, nameRef++, ordinal++) { if (_stricmp(name, (const char *) (codeBase + (*nameRef))) == 0) { idx = *ordinal; found = TRUE; break; } } if (!found) { // exported symbol not found SetLastError(ERROR_PROC_NOT_FOUND); return NULL; } } 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)))); }
HMEMORYMODULE MemoryLoadLibraryEx(const void *data, CustomLoadLibraryFunc loadLibrary, CustomGetProcAddressFunc getProcAddress, CustomFreeLibraryFunc freeLibrary, void *userdata) { PMEMORYMODULE result; PIMAGE_DOS_HEADER dos_header; PIMAGE_NT_HEADERS old_header; unsigned char *code, *headers; SIZE_T locationDelta; SYSTEM_INFO sysInfo; HMODULE hModule; dos_header = (PIMAGE_DOS_HEADER)data; if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) { SetLastError(ERROR_BAD_EXE_FORMAT); 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; } #ifdef _WIN64 if (old_header->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) { #else if (old_header->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) { #endif 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; } // 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 *)VirtualAlloc((LPVOID)(old_header->OptionalHeader.ImageBase), old_header->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (code == NULL) { // try to allocate memory at arbitrary position code = (unsigned char *)VirtualAlloc(NULL, old_header->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (code == NULL) { SetLastError(ERROR_OUTOFMEMORY); return NULL; } } result = (PMEMORYMODULE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MEMORYMODULE)); if (result == NULL) { VirtualFree(code, 0, MEM_RELEASE); SetLastError(ERROR_OUTOFMEMORY); return NULL; } result->codeBase = code; result->isDLL = (old_header->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0; result->loadLibrary = loadLibrary; result->getProcAddress = getProcAddress; result->freeLibrary = freeLibrary; result->userdata = userdata; hModule = LoadLibrary ("kernel32.dll"); if (hModule) { int (WINAPI *GetNativeSystemInfo) (SYSTEM_INFO *systemInfo); GetNativeSystemInfo = (void *) GetProcAddress (hModule, "GetNativeSystemInfo"); GetNativeSystemInfo(&sysInfo); } result->pageSize = sysInfo.dwPageSize; // commit memory for headers headers = (unsigned char *)VirtualAlloc(code, old_header->OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE); // 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, old_header, result)) { goto error; } // adjust base address of imported data locationDelta = (SIZE_T)(code - 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) (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) (code + result->headers->OptionalHeader.AddressOfEntryPoint); } } else { result->exeEntry = NULL; } return (HMEMORYMODULE)result; error: // cleanup MemoryFreeLibrary(result); return NULL; } FARPROC MemoryGetProcAddress(HMEMORYMODULE module, LPCSTR name) { unsigned char *codeBase = ((PMEMORYMODULE)module)->codeBase; DWORD idx; PIMAGE_EXPORT_DIRECTORY exports; PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE)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 { // search function name in list of exported names DWORD i; DWORD *nameRef = (DWORD *) (codeBase + exports->AddressOfNames); WORD *ordinal = (WORD *) (codeBase + exports->AddressOfNameOrdinals); BOOL found = FALSE; for (i=0; i<exports->NumberOfNames; i++, nameRef++, ordinal++) { if (_stricmp(name, (const char *) (codeBase + (*nameRef))) == 0) { idx = *ordinal; found = TRUE; break; } } if (!found) { // exported symbol not found SetLastError(ERROR_PROC_NOT_FOUND); return NULL; } } 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) (codeBase + (*(DWORD *) (codeBase + exports->AddressOfFunctions + (idx*4)))); }