Improve the DisableAslr functionality

It will now retry 10 times to get the image to load at the expected base
This commit is contained in:
Duncan Ogilvie 2022-09-10 00:55:19 +02:00
parent b435ee57d3
commit 807951bac6
6 changed files with 202 additions and 158 deletions

View File

@ -35,7 +35,7 @@ namespace GleeBug
// Check if DBG_REPLY_LATER is supported based on Windows version (Windows 10, version 1507 or above)
// https://www.gaijin.at/en/infos/windows-version-numbers
const uint32_t NtBuildNumber = *(uint32_t*)(0x7FFE0000 + 0x260);
if (NtBuildNumber != 0 && NtBuildNumber >= 10240)
if(NtBuildNumber != 0 && NtBuildNumber >= 10240)
{
IsDbgReplyLaterSupported = mSafeStep;
}
@ -71,27 +71,27 @@ namespace GleeBug
}
// Handle safe stepping
if (IsDbgReplyLaterSupported)
if(IsDbgReplyLaterSupported)
{
if (mDebugEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
if(mDebugEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
{
// Check if there is a thread processing a single step
if (ThreadBeingProcessed != 0 && mDebugEvent.dwThreadId != ThreadBeingProcessed)
if(ThreadBeingProcessed != 0 && mDebugEvent.dwThreadId != ThreadBeingProcessed)
{
// Reply to the event later
if (!ContinueDebugEvent(mDebugEvent.dwProcessId, mDebugEvent.dwThreadId, DBG_REPLY_LATER))
if(!ContinueDebugEvent(mDebugEvent.dwProcessId, mDebugEvent.dwThreadId, DBG_REPLY_LATER))
break;
// Wait for the next event
continue;
}
}
else if (mDebugEvent.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT)
else if(mDebugEvent.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT)
{
if (ThreadBeingProcessed != 0 && mDebugEvent.dwThreadId == ThreadBeingProcessed)
if(ThreadBeingProcessed != 0 && mDebugEvent.dwThreadId == ThreadBeingProcessed)
{
// Resume the other threads since the thread being processed is exiting
for (auto& itr : SuspendedThreads)
for(auto & itr : SuspendedThreads)
ResumeThread(itr.second);
SuspendedThreads.clear();
@ -139,10 +139,10 @@ namespace GleeBug
{
case CREATE_PROCESS_DEBUG_EVENT:
// HACK: when hollowing the process the debug event still delivers the original image base
if (mDisableAslr && mDebugModuleImageBase != 0)
if(mDisableAslr && mDebugModuleImageBase != 0)
{
auto startAddress = ULONG_PTR(mDebugEvent.u.CreateProcessInfo.lpStartAddress);
if (startAddress)
if(startAddress)
{
startAddress -= ULONG_PTR(mDebugEvent.u.CreateProcessInfo.lpBaseOfImage);
startAddress += mDebugModuleImageBase;
@ -168,10 +168,10 @@ namespace GleeBug
unloadDllEvent(mDebugEvent.u.UnloadDll);
break;
case EXCEPTION_DEBUG_EVENT:
if (IsDbgReplyLaterSupported && mDebugEvent.u.Exception.ExceptionRecord.ExceptionCode == STATUS_SINGLE_STEP)
if(IsDbgReplyLaterSupported && mDebugEvent.u.Exception.ExceptionRecord.ExceptionCode == STATUS_SINGLE_STEP)
{
// Resume the other threads since we are done processing the single step
for (auto& itr : SuspendedThreads)
for(auto & itr : SuspendedThreads)
ResumeThread(itr.second);
SuspendedThreads.clear();
@ -209,27 +209,27 @@ namespace GleeBug
}
// Handle safe stepping
if (IsDbgReplyLaterSupported && mDebugEvent.dwDebugEventCode != EXIT_THREAD_DEBUG_EVENT)
if(IsDbgReplyLaterSupported && mDebugEvent.dwDebugEventCode != EXIT_THREAD_DEBUG_EVENT)
{
// If TF is set (single step), then suspend all the other threads
if (mThread && mThread->isInternalStepping)
if(mThread && mThread->isInternalStepping)
{
ThreadBeingProcessed = mDebugEvent.dwThreadId;
for (auto& Thread : mProcess->threads)
for(auto & Thread : mProcess->threads)
{
auto dwThreadId = Thread.first;
auto hThread = Thread.second->hThread;
// Do not suspend the current thread
if (ThreadBeingProcessed == dwThreadId)
if(ThreadBeingProcessed == dwThreadId)
continue;
// Check if the thread is already suspended
if (SuspendedThreads.count(dwThreadId) != 0)
if(SuspendedThreads.count(dwThreadId) != 0)
continue;
if (SuspendThread(hThread) != -1)
if(SuspendThread(hThread) != -1)
SuspendedThreads.emplace(dwThreadId, hThread);
}
}

View File

@ -40,18 +40,20 @@ namespace GleeBug
auto creationFlags = DEBUG_PROCESS;
creationFlags |= DEBUG_ONLY_THIS_PROCESS; // TODO: support child process debugging
if (newConsole)
if(newConsole)
creationFlags |= CREATE_NEW_CONSOLE;
if (startSuspended)
if(startSuspended)
creationFlags |= CREATE_SUSPENDED;
if (mDisableAslr)
if(mDisableAslr)
{
creationFlags |= CREATE_SUSPENDED;
// We will attach manually later
creationFlags &= ~(DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS);
}
int retries = 0;
retry_no_aslr:
bool result = !!CreateProcessW(szFileNameCreateProcess,
szCommandLineCreateProcess,
nullptr,
@ -63,13 +65,23 @@ namespace GleeBug
&mMainStartupInfo,
&mMainProcess);
if (result && mDisableAslr)
if(result && mDisableAslr)
{
HollowProcessWithoutASLR(szFileNameCreateProcess, mMainProcess);
DebugActiveProcess_(mMainProcess.dwProcessId);
DebugSetProcessKillOnExit(TRUE);
if (!startSuspended)
ResumeThread(mMainProcess.hThread);
if(!HollowProcessWithoutASLR(szFileNameCreateProcess, mMainProcess))
{
TerminateThread(mMainProcess.hThread, STATUS_CONFLICTING_ADDRESSES);
TerminateProcess(mMainProcess.hProcess, STATUS_CONFLICTING_ADDRESSES);
if(retries++ < 10)
goto retry_no_aslr;
result = false;
}
else
{
DebugActiveProcess_(mMainProcess.dwProcessId);
DebugSetProcessKillOnExit(TRUE);
if(!startSuspended)
ResumeThread(mMainProcess.hThread);
}
}
delete[] szCreateWithCmdLine;
@ -127,26 +139,26 @@ namespace GleeBug
mDetach = false;
}
static bool GetPeData(HANDLE hFile, ULONG_PTR& imageBase, ULONG_PTR& entryPoint)
static bool GetPeData(HANDLE hFile, ULONG_PTR & imageBase, ULONG_PTR & entryPoint)
{
IMAGE_DOS_HEADER idh;
DWORD read = 0;
if (!ReadFile(hFile, &idh, sizeof(idh), &read, nullptr))
if(!ReadFile(hFile, &idh, sizeof(idh), &read, nullptr))
return false;
if (idh.e_magic != IMAGE_DOS_SIGNATURE)
if(idh.e_magic != IMAGE_DOS_SIGNATURE)
return false;
if (!SetFilePointer(hFile, idh.e_lfanew, nullptr, FILE_BEGIN))
if(!SetFilePointer(hFile, idh.e_lfanew, nullptr, FILE_BEGIN))
return false;
IMAGE_NT_HEADERS64 inth;
if (!ReadFile(hFile, &inth, sizeof(inth), &read, nullptr))
if(!ReadFile(hFile, &inth, sizeof(inth), &read, nullptr))
return false;
if (inth.Signature != IMAGE_NT_SIGNATURE)
if(inth.Signature != IMAGE_NT_SIGNATURE)
return false;
switch (inth.FileHeader.Machine)
switch(inth.FileHeader.Machine)
{
case IMAGE_FILE_MACHINE_AMD64:
{
imageBase = inth.OptionalHeader.ImageBase;
imageBase = (ULONG_PTR)inth.OptionalHeader.ImageBase;
entryPoint = inth.OptionalHeader.AddressOfEntryPoint;
}
break;
@ -165,55 +177,87 @@ namespace GleeBug
return true;
}
bool Debugger::HollowProcessWithoutASLR(const wchar_t* szFileName, PROCESS_INFORMATION& pi)
#ifndef _WIN64
static bool isThisProcessWow64()
{
typedef BOOL(WINAPI * tIsWow64Process)(HANDLE hProcess, PBOOL Wow64Process);
BOOL bIsWow64 = FALSE;
static auto fnIsWow64Process = (tIsWow64Process)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
if(fnIsWow64Process)
{
fnIsWow64Process(GetCurrentProcess(), &bIsWow64);
}
return (bIsWow64 != FALSE);
}
#endif
bool Debugger::HollowProcessWithoutASLR(const wchar_t* szFileName, PROCESS_INFORMATION & pi)
{
bool success = false;
auto hFile = CreateFileW(szFileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (hFile != INVALID_HANDLE_VALUE)
if(hFile != INVALID_HANDLE_VALUE)
{
// Retrieve image base and entry point
ULONG_PTR debugModuleEntryPoint = 0;
if (GetPeData(hFile, mDebugModuleImageBase, debugModuleEntryPoint))
if(GetPeData(hFile, mDebugModuleImageBase, debugModuleEntryPoint))
{
SetFilePointer(hFile, 0, nullptr, FILE_BEGIN);
auto hMapping = CreateFileMappingW(hFile, nullptr, SEC_IMAGE | PAGE_READONLY, 0, 0, nullptr);
if (hMapping)
if(hMapping)
{
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_ALL;
if (GetThreadContext(pi.hThread, &ctx))
if(GetThreadContext(pi.hThread, &ctx))
{
PVOID imageBase;
// TODO: support wow64 processes
#ifdef _WIN64
auto& pebRegister = ctx.Rdx;
auto& entryPointRegister = ctx.Rcx;
auto & pebRegister = ctx.Rdx;
auto & entryPointRegister = ctx.Rcx;
#else
auto& pebRegister = ctx.Ebx;
auto& entryPointRegister = ctx.Eax;
auto & pebRegister = ctx.Ebx;
auto & entryPointRegister = ctx.Eax;
#endif // _WIN64
if (ReadProcessMemory(pi.hProcess, (char*)pebRegister + offsetof(PEB, ImageBaseAddress), &imageBase, sizeof(PVOID), nullptr))
if(ReadProcessMemory(pi.hProcess, (char*)pebRegister + offsetof(PEB, ImageBaseAddress), &imageBase, sizeof(PVOID), nullptr))
{
auto status = NtUnmapViewOfSection(pi.hProcess, imageBase);
if (status == STATUS_SUCCESS)
if(ULONG_PTR(imageBase) == mDebugModuleImageBase)
{
SIZE_T viewSize = 0;
imageBase = PVOID(mDebugModuleImageBase);
status = NtMapViewOfSection(hMapping, pi.hProcess, &imageBase, 0, 0, nullptr, &viewSize, ViewUnmap, 0, PAGE_READONLY);
if (status == STATUS_SUCCESS || status == STATUS_IMAGE_NOT_AT_BASE)
// Already at the right base
success = true;
}
else
{
auto status = NtUnmapViewOfSection(pi.hProcess, imageBase);
if(status == STATUS_SUCCESS)
{
if (WriteProcessMemory(pi.hProcess, (char*)pebRegister + offsetof(PEB, ImageBaseAddress), &imageBase, sizeof(PVOID), nullptr))
SIZE_T viewSize = 0;
imageBase = PVOID(mDebugModuleImageBase);
status = NtMapViewOfSection(hMapping, pi.hProcess, &imageBase, 0, 0, nullptr, &viewSize, ViewUnmap, 0, PAGE_READONLY);
if(status == STATUS_CONFLICTING_ADDRESSES)
{
entryPointRegister = mDebugModuleImageBase + debugModuleEntryPoint;
if (SetThreadContext(pi.hThread, &ctx))
// Remap in a random location (otherwise the process will crash)
imageBase = 0;
status = NtMapViewOfSection(hMapping, pi.hProcess, &imageBase, 0, 0, nullptr, &viewSize, ViewUnmap, 0, PAGE_READONLY);
}
if(status == STATUS_SUCCESS || status == STATUS_IMAGE_NOT_AT_BASE)
{
if(WriteProcessMemory(pi.hProcess, (char*)pebRegister + offsetof(PEB, ImageBaseAddress), &imageBase, sizeof(PVOID), nullptr))
{
success = true;
auto expectedBase = mDebugModuleImageBase == ULONG_PTR(imageBase);
mDebugModuleImageBase = ULONG_PTR(imageBase);
entryPointRegister = mDebugModuleImageBase + debugModuleEntryPoint;
if(SetThreadContext(pi.hThread, &ctx))
{
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;
// 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
}
}
}
}
@ -228,7 +272,7 @@ namespace GleeBug
CloseHandle(hFile);
}
if (!success)
if(!success)
{
mDebugModuleImageBase = 0;
}

View File

@ -306,7 +306,7 @@ namespace GleeBug
Thread* mThread = nullptr;
private:
bool HollowProcessWithoutASLR(const wchar_t* szFileName, PROCESS_INFORMATION& pi);
bool HollowProcessWithoutASLR(const wchar_t* szFileName, PROCESS_INFORMATION & pi);
ULONG_PTR mDebugModuleImageBase = 0;
};
};