diff --git a/GleeBug/Debugger.Global.h b/GleeBug/Debugger.Global.h index cd54981..b31c12c 100644 --- a/GleeBug/Debugger.Global.h +++ b/GleeBug/Debugger.Global.h @@ -12,6 +12,14 @@ #define PAGE_SIZE 0x1000 #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 { //forward declarations diff --git a/GleeBug/Debugger.Loop.Exception.cpp b/GleeBug/Debugger.Loop.Exception.cpp index 1a430d5..fb28d39 100644 --- a/GleeBug/Debugger.Loop.Exception.cpp +++ b/GleeBug/Debugger.Loop.Exception.cpp @@ -560,9 +560,11 @@ namespace GleeBug switch(exceptionInfo.ExceptionRecord.ExceptionCode) { case STATUS_BREAKPOINT: + case STATUS_WX86_BREAKPOINT: exceptionBreakpoint(exceptionRecord, firstChance); break; case STATUS_SINGLE_STEP: + case STATUS_WX86_SINGLE_STEP: exceptionSingleStep(exceptionRecord, firstChance); break; case STATUS_GUARD_PAGE_VIOLATION: diff --git a/GleeBug/Debugger.Loop.cpp b/GleeBug/Debugger.Loop.cpp index 3b089e2..74a6be0 100644 --- a/GleeBug/Debugger.Loop.cpp +++ b/GleeBug/Debugger.Loop.cpp @@ -185,7 +185,9 @@ namespace GleeBug unloadDllEvent(mDebugEvent.u.UnloadDll); break; 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 for(auto & itr : SuspendedThreads) @@ -196,6 +198,7 @@ namespace GleeBug } exceptionEvent(mDebugEvent.u.Exception); break; + } case OUTPUT_DEBUG_STRING_EVENT: debugStringEvent(mDebugEvent.u.DebugString); break; diff --git a/GleeBug/Debugger.Process.cpp b/GleeBug/Debugger.Process.cpp index 19e8b45..19ae33e 100644 --- a/GleeBug/Debugger.Process.cpp +++ b/GleeBug/Debugger.Process.cpp @@ -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) { - 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]; if(MemReadSafe(gip, data, sizeof(data))) { ZydisDisassembledInstruction instruction; if(ZYAN_SUCCESS(ZydisDisassembleIntel( - GleeArchValue(ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_MACHINE_MODE_LONG_COMPAT_32), + machineMode, gip, data, sizeof(data), @@ -111,6 +122,7 @@ namespace GleeBug // Check if we're currently stepping on a pushf instruction auto isPushf = false; + auto pointerSize = registers.PointerSize(); { auto gip = registers.Gip(); unsigned char data[16]; @@ -118,7 +130,7 @@ namespace GleeBug { ZydisDisassembledInstruction instruction; if(ZYAN_SUCCESS(ZydisDisassembleIntel( - GleeArchValue(ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_MACHINE_MODE_LONG_COMPAT_32), + GetMachineMode(registers), gip, data, sizeof(data), @@ -141,15 +153,16 @@ namespace GleeBug 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(); - GleeBug::ptr data; - if(MemReadUnsafe(gsp, &data, sizeof(data))) + uint64 data = 0; + if(MemReadUnsafe(gsp, &data, pointerSize)) { - data &= ~(int)Registers::F::Trap; - MemWriteUnsafe(gsp, &data, sizeof(data)); + data &= ~uint64(Registers::F::Trap); + MemWriteUnsafe(gsp, &data, pointerSize); } cbStep(); diff --git a/GleeBug/Debugger.Thread.Registers.cpp b/GleeBug/Debugger.Thread.Registers.cpp index b4d6bb4..e7f4811 100644 --- a/GleeBug/Debugger.Thread.Registers.cpp +++ b/GleeBug/Debugger.Thread.Registers.cpp @@ -106,6 +106,12 @@ namespace GleeBug TrapFlag(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)); mContext.ContextFlags = ContextFlags; if(!!GetThreadContext(hThread, &mContext)) @@ -129,4 +135,18 @@ namespace GleeBug { return &mContext; } -}; \ No newline at end of file + + 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); + } +}; diff --git a/GleeBug/Debugger.Thread.Registers.h b/GleeBug/Debugger.Thread.Registers.h index 4fd81c5..78d8d72 100644 --- a/GleeBug/Debugger.Thread.Registers.h +++ b/GleeBug/Debugger.Thread.Registers.h @@ -159,6 +159,16 @@ namespace GleeBug */ 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: HANDLE hThread; CONTEXT mContext;