PTRACE_TRACEME 与反调试

文章目录

  • [PTRACE_TRACEME 与反调试](#PTRACE_TRACEME 与反调试)
    • [TRACEME 的底层机制](#TRACEME 的底层机制)
      • 内核级标记
      • [后续 - 触发中断](#后续 - 触发中断)
        • [使用 ptrace 单步调试的代码模式:](#使用 ptrace 单步调试的代码模式:)
    • 反调试:抢占调试器插槽

PTRACE_TRACEME 与反调试

设置 PTRACE_TRACEME 是 Linux 的经典反调试手段,因为系统限制调试是独占的,一个进程只能有一个 debugger。我们看下面两种 Linux 系统调用模式,其中 PTRACE_TRACEME 适用于让父进程调试自己,PTRACE_ATTACH 适用于调试器附加到其他进程。

c 复制代码
// TRACEME demo
	pid_t child_pid;
    child_pid = fork();
    if (child_pid == 0) {
        // 子进程:声明自己被跟踪,允许父进程跟踪
        if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) { /* err */ }
        printf("Child: I'm being traced!\n");
        // 子进程执行一些操作
        execl("/bin/ls", "ls", NULL);  // 替换为 ls 命令
    } else if (child_pid > 0) {
        // 父进程:等待子进程停止
        int status;
        waitpid(child_pid, &status, 0);
        if (WIFSTOPPED(status)) printf("Parent: Child is stopped by ptrace.\n");
        // 父进程继续跟踪子进程
        ptrace(PTRACE_CONT, child_pid, NULL, NULL);
        // 等待子进程结束
        waitpid(child_pid, &status, 0);
        printf("Parent: Child process exited.\n");
    } 
 
// output 
Child: I'm being traced!
Parent: Child is stopped by ptrace.
trace-me  trace-me.c
Parent: Child process exited.
c 复制代码
// PTRACE_ATTACH demo
	pid_t target_pid;
    printf("Enter the PID of the process to attach: ");
    scanf("%d", &target_pid);

    // 附加到目标进程,目标进程会受到 SIGSTOP 停止
    if (ptrace(PTRACE_ATTACH, target_pid, NULL, NULL) < 0) { /* err */ }
    printf("Attached to process %d\n", target_pid);

    // 等待目标进程停止
    int status;
    waitpid(target_pid, &status, 0);
    if (WIFSTOPPED(status)) printf("Process %d is stopped.\n", target_pid);

    // 继续运行目标进程
    if (ptrace(PTRACE_CONT, target_pid, NULL, NULL) < 0) { /* err */ }
    printf("Process %d continued.\n", target_pid);

    // 分离目标进程
    if (ptrace(PTRACE_DETACH, target_pid, NULL, NULL) < 0) { /* err */ }
    printf("Detached from process %d\n", target_pid);
}

TRACEME 的底层机制

内核级标记

当进程调用 ptrace(PTRACE_TRACEME, 0, 0, 0) 时,内核会在该进程的 task_struct 中设置 PT_PTRACED 标志位。此标志表示该进程已主动声明 "允许被父进程跟踪",同时隐式触发以下规则:

  • 一个进程同一时间只能被一个调试器跟踪(无论是通过 PTRACE_TRACEME 还是 PTRACE_ATTACH)。
  • 若后续有其他调试器尝试附加(如 gdb 的 attach 命令),内核会拒绝并返回 EPERM 错误。

后续 - 触发中断

设置标记不代表子进程立刻就会被中断,必须收到停止信号时才会被父进程捕获。通常的做法是调用 exec() 触发一个 SIGTRAP。当前进程被标记为 PT_PTRACED 后,在调用 exec 系列函数时,内部的 load_elf_binary 函数会给自己发送 SIGTRAP 信号触发信号处理。

通用的 do_signal 函数在处理 PT_PTRACED 进程时会触发特殊逻辑。它会停止 current_thread,给父进程发送 SIGCHLD 信号,然后调用 schedule 切换时间片。此时子进程被父进程通过 waitpid 捕获。父进程之后可以使用 PTRACE_CONT, PTRACE_SINGLESTEP 调试子进程。

使用 ptrace 单步调试的代码模式:
ptrace 请求 作用
PTRACE_TRACEME 设置当前进程为可被跟踪状态,并在 execve() 后触发 SIGTRAP
PTRACE_GETREGS 获取寄存器信息
PTRACE_PEEKDATA 读取进程内存
PTRACE_POKEDATA 修改进程内存
PTRACE_SETREGS 修改寄存器信息
PTRACE_SINGLESTEP 单步执行一条指令
PTRACE_SYSCALL 在系统调用进入和退出时暂停

具体操作流程

  1. 父进程 fork 出一个子进程
  2. 子进程调用 PTRACE_TRACEME 并执行 execve()
  3. 父进程 wait 子进程 SIGTRAP
  4. 父进程循环调用 ptrace(PTRACE_SYSCALL, ...)PTRACE_SINGLESTEP 进行调试**
    • PTRACE_GETREGS 获取 rip(指令地址)、rax(返回值)、orig_rax(系统调用号)
    • PTRACE_PEEKDATA 读取内存内容( get/set 内存的数据和指令)
    • PTRACE_POKEDATA 修改指令执行行为,配合单步 rip 可以解析当前汇编,执行到特定地址时修改寄存器。
  5. SYSCALL 分别在 syscall 的 enter 和 exit 时触发,配合 orig_rax, rax 可以判断在哪个 syscall

反调试:抢占调试器插槽

子进程在启动时立即调用 PTRACE_TRACEME,相当于提前占用了唯一的调试器插槽。即使父进程不进行任何实际跟踪操作,该插槽已被占用,导致外部调试器无法通过常规手段附加。

例如 XCTF 的 LoopCrypto 首先调用 TRACEME,然后检查 /proc/pid/status 的 TracerPid 字段等于零,来保证成功设置 TRACEME

(感觉应该类似判断返回值小于零?)

绕过 TRACEME 反调试

利用时间差:在子进程调用 PTRACE_TRACEME 前,父进程或外部程序能快速附加调试器,仍可拦截子进程。

静默跟踪:父进程可通过 PTRACE_SEIZE(非侵入式跟踪)或直接忽略跟踪事件,避免触发信号暂停,使子进程看似未被调试。

对抗手段:
  • 代码修补:修改二进制,移除 ptrace 调用或跳过相关代码。
  • 内核模块:通过自定义内核模块强制清除 PT_PTRACED 标志。
  • Frida 等工具:使用 frida-server 或 LD_PRELOAD 注入,在进程内存中动态禁用反调试代码。例如在二进制加载后运行前直接 hook 掉 init array 中的反调试函数。(XCTF LoopCrypto)
相关推荐
HaoHao_0105 分钟前
AWS Fargate
大数据·服务器·云计算·aws·云服务器
Channing Lewis9 分钟前
Linux 中为什么进程是休眠的,但是还是处理了数据
linux·运维·服务器
深度Linux23 分钟前
Linux性能优化实战,网络丢包问题分析
linux·性能优化·linux内核
赶紧写完去睡觉27 分钟前
尚硅谷课程【笔记】——大数据之Shell【二】
大数据·linux·shell编程
激进的猴哥31 分钟前
day33-数据同步rsync
linux·运维·服务器
kfepiza1 小时前
for fn in *.html ;do fns=“${fns} ${fn} “ ;done; firefox ${fns}
linux·html·firefox
似霰1 小时前
Unix/Linux编程:fcntl函数总结
linux·unix
我是唐青枫1 小时前
Linux iostat 命令使用详解
linux·运维·服务器
Azure DevOps1 小时前
Azure DevOps Server:集成奇安信开源卫士(OpenSourceSafe)
运维·microsoft·开源·azure·devops
高hongyuan2 小时前
WGCLOUD监控系统部署教程
linux·运维·服务器·数据库·监控平台