首页
论坛
课程
招聘
[原创]使用 ptrace 过 ptrace 反调试
2020-7-16 21:01 4602

[原创]使用 ptrace 过 ptrace 反调试

2020-7-16 21:01
4602

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


《0day安全 软件漏洞分析技术(第二版)》第三次再版印刷预售开始!

收藏
点赞2
打赏
分享
最新回复 (5)
雪    币: 1082
活跃值: 活跃值 (98)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
lscmxl 活跃值 2020-7-17 09:07
2
0
不错,有点意思,不去patch调用ptrace(PTRACE_TRACEME)的代码,去搞实现功能的zygote。
雪    币: 630
活跃值: 活跃值 (1224)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
supperlitt 活跃值 2020-7-17 10:45
3
0
先做个点。
雪    币: 1356
活跃值: 活跃值 (161)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Amun 活跃值 2020-7-17 12:37
4
0
如果最后实现的效果是把 TracerPid 改成 PPid,而不是 0,那就更好了
雪    币: 4813
活跃值: 活跃值 (454)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
白菜大哥 活跃值 2020-7-20 11:15
5
0
有些手机,比如龙岗无敌手的个别机型,内核层就没开启ptrace,其实完全大胆一点,给他返回失败就行了。
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
crl7885 活跃值 2020-7-27 17:30
6
0
楼主,能放个附件出来,学习下吗?谢谢!
游客
登录 | 注册 方可回帖
返回