Add CS-aware WoW64 debugging support

This commit is contained in:
Duncan Ogilvie 2026-08-02 15:27:20 +02:00
parent 6d37c95192
commit dbb4aa83b9
6 changed files with 67 additions and 11 deletions

View File

@ -12,6 +12,14 @@
#define PAGE_SIZE 0x1000 #define PAGE_SIZE 0x1000
#endif // PAGE_SIZE #endif // PAGE_SIZE
#ifndef STATUS_WX86_SINGLE_STEP
#define STATUS_WX86_SINGLE_STEP ((DWORD)0x4000001EL)
#endif // STATUS_WX86_SINGLE_STEP
#ifndef STATUS_WX86_BREAKPOINT
#define STATUS_WX86_BREAKPOINT ((DWORD)0x4000001FL)
#endif // STATUS_WX86_BREAKPOINT
namespace GleeBug namespace GleeBug
{ {
//forward declarations //forward declarations

View File

@ -560,9 +560,11 @@ namespace GleeBug
switch(exceptionInfo.ExceptionRecord.ExceptionCode) switch(exceptionInfo.ExceptionRecord.ExceptionCode)
{ {
case STATUS_BREAKPOINT: case STATUS_BREAKPOINT:
case STATUS_WX86_BREAKPOINT:
exceptionBreakpoint(exceptionRecord, firstChance); exceptionBreakpoint(exceptionRecord, firstChance);
break; break;
case STATUS_SINGLE_STEP: case STATUS_SINGLE_STEP:
case STATUS_WX86_SINGLE_STEP:
exceptionSingleStep(exceptionRecord, firstChance); exceptionSingleStep(exceptionRecord, firstChance);
break; break;
case STATUS_GUARD_PAGE_VIOLATION: case STATUS_GUARD_PAGE_VIOLATION:

View File

@ -185,7 +185,9 @@ 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) {
auto exceptionCode = mDebugEvent.u.Exception.ExceptionRecord.ExceptionCode;
if(IsDbgReplyLaterSupported && (exceptionCode == STATUS_SINGLE_STEP || exceptionCode == STATUS_WX86_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)
@ -196,6 +198,7 @@ namespace GleeBug
} }
exceptionEvent(mDebugEvent.u.Exception); exceptionEvent(mDebugEvent.u.Exception);
break; break;
}
case OUTPUT_DEBUG_STRING_EVENT: case OUTPUT_DEBUG_STRING_EVENT:
debugStringEvent(mDebugEvent.u.DebugString); debugStringEvent(mDebugEvent.u.DebugString);
break; break;

View File

@ -62,15 +62,26 @@ namespace GleeBug
} }
} }
static ZydisMachineMode GetMachineMode(const Registers & registers)
{
#ifdef _WIN64
return registers.Is32BitMode() ? ZYDIS_MACHINE_MODE_LONG_COMPAT_32 : ZYDIS_MACHINE_MODE_LONG_64;
#else
return ZYDIS_MACHINE_MODE_LEGACY_32;
#endif // _WIN64
}
void Process::StepOver(const StepCallback & cbStep) void Process::StepOver(const StepCallback & cbStep)
{ {
auto gip = Registers(thread->hThread, CONTEXT_CONTROL).Gip(); Registers registers(thread->hThread, CONTEXT_CONTROL);
auto gip = registers.Gip();
auto machineMode = GetMachineMode(registers);
unsigned char data[16]; unsigned char data[16];
if(MemReadSafe(gip, data, sizeof(data))) if(MemReadSafe(gip, data, sizeof(data)))
{ {
ZydisDisassembledInstruction instruction; ZydisDisassembledInstruction instruction;
if(ZYAN_SUCCESS(ZydisDisassembleIntel( if(ZYAN_SUCCESS(ZydisDisassembleIntel(
GleeArchValue(ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_MACHINE_MODE_LONG_COMPAT_32), machineMode,
gip, gip,
data, data,
sizeof(data), sizeof(data),
@ -111,6 +122,7 @@ namespace GleeBug
// Check if we're currently stepping on a pushf instruction // Check if we're currently stepping on a pushf instruction
auto isPushf = false; auto isPushf = false;
auto pointerSize = registers.PointerSize();
{ {
auto gip = registers.Gip(); auto gip = registers.Gip();
unsigned char data[16]; unsigned char data[16];
@ -118,7 +130,7 @@ namespace GleeBug
{ {
ZydisDisassembledInstruction instruction; ZydisDisassembledInstruction instruction;
if(ZYAN_SUCCESS(ZydisDisassembleIntel( if(ZYAN_SUCCESS(ZydisDisassembleIntel(
GleeArchValue(ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_MACHINE_MODE_LONG_COMPAT_32), GetMachineMode(registers),
gip, gip,
data, data,
sizeof(data), sizeof(data),
@ -141,15 +153,16 @@ namespace GleeBug
if(isPushf) if(isPushf)
{ {
thread->cbInternalStep = [this, cbStep]() thread->cbInternalStep = [this, cbStep, pointerSize]()
{ {
// Remove the trap flag from the stack // Remove the trap flag from the stack using the execution
// mode's pointer width (PUSHFD is four bytes under WoW64).
auto gsp = Registers(this->thread->hThread).Gsp(); auto gsp = Registers(this->thread->hThread).Gsp();
GleeBug::ptr data; uint64 data = 0;
if(MemReadUnsafe(gsp, &data, sizeof(data))) if(MemReadUnsafe(gsp, &data, pointerSize))
{ {
data &= ~(int)Registers::F::Trap; data &= ~uint64(Registers::F::Trap);
MemWriteUnsafe(gsp, &data, sizeof(data)); MemWriteUnsafe(gsp, &data, pointerSize);
} }
cbStep(); cbStep();

View File

@ -106,6 +106,12 @@ namespace GleeBug
TrapFlag(this), TrapFlag(this),
ResumeFlag(this) ResumeFlag(this)
{ {
#ifdef _WIN64
// Preserve hardware-breakpoint state across control-context updates,
// including transitions between WoW64 compatibility and native mode.
if((ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
ContextFlags |= CONTEXT_DEBUG_REGISTERS;
#endif // _WIN64
memset(&mContext, 0, sizeof(CONTEXT)); memset(&mContext, 0, sizeof(CONTEXT));
mContext.ContextFlags = ContextFlags; mContext.ContextFlags = ContextFlags;
if(!!GetThreadContext(hThread, &mContext)) if(!!GetThreadContext(hThread, &mContext))
@ -129,4 +135,18 @@ namespace GleeBug
{ {
return &mContext; return &mContext;
} }
};
bool Registers::Is32BitMode() const
{
#ifdef _WIN64
return mContext.SegCs == 0x23;
#else
return true;
#endif // _WIN64
}
size_t Registers::PointerSize() const
{
return Is32BitMode() ? sizeof(uint32) : sizeof(ptr);
}
};

View File

@ -159,6 +159,16 @@ namespace GleeBug
*/ */
PCONTEXT GetContext(); PCONTEXT GetContext();
/**
\brief Returns true when the thread is currently executing 32-bit code.
*/
bool Is32BitMode() const;
/**
\brief Gets the pointer size for the thread's current execution mode.
*/
size_t PointerSize() const;
private: private:
HANDLE hThread; HANDLE hThread;
CONTEXT mContext; CONTEXT mContext;