assembly 如何实施PTRACE_SINGLESTEP?

r3i60tvu  于 2022-11-13  发布在  其他
关注(0)|答案(1)|浏览(118)

据我所知(我可能是错的),在x86-64系统上不可能只执行 * 一条 * 指令。也许您可以执行后面跟有'ud 2'操作码的指令来触发信号--但这样您就不得不担心指令修改控制流并转到其他地方。
然而,如果我理解正确的话,ptrace()syscall有一个SINGLESTEP选项,它只执行一条指令。这是如何实现的呢?我无法想象内核有某种反汇编程序来识别指令并对其进行推理。那么,它是否使用了某种我不知道的体系结构特性?或者完全不同的东西?

blpfk2vs

blpfk2vs1#

是的,在x86上有一个体系结构单步标志。从内核返回到用户空间使内核有机会同时设置RIP/RFLAGS,因此它可以为用户空间设置单步标志,而不必在内核指令上触发它。
由于某些原因,Trap Flag有自己的维基百科文章!另请参阅wikipedia's EFLAGS article
请访问x86标签wiki,获取英特尔架构手册的链接,这些手册记录了所有这些内容。
也许您可以执行后面跟有“ud 2”操作码的指令来触发信号
然后你需要代码来确定x86指令的长度,知道在哪里设置一个软件断点。
x86也有调试寄存器(dr0..7),可以在不修改代码的情况下设置硬件断点,或者可以监控对给定数据地址的访问或写入。(GDB hbreak使用这些寄存器,就像GDB在常量地址上的观察点一样)
但是对于jump/call/ret和其他可能对RIP有特殊影响的指令,使用jmp qword [fs: rax]这样的寻址模式的内存间接跳转需要调试器知道FS段的基址,甚至知道它将从哪个地址加载指针。(我假设您可以像获取实际寄存器值一样轻松地使用ptrace获取此值,rdfsbase是一个新的扩展。)因此,只要调试器停止了所有其他线程,您就可以'在阅读跳转目标指针和继续执行之间,不要让另一个线程修改跳转目标指针而造成TOCTOU争用条件。

有趣的事实:并非所有ISA都有PTRACE_SINGLESTEP硬件支持。

举个例子,Linux内核曾经为ARM模拟它,但是这需要内核中的ARM反汇编器在下一条指令处放置断点,即使是分支目标。现在,ptrace(PTRACE_SINGLESTEP)在ARM上返回-ENOSYS
他们只是去掉了所有的复杂性,而不是试图使它成为SMP安全的,并支持每一个新的指令,如Thumb-2等。(http://lists.infradead.org/pipermail/linux-arm-kernel/2011-February/041324.html
因此调试器必须手动使用这些ISA上的断点,而不是让内核为它们执行。如果这意味着其他线程暂时注意到内存中的调试中断操作码,那就不是内核的问题。(通常像GDB * 这样的调试器会在单步执行时停止所有线程。)
这意味着调试器必须对分支指令进行解码,以确定断点的位置,包括寄存器间接分支和/或 predicate 分支。

相关问题