Reputation: 61
I made a very simple debugger in C++ and it seems to work alright except if I call WaitForDebugEvent
with any value for dwMilliseconds
besides INFINITE
it doesn't suspend the debugee immediately.
I have an if
statement that checks whether the exception address matches the address of the breakpoint I set if(ExceptionDebugInfo.ExceptionRecord.ExceptionAddress == lpBreakpoint)
.
Decrements eip
(wow64cntxt.Eip --;
), replaces the breakpoint(int3
) byte with the original instruction(WriteProcessMemory
), flushes the instruction cache(FlushInstructionCache
), and then sets eip
to point to the breakpoint I replaced with the original instruction(Wow64SetThreadContext
).
Then it returns to the main debug loop(break
) and continues debugging(ContinueDebugEvent
).
case EXCEPTION_BREAKPOINT:
{
WOW64_CONTEXT wow64cntxt = {0};
wow64cntxt.ContextFlags = WOW64_CONTEXT_ALL;
if(!Wow64GetThreadContext(hThread, &wow64cntxt))
{
printf("Error getting thread context: %lu\n", GetLastError());
}
//lpFunction is the address of a mov instruction I set a breakpoint on
if(excDbgInfo.ExceptionRecord.ExceptionAddress == lpBreakpoint)
{
printf("EIP-Before: 0x%X\n", wow64cntxt.Eip);
//Decrement eip value to point back to the opcode I wrote over with int3
wow64cntxt.Eip --;
printf("EIP-After: 0x%X\n", wow64cntxt.Eip);
//original opcode I replaced with int3(0xCC)
instr = 0x89;
//replace the breakpoint with the original instruction
if(!WriteProcessMemory(hProcess, lpBreakpoint, &instr, sizeof(CHAR), NULL))
{
printf("Error reversing breakpoint: %lu\n", GetLastError());
}
//Flush the instruction cache
FlushInstructionCache(hProcess, lpBreakpoint, 1);
//Set eip to previous instruction
if(!Wow64SetThreadContext(hThread, &wow64cntxt))
{
printf("Error setting thread context: %lu\n", GetLastError());
}
}
system("pause");
//Return to main debug loop, ContinueDebugEvent...
break;
}
If I use anything other than INFINITE
with WaitForDebugEvent
then eip
is set to an address that executes some time after the breakpoint I set.
The problem is that if I don't use WaitForDebugEvent
with INFINITE
then eip
has already gone past the breakpoint when the debugger catches the exception. Even if I have WaitForDebugEvent
wait for 0 milliseconds, return immediately, the debugee still runs past the breakpoint.
This then results in an access violation, I'm guessing because the other half of the instruction I replaced with the breakpoint becomes a new opcode that modifies memory it isn't allowed to.
Here's my main debug loop:
while(1)
{
WaitForDebugEvent(&dbgEvent, INFINITE);
ProcessDebugEvent(&dbgEvent);
ContinueDebugEvent(dbgEvent.dwProcessId, dbgEvent.dwThreadId, DBG_CONTINUE);
}
Any information, insight, tips, explanations, etc would be greatly appreciated. Thanks.
Upvotes: 2
Views: 363
Reputation: 597550
The dwMilliseconds
parameter tells WaitForDebugEvent()
how long to wait for a debug event to arrive:
dwMilliseconds [in]
The number of milliseconds to wait for a debugging event. If this parameter is zero, the function tests for a debugging event and returns immediately. If the parameter is INFINITE, the function does not return until a debugging event has occurred
You need to check the return value of WaitForDebugEvent()
to make sure you actually have a real debug event that needs processing:
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call
GetLastError
.
For example:
while (1)
{
if (WaitForDebugEvent(&dbgEvent, AnyValueHere)) // <--
{
ProcessDebugEvent(&dbgEvent);
ContinueDebugEvent(dbgEvent.dwProcessId, dbgEvent.dwThreadId, DBG_CONTINUE);
}
...
}
That being said, the dwMilliseconds
parameter has no effect on how long the debuggee waits when a breakpoint is hit. When the breakpoint is hit, the debugee stops immediately, and your debugger is notified. This is clearly stated in the documentation:
When the system notifies the debugger of a debugging event, it also suspends all threads in the affected process. The threads do not resume execution until the debugger continues the debugging event by using
ContinueDebugEvent
.
So chances are, ProcessDebugEvent()
is simply not handling the breakpoint correctly, and then the debuggee is woken up only when you call ContinueDebugEvent()
after done processing.
Upvotes: 2