From 7c6640decadcfb9e8ac608b71ace8b39967189b0 Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie Date: Sun, 11 Sep 2022 15:52:00 +0200 Subject: [PATCH] Relocate the image correctly with DisableAslr --- GleeBug/Debugger.cpp | 113 ++++++++++++++++++++++++++++++++++++++++++- GleeBug/oprintf.h | 5 +- 2 files changed, 114 insertions(+), 4 deletions(-) diff --git a/GleeBug/Debugger.cpp b/GleeBug/Debugger.cpp index e224e19..48f133a 100644 --- a/GleeBug/Debugger.cpp +++ b/GleeBug/Debugger.cpp @@ -193,6 +193,115 @@ retry_no_aslr: } #endif + static bool ProcessRelocations(char* imageCopy, ULONG_PTR imageSize, 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; + + // Nothing to do if relocations are stripped + if(pnth->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) + return true; + + // Nothing to do if there are no relocations + const auto & relocDir = pnth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + if(relocDir.Size == 0 || relocDir.VirtualAddress == 0) + return true; + + // Process the relocations + auto delta = newImageBase - oldImageBase; + auto relocationItr = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)imageCopy + relocDir.VirtualAddress); + auto relocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)relocationItr + relocDir.Size); + + while(relocationItr < relocationEnd && relocationItr->SizeOfBlock > 0) + { + auto count = (relocationItr->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT); + auto address = (ULONG_PTR)imageCopy + relocationItr->VirtualAddress; + auto typeOffset = (PUSHORT)(relocationItr + 1); + + relocationItr = LdrProcessRelocationBlock(address, (ULONG)count, typeOffset, delta); + if(relocationItr == nullptr) + return false; + } + return true; + } + + static bool RelocateImage(HANDLE hProcess, PVOID imageBase, SIZE_T imageSize) + { + constexpr auto pageSize = 0x1000; + std::vector writeback(imageSize / pageSize); + // allocate a local copy of the mapped image + auto imageCopy = (char*)VirtualAlloc(0, imageSize, MEM_COMMIT, PAGE_READWRITE); + if(imageCopy == nullptr) + return false; + + // read all the pages + for(size_t i = 0; i < writeback.size(); i++) + { + auto offset = i * pageSize; + SIZE_T read = 0; + if(NT_SUCCESS(NtReadVirtualMemory(hProcess, (char*)imageBase + offset, imageCopy + offset, pageSize, &read))) + writeback[i] = true; + } + + // perform the actual relocations + ULONG_PTR oldImageBase = 0; + auto success = ProcessRelocations(imageCopy, imageSize, (ULONG_PTR)imageBase, oldImageBase); + + // write back the pages + auto memWrite = [hProcess](PVOID ptr, LPCVOID data, SIZE_T size) + { + // Make the page writable + ULONG oldProtect = 0; + if(NT_SUCCESS(NtProtectVirtualMemory(hProcess, &ptr, &size, PAGE_READWRITE, &oldProtect))) + { + // Write the memory + SIZE_T written = 0; + if(NT_SUCCESS(NtWriteVirtualMemory(hProcess, ptr, data, size, &written))) + { + // Restore the old protection + return NT_SUCCESS(NtProtectVirtualMemory(hProcess, &ptr, &size, oldProtect, &oldProtect)); + } + } + return false; + }; + for(size_t i = 0; i < writeback.size(); i++) + { + if(writeback[i]) + { + auto offset = pageSize * i; + if(!memWrite((char*)imageBase + offset, imageCopy + offset, pageSize)) + success = false; + } + } + + // Create a copy of the header at the original image base + // The kernel uses it in ZwCreateThread to get the stack size for example + if(success) + { + success = false; + auto oldPage = (LPVOID)oldImageBase; + if(VirtualAllocEx(hProcess, oldPage, pageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)) + { + if(memWrite(oldPage, imageCopy, pageSize)) + { + DWORD oldProtect = 0; + if(VirtualProtectEx(hProcess, oldPage, pageSize, PAGE_READONLY, &oldProtect)) + success = true; + } + } + } + + // Free the copy of the image + VirtualFree(imageCopy, imageSize, MEM_DECOMMIT); + + return success; + } + bool Debugger::HollowProcessWithoutASLR(const wchar_t* szFileName, PROCESS_INFORMATION & pi) { bool success = false; @@ -244,7 +353,9 @@ retry_no_aslr: } if(status == STATUS_SUCCESS || status == STATUS_IMAGE_NOT_AT_BASE) { - if(WriteProcessMemory(pi.hProcess, (char*)pebRegister + offsetof(PEB, ImageBaseAddress), &imageBase, sizeof(PVOID), nullptr)) + auto pebOk = WriteProcessMemory(pi.hProcess, (char*)pebRegister + offsetof(PEB, ImageBaseAddress), &imageBase, sizeof(PVOID), nullptr); + auto relocatedOk = RelocateImage(pi.hProcess, imageBase, viewSize); + if(pebOk && relocatedOk) { auto expectedBase = mDebugModuleImageBase == ULONG_PTR(imageBase); mDebugModuleImageBase = ULONG_PTR(imageBase); diff --git a/GleeBug/oprintf.h b/GleeBug/oprintf.h index 0ad8d71..a1e740d 100644 --- a/GleeBug/oprintf.h +++ b/GleeBug/oprintf.h @@ -8,14 +8,13 @@ static inline void oprintf(const char* format, ...) { va_list args; va_start(args, format); - static char dprintf_msg[66000]; - *dprintf_msg = 0; + char dprintf_msg[2048]; vsnprintf_s(dprintf_msg, sizeof(dprintf_msg), format, args); if(GetConsoleWindow() == NULL) AllocConsole(); auto hOut = GetStdHandle(STD_OUTPUT_HANDLE); DWORD w; - WriteFile(hOut, dprintf_msg, strlen(dprintf_msg), &w, nullptr); + WriteFile(hOut, dprintf_msg, (DWORD)strlen(dprintf_msg), &w, nullptr); } static inline void oputs(const char* text)