使用 ptrace 过 ptrace 反调试
2020-07-17 16:59:34 Author: bbs.pediy.com(查看原文) 阅读量:619 收藏

ptrace 反调试的原理是进程只能被一个进程附加。

当一个应用使用了 ptrace 反调试的时候,以 objection 为例,附加时候会出现这种现象:
图片描述
查看进程 /proc/self/status 目录,会看到 TracerPid 不为0,其值为附加它的父进程 pid ,这里是 zygote进程。
图片描述

图片描述

它的原理也很,加上 ptrace(PTRACE_TRACEME); 就行。

过掉它的方法很多,下面介绍使用 ptrace 怎么过掉 ptrace :
1、使用 ptrace 附加 zygote 进程。
2、拦截 zygotefork 调用,在 fork 子进程时候获取当前子进程名称,判断是不是我们想要的那个应用,如果是,就保存子进程 pid
3、获取到子进程 pid 后,再拦截子进程的系统调用,判断此系统调用是不是 ptrace,并且参数是 PTRACE_TRACEME
4、拦截到指定系统调用后,修改调用参数,让 ptrace(PTRACE_TRACEME); 执行失败。

// 拦截 zygote 进程的 fork
        if (ptrace(PTRACE_SETOPTIONS, pid, (void *)1, (void *)(PTRACE_O_TRACEFORK))) {
            printf("FATAL ERROR: ptrace(PTRACE_SETOPTIONS, ...)");
            return -1;
        }
        ptrace(PTRACE_CONT, pid, (void *)1, 0);
        int t;
        int stat;
        int child_pid = 0;
        for (;;) {
            t = waitpid(-1, &stat, __WALL | WUNTRACED);

            // 判断当前 fork 程序是不是我们指定的应用
            if (t != 0 && t == child_pid) {
                if (debug > 1)
                    printf(".");
                char fname[256];
                sprintf(fname, "/proc/%d/cmdline", child_pid);
                int fp = open(fname, O_RDONLY);
                if (fp < 0) {
                    ptrace(PTRACE_SYSCALL, child_pid, 0, 0);
                    continue;
                }
                read(fp, fname, sizeof(fname));
                close(fp);

                if (strcmp(fname, appname) == 0) {
                    if (debug)
                        printf("zygote -> %s\n", fname);

                    // detach from zygote
                    ptrace(PTRACE_DETACH, pid, 0, (void *)SIGCONT);

                    // now perform on new process
                    pid = child_pid;
                    break;
                } else {
                    ptrace(PTRACE_SYSCALL, child_pid, 0, 0);
                    continue;
                }
            }
if (zygote) {
        // 获取到指定进程pid后,拦截它的system_call
        ptrace(PTRACE_SYSCALL, pid, 0, 0);
        while (1) {
            waitpid(pid, NULL, 0);
            //系统调用前修改调用参数
            hookSysCallBefore(pid);
            ptrace(PTRACE_SYSCALL, pid, 0, 0);

            waitpid(pid, NULL, 0);
            // 修改系统调用结果
            hookSysCallAfter(pid);
            ptrace(PTRACE_SYSCALL, pid, 0, 0);
        }
    }
void hookSysCallBefore(pid_t pid) {
    struct pt_regs regs;
    int sysCallNo = 0;

    ptrace(PTRACE_GETREGS, pid, NULL, &regs);
    sysCallNo = getSysCallNo(pid, &regs);

    if (sysCallNo == __NR_ptrace) {
        regs.ARM_r0 = 0; // 伪造一个成功的返回值
        ptrace(PTRACE_SETREGS, pid, NULL, regs);
        printf("__NR_ptrace: %d\n", regs.ARM_r0);
    }
}

void hookSysCallAfter(pid_t pid) {
    struct pt_regs regs;
    int sysCallNo = 0;

    ptrace(PTRACE_GETREGS, pid, NULL, &regs);
    sysCallNo = getSysCallNo(pid, &regs);

    if (sysCallNo == __NR_ptrace) {
        regs.ARM_r0 = PTRACE_PEEKTEXT;
        ptrace(PTRACE_SETREGS, pid, NULL, regs);
        printf("__NR_ptrace: %ld\n", regs.ARM_r0);
    }
}

最后效果如下,可以看到 TracerPid = 0 了
图片描述

参考:https://bbs.pediy.com/thread-212404.htm

[看雪官方培训]《安卓高级研修班(网课)》9月班开始招生!顶尖技术、挑战极限、工资翻倍!


文章来源: https://bbs.pediy.com/thread-260731.htm
如有侵权请联系:admin#unsafe.sh