Log in

View Full Version : How do debuggers restore the breakpoint?


Wayne
January 12th, 2003, 20:49
Consider the following code ...
<pre>50 PUSH EAX
51 PUSH ECX
56 PUSH ESI</pre>
When a debugger sets a breakpoint on the first instruction, it replaces 50 with CC, and the debugger when it gets there then breaks at that instruction. When the user presses Step Into to go to the next instruction (api ContinueDebugEvent), it replaces CC with the original instruction - 50 (push eax). However, wouldnt the call to ContinueDebugEvent immediately go to the next instruction (51 PUSH ECX) and skip over the first instruction? Because if it didnt do that, it seems the debugger would get stuck in a loop? But if it does skip over the instruction, then wouldn't I need to decrement the EIP register?

Any help would be much appreciated!

focht
January 12th, 2003, 23:18
Greets,

well the break event (EXCEPTION_BREAKPOINT) reports an ExceptionAddress.
This is the return address where the exception occurred.
Restore the original opcode with WriteProcessMemory(). Then you just contine with EXCEPTION_CONTINUE_EXECUTION (OS continues just from where execption occurred - it executes the real instruction).

If you want to keep existing breakpoints you need to do more:

Set the trace flag and SetThreadContext() to continue to execute from last exception.
Wait for the incoming trace exception event.
Restore the original breakpoint (WriteProcessMemory) and disable tracing by resetting the trace flag (again using SetThreadContext()).


Regards,

Anastasius Focht

Wayne
January 12th, 2003, 23:25
Thankyou very much! thats an excellent description of how it works
Ive read a lot on the subject over the last few days, including MSDN tips, but nothing really hit on the subject of 'breakpoint restoration' as well as your description does
Thanks very much again

Wayne
January 13th, 2003, 19:40
Last question - how do I "just continue with EXCEPTION_CONTINUE_EXECUTION"? Im not using C or C++ and im not using SEH - Im just catching EXCEPTION_BREAKPOINT in the main debugger loop

focht
January 13th, 2003, 23:15
Greets,

your exception filter/main loop returns with EXCEPTION_CONTINUE_EXECUTION.
This forces the OS to restore the thread context from the EXCEPTION_POINTERS context record and continue executing where the exception occurred (the thread executes the real instruction).

Regards,

Anastasius Focht

Wayne
January 13th, 2003, 23:50
Thankyou so much for your help with this focht, this is something I need to get working, its not optional! but nobody seems to know other than you!
Here's what Ive got so far (its working perfectly so far and it's restoring the breakpoint, all it's not doing is the EXCEPTION_CONTINUE_EXECUTION bit to go back to the restored breakpoint -- instead, because Im not doing that, its just continuing to the next instruction, skipping over my restored instruction) ...
<pre>DO
WaitForDebugEvent DE, %INFINITE
SELECT CASE DE.dwDebugEventCode
CASE %EXCEPTION_DEBUG_EVENT
SELECT CASE DE.u.deuException.ExceptionRecord.ExceptionCode
CASE 2147483651: ' &h80000003:
STDOUT "STATUS_BREAKPOINT, address 0x" & HEX$(DE.u.deuException.ExceptionRecord.ExceptionAddress,8)
DIM Patch AS BYTE, PatchPtr AS DWORD, lpBytes AS DWORD
hThread = OpenThread(0,0, DE.dwThreadid)
Patch = &h40
PatchPtr = VARPTR(Patch)
WriteProcessMemory hProc, BYVAL DE.u.deuException.ExceptionRecord.ExceptionAddress, BYVAL PatchPtr, BYVAL LEN(Patch), lpBytes
Patch = 0
ReadProcessMemory hProc, BYVAL DE.u.deuException.ExceptionRecord.ExceptionAddress, BYVAL PatchPtr, BYVAL LEN(Patch), lpBytes
IF Patch = &h40 THEN: STDOUT "Wrote ok!": ELSE: STDOUT "Write failed, byte=" & HEX$(Patch)
/* Need to somehow call EXCEPTION_CONTINUE_EXECUTION at this stage!! */
CASE ELSE
STDOUT "Other exception: 0x" & HEX$(DE.u.deuException.ExceptionRecord.ExceptionCode)
END SELECT
END SELECT
ContinueDebugEvent DE.dwProcessId, DE.dwThreadId, %DBG_CONTINUE
LOOP</pre>

focht
January 14th, 2003, 11:57
Greets again...

Wow first time i see someone using debugger loop in Visual Basic (looks like) :|

Well i missed the part about the "main debugger loop" so this is how it should work (pseudo C-Code):

<pre>

case EXCEPTION_DEBUG_EVENT:

if( exceptRec.ExceptionCode == EXCEPTION_BREAKPOINT)
{
...
byte opcode;
if( get_patch_byte_from_bpt_list( exceptRec.ExceptionAddress, &opcode))
{
// WriteProcessMemory( ...)
restore_original_instruction( exceptRec.ExceptionAddress, opcode)

// get thread context for the thread that actually hit the BP
CONTEXT context;
context.ContextFlags = CONTEXT_FULL;

if( GetThreadContext( hExceptionThread, &context))
{
// The EIP in the context structure points past the breakpoint, so
// decrement EIP to point at the original instruction
context.Eip = context.Eip-1;

// set the new context
SetThreadContext( hExceptionThread, &context);

// ContinueDebugEvent with DBG_CONTINUE will the thread
// execute original instruction
}
}
...
}

...
ContinueDebugEvent( ..., DBG_CONTINUE);

</pre>

Regards,

Anastasius Focht

Wayne
January 14th, 2003, 18:57
Anastasius, bingo! That's exactly what I needed to see
Thankyou so very, very much with this - it's an area of programming that not many people seem to have much experience with so it's hard finding somebody who's both willing and able to help. You're a gentleman!