如何用ptrace为地址范围设置监视点/断点?父子共享内存技术求助
父子进程共享内存的预读取修改方案
一、无需硬件断点的更优解决方案
硬件断点本身受限于CPU寄存器数量(x86架构最多4个),且权限配置麻烦,更可靠的是用以下两种方案实现需求:
1. 内存保护+信号拦截
核心思路:把共享内存设为只读,子进程读取时触发SIGSEGV信号,父进程捕获信号后修改内存,再恢复权限让子进程继续执行。
简化代码示例:
#include <sys/ptrace.h> #include <sys/wait.h> #include <sys/mman.h> #include <stdio.h> #include <signal.h> int main() { // 创建匿名共享内存页 void* shmem = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); // 初始设置为只读 mprotect(shmem, 4096, PROT_READ); pid_t pid = fork(); if (pid == 0) { // 子进程:主动让父进程跟踪自己 ptrace(PTRACE_TRACEME, 0, NULL, NULL); // 尝试读取共享内存,触发SIGSEGV int val = *(int*)shmem; printf("子进程读取到:%d\n", val); munmap(shmem, 4096); return 0; } else { int status; // 等待子进程触发信号 waitpid(pid, &status, 0); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSEGV) { // 父进程修改共享内存内容 *(int*)shmem = 12345; // 恢复内存可读可写权限 mprotect(shmem, 4096, PROT_READ | PROT_WRITE); // 让子进程继续执行 ptrace(PTRACE_CONT, pid, NULL, NULL); // 等待子进程结束 waitpid(pid, &status, 0); } munmap(shmem, 4096); return 0; } }
2. 显式同步机制(最简单可靠)
如果子进程的读取时机可控,直接用信号量、互斥锁或管道做同步,完全不需要依赖断点或内存保护:
信号量实现示例:
#include <semaphore.h> #include <sys/mman.h> #include <unistd.h> #include <stdio.h> typedef struct { sem_t sem; int value; } SharedData; int main() { // 创建包含信号量的共享内存 SharedData* shmem = mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); // 初始化信号量,初始值0(子进程等待父进程信号) sem_init(&shmem->sem, 1, 0); pid_t pid = fork(); if (pid == 0) { // 子进程:等待父进程通知 sem_wait(&shmem->sem); printf("子进程读取到:%d\n", shmem->value); sem_destroy(&shmem->sem); munmap(shmem, sizeof(SharedData)); return 0; } else { // 父进程:先修改共享内存内容 shmem->value = 9876; // 通知子进程可以读取 sem_post(&shmem->sem); wait(NULL); munmap(shmem, sizeof(SharedData)); return 0; } }
二、硬件断点的问题修复(如果坚持使用)
1. 权限问题解决
硬件断点需要进程拥有CAP_SYS_PTRACE权限,可通过以下方式配置:
- 给程序添加权限:
sudo setcap cap_sys_ptrace+ep ./your_program - 临时关闭系统ptrace限制(不推荐,不安全):
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
2. 永久断点实现
硬件断点触发后CPU调试寄存器会自动重置,要实现永久断点,需在信号处理后重新写入寄存器:
伪代码逻辑:
// 父进程捕获子进程的SIGTRAP信号 waitpid(pid, &status, 0); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { // 修改共享内存内容 *(int*)shmem = 54321; // 重新设置硬件断点 struct user_regs_struct regs; ptrace(PTRACE_GETREGS, pid, NULL, ®s); regs.dr0 = (unsigned long)shmem; // 设置断点地址 regs.dr7 = (1 << 0); // 启用dr0的读断点 ptrace(PTRACE_SETREGS, pid, NULL, ®s); // 让子进程继续执行 ptrace(PTRACE_CONT, pid, NULL, NULL); }
注意:这种方式依然受限于CPU最多4个硬件断点的限制,不适合多地址监控场景。
内容的提问来源于stack exchange,提问作者Maray97
相关产品推荐
相关产品推荐

