#include #include #include #include #include static SIZE_T align_up(SIZE_T v, SIZE_T a) { return (v + a - 1) & ~(a - 1); } std::vector read_file_bytes(const char* path) { std::ifstream f(path, std::ios::binary | std::ios::ate); if (!f) return {}; std::streamsize sz = f.tellg(); f.seekg(0, std::ios::beg); std::vector buf((size_t)sz); if (!f.read(buf.data(), sz)) return {}; return buf; } HMODULE manual_map_from_memory(const uint8_t* src, size_t srcSize) { if (!src) return nullptr; auto dos = (IMAGE_DOS_HEADER*)src; if (dos->e_magic != IMAGE_DOS_SIGNATURE) return nullptr; auto nth = (IMAGE_NT_HEADERS*)(src + dos->e_lfanew); if (nth->Signature != IMAGE_NT_SIGNATURE) return nullptr; IMAGE_OPTIONAL_HEADER& opt = nth->OptionalHeader; const SIZE_T imageSize = opt.SizeOfImage; const SIZE_T headersSize = opt.SizeOfHeaders; const DWORD align = opt.SectionAlignment; // Allocate memory for the image uint8_t* imageBase = (uint8_t*)VirtualAlloc(nullptr, imageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!imageBase) return nullptr; // Copy headers memcpy(imageBase, src, headersSize); // Copy sections IMAGE_SECTION_HEADER* sect = IMAGE_FIRST_SECTION(nth); for (unsigned i = 0; i < nth->FileHeader.NumberOfSections; ++i) { uint8_t* dest = imageBase + sect[i].VirtualAddress; uint8_t* srcSec = (uint8_t*)src + sect[i].PointerToRawData; SIZE_T copySize = min((SIZE_T)sect[i].SizeOfRawData, (SIZE_T)sect[i].Misc.VirtualSize); if (copySize && (SIZE_T)sect[i].PointerToRawData + copySize <= srcSize) { memcpy(dest, srcSec, copySize); } else { // Zero remainder SIZE_T vsz = sect[i].Misc.VirtualSize; if (vsz) memset(dest, 0, vsz); } } // Perform base relocations if needed intptr_t delta = (intptr_t)(imageBase - (uint8_t*)opt.ImageBase); if (delta != 0 && opt.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) { auto reloc = (IMAGE_BASE_RELOCATION*)(imageBase + opt.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); SIZE_T relocSize = opt.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; SIZE_T processed = 0; while (processed < relocSize && reloc->SizeOfBlock) { DWORD count = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); WORD* list = (WORD*)((uint8_t*)reloc + sizeof(IMAGE_BASE_RELOCATION)); for (DWORD i = 0; i < count; ++i) { WORD entry = list[i]; WORD type = entry >> 12; WORD offset = entry & 0x0FFF; if (type == IMAGE_REL_BASED_DIR64) { uint64_t* addr = (uint64_t*)(imageBase + reloc->VirtualAddress + offset); *addr = (uint64_t)(*addr + delta); } else if (type == IMAGE_REL_BASED_HIGHLOW) { uint32_t* addr = (uint32_t*)(imageBase + reloc->VirtualAddress + offset); *addr = (uint32_t)(*addr + (uint32_t)delta); } else if (type == IMAGE_REL_BASED_ABSOLUTE) { // skip } else { // other types ignored in simple loader } } processed += reloc->SizeOfBlock; reloc = (IMAGE_BASE_RELOCATION*)((uint8_t*)reloc + reloc->SizeOfBlock); } } // Resolve imports if (opt.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size) { auto importDesc = (IMAGE_IMPORT_DESCRIPTOR*)(imageBase + opt.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); while (importDesc->Name) { char* dllName = (char*)(imageBase + importDesc->Name); HMODULE hMod = LoadLibraryA(dllName); if (!hMod) { VirtualFree(imageBase, 0, MEM_RELEASE); return nullptr; } IMAGE_THUNK_DATA* thunkILT = (IMAGE_THUNK_DATA*)(imageBase + importDesc->OriginalFirstThunk); IMAGE_THUNK_DATA* thunkIAT = (IMAGE_THUNK_DATA*)(imageBase + importDesc->FirstThunk); if (!importDesc->OriginalFirstThunk) thunkILT = thunkIAT; // some binaries omit OriginalFirstThunk while (thunkILT->u1.AddressOfData) { FARPROC func = nullptr; if (thunkILT->u1.Ordinal & IMAGE_ORDINAL_FLAG) { // import by ordinal DWORD ord = IMAGE_ORDINAL(thunkILT->u1.Ordinal); func = GetProcAddress(hMod, reinterpret_cast(static_cast(ord))); } else { IMAGE_IMPORT_BY_NAME* ibn = (IMAGE_IMPORT_BY_NAME*)(imageBase + thunkILT->u1.AddressOfData); func = GetProcAddress(hMod, ibn->Name); } if (!func) { VirtualFree(imageBase, 0, MEM_RELEASE); return nullptr; } thunkIAT->u1.Function = (ULONGLONG)func; ++thunkILT; ++thunkIAT; } ++importDesc; } } // Set final protections for sections for (unsigned i = 0; i < nth->FileHeader.NumberOfSections; ++i) { uint8_t* secAddr = imageBase + sect[i].VirtualAddress; SIZE_T secSize = max((SIZE_T)sect[i].Misc.VirtualSize, (SIZE_T)1); DWORD protect = PAGE_NOACCESS; bool isExec = (sect[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; bool isRead = (sect[i].Characteristics & IMAGE_SCN_MEM_READ) != 0; bool isWrite = (sect[i].Characteristics & IMAGE_SCN_MEM_WRITE) != 0; if (isExec) { if (isRead) protect = isWrite ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; else protect = isWrite ? PAGE_EXECUTE_WRITECOPY : PAGE_EXECUTE; } else { if (isRead) protect = isWrite ? PAGE_READWRITE : PAGE_READONLY; else protect = isWrite ? PAGE_WRITECOPY : PAGE_NOACCESS; } DWORD old; VirtualProtect(secAddr, secSize, protect, &old); } // Run TLS callbacks if present if (opt.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size) { auto tls = (IMAGE_TLS_DIRECTORY*)(imageBase + opt.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress); #ifdef _WIN64 PIMAGE_TLS_DIRECTORY64 tls64 = (PIMAGE_TLS_DIRECTORY64)tls; auto callbacks = (PIMAGE_TLS_CALLBACK*)(tls64->AddressOfCallBacks); #else PIMAGE_TLS_DIRECTORY32 tls32 = (PIMAGE_TLS_DIRECTORY32)tls; auto callbacks = (PIMAGE_TLS_CALLBACK*)(tls32->AddressOfCallBacks); #endif if (callbacks) { for (size_t i = 0; callbacks[i]; ++i) { callbacks[i]((PVOID)imageBase, DLL_PROCESS_ATTACH, nullptr); } } } // Call entry point (DllMain) if (opt.AddressOfEntryPoint) { auto entry = (FARPROC)(imageBase + opt.AddressOfEntryPoint); typedef BOOL(WINAPI* DllEntry)(HINSTANCE, DWORD, LPVOID); DllEntry DllMain = (DllEntry)entry; // change protection for entry point region to be executable DWORD oldProt; VirtualProtect((LPVOID)entry, 1, PAGE_EXECUTE_READ, &oldProt); BOOL ok = DllMain((HINSTANCE)imageBase, DLL_PROCESS_ATTACH, nullptr); if (!ok) { VirtualFree(imageBase, 0, MEM_RELEASE); return nullptr; } } // Flush instruction cache for safety FlushInstructionCache(GetCurrentProcess(), imageBase, imageSize); return (HMODULE)imageBase; } HMODULE manual_map_from_file(const char* path) { auto buf = read_file_bytes(path); if (buf.empty()) return nullptr; return manual_map_from_memory((const uint8_t*)buf.data(), buf.size()); } int main(int argc, char** argv) { if (argc < 2) { std::cout << "Usage: " << argv[0] << " \n"; return 1; } std::cout << "Press Enter to load " << argv[1] << " via manual mapping..."; std::cin.get(); HMODULE h = manual_map_from_file(argv[1]); if (h) { std::cout << "Manual map succeeded: base= " << h << "\n"; } else { std::cout << "Manual map failed\n"; } std::cout << "Press Enter to exit..."; std::cin.get(); return 0; }