如何通过父进程配置x86-64调试寄存器实现硬件断点?
硬件断点配置问题:子进程未触发SIGTRAP信号
问题背景
我尝试为x86-64架构的子进程配置DR0、DR6、DR7调试寄存器来设置硬件断点,但恢复子进程后始终收不到SIGTRAP信号。
寄存器配置方案
- DR0:设置为子进程中需要触发断点的函数地址
- DR6:置为0,根据资料设置断点/观察点时需要清空该寄存器
- DR7:设置为
0x701,即把reserved_10、dr0_local、LE、GE标志位设为1
遇到的问题是,使用ptrace(PTRACE_POKEUSER, ...)写入user结构体的DR7字段时常返回-1,即便返回0,断点也无法正常工作。
初始代码
#include <stdio.h> #include <unistd.h> #include <sys/ptrace.h> #include <sys/user.h> #include <sys/wait.h> #include <inttypes.h> #include <string.h> #define DR_OFFSET(dr) ((((struct user *)0)->u_debugreg) + (dr)) pid_t child_pid; void child_func() { printf("CHILD\n"); } int main() { if(child_pid) { waitpid(child_pid, NULL, 0); struct user_regs_struct regs = {}; ptrace(PTRACE_GETREGS, child_pid, NULL, ®s); printf("BREAKPOINT : RIP -> %llx\n", regs.rip); printf("POKE DRO : %lx\n", ptrace(PTRACE_POKEUSER, child_pid, DR_OFFSET(0), (void*)&child_func)); uint64_t dr7 = 0; dr7 |= (uint64_t)pow(2, 0) + (uint64_t)pow(2, 8) + (uint64_t)pow(2, 9) + (uint64_t)pow(2, 10); printf("POKE DR7 : %lx\n", ptrace(PTRACE_POKEUSER, child_pid, DR_OFFSET(7), &dr7)); printf("POKE DR6 : %lx\n", ptrace(PTRACE_POKEUSER, child_pid, DR_OFFSET(6), 0)); printf("PTRACE_CONT : %lx\n", ptrace(PTRACE_CONT, child_pid, NULL, NULL)); printf("WAITPID : %x\n", waitpid(child_pid, NULL, 0)); memset(®s, 0, sizeof(regs)); ptrace(PTRACE_GETREGS, child_pid, NULL, ®s); printf("BREAKPOINT : RIP -> %llx\n", regs.rip); ptrace(PTRACE_CONT, child_pid, NULL, NULL); } else { ptrace(PTRACE_TRACEME, 0, NULL, NULL); __asm__("int3"); child_func(); } return 0; } void __attribute__ ((constructor)) premain() { child_pid = fork(); }
初始代码执行结果
我在子进程中设置了软件断点使其暂停,以便操作其user结构体。当DR7写入成功时,执行结果如下:
BREAKPOINT : RIP -> 5651726ec2ee POKE DRO : 0 POKE DR7 : 0 POKE DR6 : 0 PTRACE_CONT : 0 CHILD WAITPID : 38af BREAKPOINT : RIP -> 0
第二个RIP值为0,说明硬件断点未触发。
更新:修复后代码及结果
参考建议后代码已正常工作:
#include <stdio.h> #include <unistd.h> #include <sys/ptrace.h> #include <sys/user.h> #include <math.h> #include <sys/wait.h> #include <inttypes.h> #include <string.h> #define DR_OFFSET(dr) ((((struct user *)0)->u_debugreg) + (dr)) pid_t child_pid; void child_func() { printf("CHILD\n"); } int main() { if(child_pid) { waitpid(child_pid, NULL, 0); struct user_regs_struct regs = {}; ptrace(PTRACE_GETREGS, child_pid, NULL, ®s); printf("BREAKPOINT : RIP -> %llx\n", regs.rip); printf("POKE DRO : %lx\n", ptrace(PTRACE_POKEUSER, child_pid, DR_OFFSET(0), (void*)&child_func)); uint64_t dr7 = 0; dr7 |= (uint64_t)pow(2, 0) + (uint64_t)pow(2, 8) + (uint64_t)pow(2, 9) + (uint64_t)pow(2, 10); printf("POKE DR7 : %lx\n", ptrace(PTRACE_POKEUSER, child_pid, DR_OFFSET(7), &dr7)); printf("POKE DR6 : %lx\n", ptrace(PTRACE_POKEUSER, child_pid, DR_OFFSET(6), 0)); printf("PTRACE_CONT : %lx\n", ptrace(PTRACE_CONT, child_pid, NULL, NULL)); printf("WAITPID : %x\n", waitpid(child_pid, NULL, 0)); memset(®s, 0, sizeof(regs)); ptrace(PTRACE_GETREGS, child_pid, NULL, ®s); printf("BREAKPOINT : RIP -> %llx\n", regs.rip); ptrace(PTRACE_CONT, child_pid, NULL, NULL); } else { ptrace(PTRACE_TRACEME, 0, NULL, NULL); __asm__("int3"); child_func(); } return 0; } void __attribute__ ((constructor)) premain() { child_pid = fork(); }
执行结果:
BREAKPOINT : RIP -> 5640849712e1 POKE DRO : 0 POKE DR7 : 0 POKE DR6 : 0 PTRACE_CONT : 0 WAITPID : 3cc9 BREAKPOINT : RIP -> 564084971400 CHILD
内容的提问来源于stack exchange,提问作者HellDiner
相关产品推荐
相关产品推荐

