简体   繁体   English

即使修补了指令,当使用INT 3(0xCC)软件断点时,调试器如何保证正确性?

[英]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. 我读过INT 3(0xCC)用于软件断点。

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. 我还读到INT 3是一个“陷阱”而非“故障”异常,这意味着堆栈上的地址是INT3指令后面的指令地址。

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. 您可以通过将刚刚修补的int 3字节恢复到原始指令的第一个字节来响应,然后(再次)继续执行。

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]. 这已经有一段时间了,因为我已经深入研究了这种东西,但假设你是正确的,以下地址被推入堆栈,调试器可以弹出返回地址并使用它来找出断点的位置(返回地址减1,因为INT 3指令长一个字节)[编辑]。

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); 如果断点要保持设置,它可以使用标志中的“陷阱位”仅执行一条指令 - 原始的一条指令 - 在生成另一个陷阱之前(我认为是INT 3); then the INT 3 instruction can be re-established before continuing execution properly. 然后可以在继续正确执行之前重新建立INT 3指令。

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. 很可能他们仍然需要从陷阱地址中找出“真实”地址(即INT 3指令的地址),因为操作系统无法做到这一点。

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). 一种解决方案可能是在恢复指令之前停止所有其他线程(并在之后再次启动它们)。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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