From 69ed2e093cb6f126bf37ad647db8b0b8965ad9ef Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Sun, 2 Aug 2026 13:12:47 +0200 Subject: [PATCH] Support disabling ASLR for WoW64 targets --- TitanEngine/TitanEngine.Debugger.cpp | 129 ++++++++++++++++++++------- 1 file changed, 97 insertions(+), 32 deletions(-) diff --git a/TitanEngine/TitanEngine.Debugger.cpp b/TitanEngine/TitanEngine.Debugger.cpp index be18b2f..c39e2ac 100644 --- a/TitanEngine/TitanEngine.Debugger.cpp +++ b/TitanEngine/TitanEngine.Debugger.cpp @@ -43,15 +43,12 @@ __declspec(dllexport) void* TITCALL InitDebug(char* szFileName, char* szCommandL } } -static bool ProcessRelocations(char* imageCopy, ULONG_PTR imageSize, ULONG_PTR newImageBase, ULONG_PTR & oldImageBase) +template +static bool ProcessRelocationsForArchitecture(char* imageCopy, NtHeaders* pnth, ULONG_PTR newImageBase, ULONG_PTR & oldImageBase) { - auto pnth = RtlImageNtHeader(imageCopy); - if(pnth == nullptr) - return false; - - // Put the new base in the header - oldImageBase = pnth->OptionalHeader.ImageBase; - pnth->OptionalHeader.ImageBase = newImageBase; + // Put the new base in the header using the image's pointer width. + oldImageBase = ULONG_PTR(pnth->OptionalHeader.ImageBase); + pnth->OptionalHeader.ImageBase = decltype(pnth->OptionalHeader.ImageBase)(newImageBase); // Nothing to do if relocations are stripped if(pnth->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) @@ -63,7 +60,7 @@ static bool ProcessRelocations(char* imageCopy, ULONG_PTR imageSize, ULONG_PTR n return true; // Process the relocations - auto delta = newImageBase - oldImageBase; + auto delta = LONG_PTR(newImageBase) - LONG_PTR(oldImageBase); auto relocationItr = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)imageCopy + relocDir.VirtualAddress); auto relocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)relocationItr + relocDir.Size); @@ -80,6 +77,20 @@ static bool ProcessRelocations(char* imageCopy, ULONG_PTR imageSize, ULONG_PTR n return true; } +static bool ProcessRelocations(char* imageCopy, ULONG_PTR newImageBase, ULONG_PTR & oldImageBase) +{ + auto pnth = RtlImageNtHeader(imageCopy); + if(pnth == nullptr) + return false; + + auto magic = ((PIMAGE_NT_HEADERS32)pnth)->OptionalHeader.Magic; + if(magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + return ProcessRelocationsForArchitecture(imageCopy, (PIMAGE_NT_HEADERS32)pnth, newImageBase, oldImageBase); + if(magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + return ProcessRelocationsForArchitecture(imageCopy, (PIMAGE_NT_HEADERS64)pnth, newImageBase, oldImageBase); + return false; +} + static bool RelocateImage(HANDLE hProcess, PVOID imageBase, SIZE_T imageSize) { constexpr auto pageSize = 0x1000; @@ -100,7 +111,7 @@ static bool RelocateImage(HANDLE hProcess, PVOID imageBase, SIZE_T imageSize) // perform the actual relocations ULONG_PTR oldImageBase = 0; - auto success = ProcessRelocations(imageCopy, imageSize, (ULONG_PTR)imageBase, oldImageBase); + auto success = ProcessRelocations(imageCopy, (ULONG_PTR)imageBase, oldImageBase); // write back the pages auto memWrite = [hProcess](PVOID ptr, LPCVOID data, SIZE_T size) @@ -165,28 +176,45 @@ static bool HollowProcessWithoutASLR(const wchar_t* szFileName, PROCESS_INFORMAT auto hMapping = CreateFileMappingW(hFile, nullptr, SEC_IMAGE | PAGE_READONLY, 0, 0, nullptr); if(hMapping) { - CONTEXT ctx; + CONTEXT ctx = {}; ctx.ContextFlags = CONTEXT_ALL; if(GetThreadContext(pi.hThread, &ctx)) { - PVOID imageBase; - // TODO: support wow64 processes + bool isWow64 = false; + ULONG_PTR pebAddress = 0; + SIZE_T imageBaseOffset = 0; + SIZE_T imageBaseSize = 0; #ifdef _WIN64 - auto & pebRegister = ctx.Rdx; - auto & entryPointRegister = ctx.Rcx; -#else - auto & pebRegister = ctx.Ebx; - auto & entryPointRegister = ctx.Eax; -#endif // _WIN64 - if(ReadProcessMemory(pi.hProcess, (char*)pebRegister + offsetof(PEB, ImageBaseAddress), &imageBase, sizeof(PVOID), nullptr)) + ULONG returnLength = 0; + if(NT_SUCCESS(NtQueryInformationProcess(pi.hProcess, ProcessWow64Information, &pebAddress, sizeof(pebAddress), &returnLength)) && pebAddress != 0) { - if(ULONG_PTR(imageBase) == DebugModuleImageBase) + isWow64 = true; + imageBaseOffset = offsetof(PEB32, ImageBaseAddress); + imageBaseSize = sizeof(DWORD); + } + else + { + pebAddress = ctx.Rdx; + imageBaseOffset = offsetof(PEB64, ImageBaseAddress); + imageBaseSize = sizeof(DWORD64); + } +#else + pebAddress = ctx.Ebx; + imageBaseOffset = offsetof(PEB32, ImageBaseAddress); + imageBaseSize = sizeof(DWORD); +#endif // _WIN64 + + ULONG_PTR imageBaseValue = 0; + if(ReadProcessMemory(pi.hProcess, (char*)pebAddress + imageBaseOffset, &imageBaseValue, imageBaseSize, nullptr)) + { + if(imageBaseValue == DebugModuleImageBase) { // Already at the right base success = true; } else { + auto imageBase = PVOID(imageBaseValue); auto status = NtUnmapViewOfSection(pi.hProcess, imageBase); if(status == STATUS_SUCCESS) { @@ -201,22 +229,59 @@ static bool HollowProcessWithoutASLR(const wchar_t* szFileName, PROCESS_INFORMAT } if(status == STATUS_SUCCESS || status == STATUS_IMAGE_NOT_AT_BASE) { - auto pebOk = WriteProcessMemory(pi.hProcess, (char*)pebRegister + offsetof(PEB, ImageBaseAddress), &imageBase, sizeof(PVOID), nullptr); + imageBaseValue = ULONG_PTR(imageBase); + auto pebOk = WriteProcessMemory(pi.hProcess, (char*)pebAddress + imageBaseOffset, &imageBaseValue, imageBaseSize, nullptr); +#ifdef _WIN64 + if(pebOk && isWow64) + { + PROCESS_BASIC_INFORMATION processInfo = {}; + if(NT_SUCCESS(NtQueryInformationProcess(pi.hProcess, ProcessBasicInformation, &processInfo, sizeof(processInfo), &returnLength))) + { + DWORD64 imageBase64 = imageBaseValue; + pebOk = WriteProcessMemory(pi.hProcess, (char*)processInfo.PebBaseAddress + offsetof(PEB64, ImageBaseAddress), &imageBase64, sizeof(imageBase64), nullptr); + } + else + { + pebOk = false; + } + } +#else + if(pebOk && IsThisProcessWow64()) + { + auto peb64 = GetPEBLocation64(pi.hProcess); + DWORD64 imageBase64 = imageBaseValue; + pebOk = peb64 != nullptr && WriteProcessMemory(pi.hProcess, (char*)peb64 + offsetof(PEB64, ImageBaseAddress), &imageBase64, sizeof(imageBase64), nullptr); + } +#endif // _WIN64 auto relocatedOk = RelocateImage(pi.hProcess, imageBase, viewSize); if(pebOk && relocatedOk) { - auto expectedBase = DebugModuleImageBase == ULONG_PTR(imageBase); - DebugModuleImageBase = ULONG_PTR(imageBase); - entryPointRegister = DebugModuleImageBase + DebugModuleEntryPoint; - if(SetThreadContext(pi.hThread, &ctx)) + auto expectedBase = DebugModuleImageBase == imageBaseValue; + DebugModuleImageBase = imageBaseValue; + auto entryPoint = DebugModuleImageBase + DebugModuleEntryPoint; + bool contextOk = false; +#ifdef _WIN64 + if(isWow64) { - success = expectedBase; -#ifndef _WIN64 - // For Wow64 processes, also adjust the 64-bit PEB - if(IsThisProcessWow64() && !WriteProcessMemory(pi.hProcess, (char*)pebRegister - 0x1000 + 0x10, &imageBase, sizeof(PVOID), nullptr)) - success = false; -#endif // _WIN64 + WOW64_CONTEXT ctx32 = {}; + ctx32.ContextFlags = WOW64_CONTEXT_INTEGER; + if(Wow64GetThreadContext(pi.hThread, &ctx32)) + { + ctx32.Eax = DWORD(entryPoint); + contextOk = Wow64SetThreadContext(pi.hThread, &ctx32) != FALSE; + } } + else + { + ctx.Rcx = entryPoint; + contextOk = SetThreadContext(pi.hThread, &ctx) != FALSE; + } +#else + ctx.Eax = DWORD(entryPoint); + contextOk = SetThreadContext(pi.hThread, &ctx) != FALSE; +#endif // _WIN64 + if(contextOk) + success = expectedBase; } } }