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

View File

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

View File

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

View File

@ -1,42 +1,42 @@
#include "stringutils.h" #include "stringutils.h"
#include <Windows.h> #include <Windows.h>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
// Functions from x64dbg project: https://github.com/x64dbg/x64dbg // Functions from x64dbg project: https://github.com/x64dbg/x64dbg
//Conversion functions taken from: http://www.nubaria.com/en/blog/?p=289 //Conversion functions taken from: http://www.nubaria.com/en/blog/?p=289
String Utf16ToUtf8(const WString & wstr) String Utf16ToUtf8(const WString & wstr)
{ {
String convertedString; String convertedString;
int requiredSize = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, 0, 0, 0, 0); int requiredSize = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, 0, 0, 0, 0);
if(requiredSize > 0) if(requiredSize > 0)
{ {
std::vector<char> buffer(requiredSize); std::vector<char> buffer(requiredSize);
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &buffer[0], requiredSize, 0, 0); WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &buffer[0], requiredSize, 0, 0);
convertedString.assign(buffer.begin(), buffer.end() - 1); convertedString.assign(buffer.begin(), buffer.end() - 1);
} }
return convertedString; return convertedString;
} }
String Utf16ToUtf8(const wchar_t* wstr) String Utf16ToUtf8(const wchar_t* wstr)
{ {
return Utf16ToUtf8(wstr ? WString(wstr) : WString()); return Utf16ToUtf8(wstr ? WString(wstr) : WString());
} }
WString Utf8ToUtf16(const String & str) WString Utf8ToUtf16(const String & str)
{ {
WString convertedString; WString convertedString;
int requiredSize = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, 0, 0); int requiredSize = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, 0, 0);
if(requiredSize > 0) if(requiredSize > 0)
{ {
std::vector<wchar_t> buffer(requiredSize); std::vector<wchar_t> buffer(requiredSize);
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &buffer[0], requiredSize); MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &buffer[0], requiredSize);
convertedString.assign(buffer.begin(), buffer.end() - 1); convertedString.assign(buffer.begin(), buffer.end() - 1);
} }
return convertedString; return convertedString;
} }
WString Utf8ToUtf16(const char* str) WString Utf8ToUtf16(const char* str)
{ {
return Utf8ToUtf16(str ? String(str) : String()); return Utf8ToUtf16(str ? String(str) : String());
} }

View File

@ -1,16 +1,16 @@
#ifndef _STRINGUTILS_H #ifndef _STRINGUTILS_H
#define _STRINGUTILS_H #define _STRINGUTILS_H
#include <string> #include <string>
#include <vector> #include <vector>
typedef std::string String; typedef std::string String;
typedef std::wstring WString; typedef std::wstring WString;
String Utf16ToUtf8(const WString & wstr); String Utf16ToUtf8(const WString & wstr);
String Utf16ToUtf8(const wchar_t* wstr); String Utf16ToUtf8(const wchar_t* wstr);
WString Utf8ToUtf16(const String & str); WString Utf8ToUtf16(const String & str);
WString Utf8ToUtf16(const char* str); WString Utf8ToUtf16(const char* str);
#endif //_STRINGUTILS_H #endif //_STRINGUTILS_H

View File

@ -1,41 +1,41 @@
#ifndef ZYDIS_EXPORT_H #ifndef ZYDIS_EXPORT_H
#define ZYDIS_EXPORT_H #define ZYDIS_EXPORT_H
#ifdef ZYDIS_STATIC_DEFINE #ifdef ZYDIS_STATIC_DEFINE
# define ZYDIS_EXPORT # define ZYDIS_EXPORT
# define ZYDIS_NO_EXPORT # define ZYDIS_NO_EXPORT
#else #else
# ifndef ZYDIS_EXPORT # ifndef ZYDIS_EXPORT
# ifdef Zydis_EXPORTS # ifdef Zydis_EXPORTS
/* We are building this library */ /* We are building this library */
# define ZYDIS_EXPORT # define ZYDIS_EXPORT
# else # else
/* We are using this library */ /* We are using this library */
# define ZYDIS_EXPORT # define ZYDIS_EXPORT
# endif # endif
# endif # endif
# ifndef ZYDIS_NO_EXPORT # ifndef ZYDIS_NO_EXPORT
# define ZYDIS_NO_EXPORT # define ZYDIS_NO_EXPORT
# endif # endif
#endif #endif
#ifndef ZYDIS_DEPRECATED #ifndef ZYDIS_DEPRECATED
# define ZYDIS_DEPRECATED __declspec(deprecated) # define ZYDIS_DEPRECATED __declspec(deprecated)
#endif #endif
#ifndef ZYDIS_DEPRECATED_EXPORT #ifndef ZYDIS_DEPRECATED_EXPORT
# define ZYDIS_DEPRECATED_EXPORT ZYDIS_EXPORT ZYDIS_DEPRECATED # define ZYDIS_DEPRECATED_EXPORT ZYDIS_EXPORT ZYDIS_DEPRECATED
#endif #endif
#ifndef ZYDIS_DEPRECATED_NO_EXPORT #ifndef ZYDIS_DEPRECATED_NO_EXPORT
# define ZYDIS_DEPRECATED_NO_EXPORT ZYDIS_NO_EXPORT ZYDIS_DEPRECATED # define ZYDIS_DEPRECATED_NO_EXPORT ZYDIS_NO_EXPORT ZYDIS_DEPRECATED
#endif #endif
#if 0 /* DEFINE_NO_DEPRECATED */ #if 0 /* DEFINE_NO_DEPRECATED */
# ifndef ZYDIS_NO_DEPRECATED # ifndef ZYDIS_NO_DEPRECATED
# define ZYDIS_NO_DEPRECATED # define ZYDIS_NO_DEPRECATED
# endif # endif
#endif #endif
#endif #endif