简体   繁体   中英

How do debuggers guarantee correctness when using INT 3 (0xCC) software breakpoint even though an instruction was patched?

I've read that the INT 3 (0xCC) is used for software breakpoints.

It is set by (for instance) a debugger by overwriting the actual program code in memory.

I've also read that INT 3 is a "trap" not "fault" exception meaning the address pushed on the stack is the address of the instruction following the INT3 instruction.

How does the debugger guarantee correctness if the patched instruction is not re-executed?

When you want to continue execution after the breakpoint fires, you have two possibilities: either the breakpoint was only supposed to fire once, or it was supposed to be persistent. If it was only supposed to fire once, you restore the original value you overwrote with your breakpoint instruction, manually adjust the address to that instruction's address (remember, regardless of what instruction was there, what executed was your single-byte breakpoint, so the adjustment is always trivial). Then you continue execution.

If it was supposed to be a persistent breakpoint, there's one added wrinkle: before you continue execution, you set the single-step (aka trap) bit in the flags on the stack. That means only the one instruction where the breakpoint was set will execute, then you'll get a breakpoint interrupt again. You respond to that by restoring the int 3 byte you had just patched to the first byte of the original instruction, and (again) continue execution.

It's been a while since I've delved into that sort of stuff, but assuming you are correct that the following address is pushed on the stack, the debugger can pop the return address and use it to figure out where the breakpoint was (the return address minus one, since the INT 3 instruction is one byte long) [edited].

In other words, the debugger doesn't necessarily need to return to the address on the stack. It can restore the original instruction, and then execute it at the original location. If the breakpoint is to remain set, it can use the "trap bit" in the flags to execute only a single instruction - the original one that was overwritten - before another trap will be generated (INT 3 again I think); then the INT 3 instruction can be re-established before continuing execution properly.

Most of the time, though, debuggers are operating under a system where they're not directly handling the trap anyway; they might be delivered a signal, for instance, telling them where the trap occurred. Most likely they still need to figure out the "real" address (ie the address of the INT 3 instruction) from the trap address, as the OS has no way to do this.

Things get complicated, too, if there are multiple threads involved; in that case restoring the original instruction "in place" may lead to the breakpoint being missed if it is hit by another thread. One solution might be stopping all the other threads before restoring the instruction (and starting them again afterwards).

通常的解决方案是让调试器修改堆栈上的地址(并恢复被陷阱覆盖的指令),因此它会执行修补的指令。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM