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)
相关推荐
wirepuller_king1 小时前
创建Linux虚拟环境并远程连接,finalshell自定义壁纸
linux·运维·服务器
Yan-英杰1 小时前
【百日精通JAVA | SQL篇 | 第二篇】数据库操作
服务器·数据库·sql
在野靡生.2 小时前
Ansible(1)—— Ansible 概述
linux·运维·ansible
风123456789~2 小时前
【Linux运维】查询指定日期的上月
linux·运维·服务器
zyk_5202 小时前
Docker desktop如何汉化
运维·docker·容器
韭菜盖饭2 小时前
解决Docker端口映射后外网无法访问的问题
运维·docker·容器
我没想到原来他们都是一堆坏人2 小时前
利用vmware快速安装一个可以使用的centos7系统
linux·虚拟机
x-cmd3 小时前
[250331] Paozhu 发布 1.9.0:C++ Web 框架,比肩脚本语言 | DeaDBeeF 播放器发布 1.10.0
android·linux·开发语言·c++·web·音乐播放器·脚本语言
weitinting3 小时前
Ali linux 通过yum安装redis
linux·redis
myloveasuka3 小时前
[Linux]从硬件到软件理解操作系统
linux·开发语言·c++