美文网首页
TracerPid反调试

TracerPid反调试

作者: Sharkchilli | 来源:发表于2020-11-06 22:57 被阅读0次

前言

在很多逆向分析的时候,我们看字符串信息经常能看到TracerPid等关键字,这是一个反调试常用的手段。这次我们就来实现这个检测TracerPid的反调试。

实验

使用ps查看我们运行的包pid

ps | grep "com.shark.jnireflect"
u0_a81    27178 182   915256 42908 ffffffff 4008673c S com.shark.jnireflect

查看本程序的/proc/{PID}/status文件

cat /proc/27178/status
Name:   hark.jnireflect /*进程的程序名*/
State:  S (sleeping)
Tgid:   27178  /*线程组号*/
Pid:    27178  /*进程pid process id*/
PPid:   182  /*父进程的pid parent processid*/
TracerPid:      0  /*跟踪进程的pid*/
Uid:    10081   10081   10081   10081
Gid:    10081   10081   10081   10081
FDSize: 256
Groups: 50081
VmPeak:   915256 kB
VmSize:   885044 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:     42936 kB
VmRSS:     42908 kB
VmData:    16076 kB
VmStk:       136 kB
VmExe:        20 kB
VmLib:     49292 kB
VmPTE:       152 kB
VmSwap:        0 kB
Threads:        11
SigQ:   1/12274
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000001204
SigIgn: 0000000000000000
SigCgt: 00000002000094f8
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: fffffff000000000
Cpus_allowed:   f
Cpus_allowed_list:      0-3
voluntary_ctxt_switches:        109
nonvoluntary_ctxt_switches:     371

我注释了一些我们常看到的信息,其他的对于我们来说不那么重要

TracerPid就是调试此进程的pid,在同一时刻只能有一个进程调试本进程

调试

使用ida进行调试后在查看TracerPid

Name:   hark.jnireflect
State:  t (tracing stop)
Tgid:   27178
Pid:    27178
PPid:   182
TracerPid:      27519
Uid:    10081   10081   10081   10081
Gid:    10081   10081   10081   10081
FDSize: 256
Groups: 50081
VmPeak:   915256 kB
VmSize:   885044 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:     43332 kB
VmRSS:     43332 kB
VmData:    16076 kB
VmStk:       136 kB
VmExe:        20 kB
VmLib:     49292 kB
VmPTE:       152 kB
VmSwap:        0 kB
Threads:        11
SigQ:   1/12274
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000001204
SigIgn: 0000000000000000
SigCgt: 00000002000094f8
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: fffffff000000000
Cpus_allowed:   f
Cpus_allowed_list:      0-3
voluntary_ctxt_switches:        119
nonvoluntary_ctxt_switches:     373

可以看到TracerPid有了值,并且State变成了t状态

使用ps查看调试进程是什么

ps |grep 27519
root      27519 27074 10488  8696  ffffffff 00000000 S ./android_server

可以看到就是我们的android_server程序

当我们使用IDA attach程序的时候,在/proc/{PID}/status文件的TracerPid字段会写入调试程序的PID

也就是说使用TracerPid反调试的原理就是检测这个字段是否为0,为0说明没有被调试,不为0说明正在被调试,检测调试器直接退出就可以达到反调试的效果

代码实现

#include <jni.h>
#include <string>
#include "antidebug.h"

#define NULL 0
#define CHECK_TIME 10
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "shark chilli", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "shark chilli", __VA_ARGS__)

pthread_t id_anti_debug = NULL;

void readStatus() {
    FILE *fd;
    char filename[128];
    char line[128];
    pid_t pid = syscall(__NR_getpid);
    LOGI("PID : %d", pid);
    sprintf(filename, "/proc/%d/status", pid);// 读取proc/pid/status中的TracerPid
    while (1) {
        fd = fopen(filename, "r");
        while (fgets(line, 128, fd)) {
            if (strncmp(line, "TracerPid", 9) == 0) {
                //TracerPid值
                int status = atoi(&line[10]);
                LOGI("########## status = %d, %s", status, line);
                fclose(fd);
                syscall(__NR_close, fd);
                if (status != 0) {
                    LOGI("########## FBI WARNING ##########");
                    LOGI("######### FIND DEBUGGER #########");
                    kill(pid, SIGKILL);
                    return;
                }
                break;
            }
        }
        sleep(CHECK_TIME);
    }
}

void checkAnti() {
    LOGI("Call readStatus...");
    readStatus();
}

void anti_debug() {
    LOGI("Call anti_debug...");
    //创建线程
    if (pthread_create(&id_anti_debug, NULL, (void *(*)(void *)) &checkAnti, NULL) != 0) {
        LOGE("Failed to create a debug checking thread!");
        exit(-1);
    };
    //线程分离 释放资源
    pthread_detach(id_anti_debug);
}

extern "C" JNIEXPORT void JNICALL
Java_com_shark_tracerpidapp_MainActivity_checkTracerPid(
        JNIEnv *env,
        jobject /* this */) {
    anti_debug();
}

上面的代码我在java层的MainActivity的onCreate中调用了
这里开启一个线程在线程中一直去查询自己status文件中TracerPid的值,当TracerPid不为0的时候说明有调试,我们调用kill将其杀死就可以了

使用ida进行调试,断下后输出如下

11-06 14:17:47.283 14196-14196/com.shark.tracerpidapp I/shark chilli: Call anti_debug...
11-06 14:17:47.283 14196-14218/com.shark.tracerpidapp I/shark chilli: Call readStatus...
11-06 14:17:47.283 14196-14218/com.shark.tracerpidapp I/shark chilli: PID : 14196
11-06 14:17:47.283 14196-14218/com.shark.tracerpidapp I/shark chilli: ########## status = 0, TracerPid: 0
11-06 14:17:57.293 14196-14218/com.shark.tracerpidapp I/shark chilli: ########## status = 0, TracerPid: 0
11-06 14:18:07.293 14196-14218/com.shark.tracerpidapp I/shark chilli: ########## status = 0, TracerPid: 0
11-06 14:18:17.293 14196-14218/com.shark.tracerpidapp I/shark chilli: ########## status = 0, TracerPid: 0
11-06 14:18:27.293 14196-14218/com.shark.tracerpidapp I/shark chilli: ########## status = 0, TracerPid: 0
11-06 14:18:37.293 14196-14218/com.shark.tracerpidapp I/shark chilli: ########## status = 0, TracerPid: 0

可以看到在没调试前我们的日志一直在循环输出,说明一直在循环检测TracerPid
将ida运行

11-06 14:19:17.303 14196-14218/com.shark.tracerpidapp I/shark chilli: ########## status = 0, TracerPid: 0
11-06 14:26:07.143 14196-14218/com.shark.tracerpidapp I/shark chilli: ########## status = 13967, TracerPid: 13967
11-06 14:26:07.143 14196-14218/com.shark.tracerpidapp I/shark chilli: ########## FBI WARNING ##########
11-06 14:26:07.143 14196-14218/com.shark.tracerpidapp I/shark chilli: ######### FIND DEBUGGER #########

我们的ida直接退出了,说明成功了!

这里我们并没有进入断点后就退出,是在执行的时候才退出的
我们来改进一下


void readStatus() {
    FILE *fd;
    char filename[128];
    char line[128];
    pid_t pid = syscall(__NR_getpid);
    LOGI("PID : %d", pid);
    sprintf(filename, "/proc/%d/status", pid);// 读取proc/pid/status中的TracerPid
    if (fork() == 0) {
        while (1) {
            fd = fopen(filename, "r");
            while (fgets(line, 128, fd)) {
                if (strncmp(line, "TracerPid", 9) == 0) {
                    int status = atoi(&line[10]);
                    LOGI("########## status = %d, %s", status, line);
                    fclose(fd);
                    syscall(__NR_close, fd);
                    if (status != 0) {
                        LOGI("########## FBI WARNING ##########");
                        LOGI("######### FIND DEBUGGER #########");
                        kill(pid, SIGKILL);
                        return;
                    }
                    break;
                }
            }
            sleep(CHECK_TIME);
        }
    } else {
        LOGE("fork error");
    }
}

很简单就是改成了在子进程里面跑我们的反调试逻辑.
运行使用ps查看应用

root@hammerhead:/ # ps |grep "shark"
u0_a82    14700 182   915140 42784 ffffffff 4008673c S com.shark.tracerpidapp
u0_a82    14721 14700 875236 28968 c01b9228 40085e84 S com.shark.tracerpidapp

可以看到有两个进程了

我们再次使用IDA进行调试

image.png

选择父进程


image.png

直接闪退

这里有个问题我们的子进程是可以被调试的,也就是可以在子进程中绕过调试或者直接杀死子进程
再来改进

void readStatus() {
    FILE *fd;
    char filename[128];
    char line[128];
    pid_t pid = syscall(__NR_getpid);
    LOGI("PID : %d", pid);
    sprintf(filename, "/proc/%d/status", pid);//读取/proc/pid/status中的TracerPid
    if (fork() == 0) {
        int pt = ptrace(PTRACE_TRACEME, 0, 0, 0); //子进程反调试
        if (pt == -1)
            exit(0);
        while (1) {
            fd = fopen(filename, "r");
            while (fgets(line, 128, fd)) {
                if (strncmp(line, "TracerPid", 9) == 0) {
                    int status = atoi(&line[10]);
                    LOGI("########## status = %d, %s", status, line);
                    fclose(fd);
                    syscall(__NR_close, fd);
                    if (status != 0) {
                        LOGI("########## FBI WARNING ##########");
                        LOGI("######### FIND DEBUGGER #########");
                        kill(pid, SIGKILL);
                        return;
                    }
                    break;
                }
            }
            sleep(CHECK_TIME);
        }
    } else {
        LOGE("fork error");
    }
}

上面加上了这段关键代码

 int pt = ptrace(PTRACE_TRACEME, 0, 0, 0); //子进程反调试
        if (pt == -1)
            exit(0);

我们在子进程中使用ptrace,将请求类型设为PTRACE_TRACEME,表示让父进程跟踪自己,而进程在同一时间,只能被一个调试器调试或者跟踪,所以这里就是一个父进程,一个子进程,子进程通过读取父进程的/proc/{PID}/status文件保护父进程不被调试,同时让父进程跟踪自己,保护自己不被调试,如果ptrace失败,说明有调试器已经在调试自己,直接退出

运行,使用cat查看子进程TracerPid

root@hammerhead:/ # ps |grep "shark"
u0_a82    15097 182   915132 42776 ffffffff 4008673c S com.shark.tracerpidapp
u0_a82    15117 15097 875236 29060 c01b9228 40085e84 S com.shark.tracerpidapp
root@hammerhead:/ # cat /proc/15097/status |grep "TracerPid"
TracerPid:      0
root@hammerhead:/ # cat /proc/15117/status |grep "TracerPid"
TracerPid:      15097
root@hammerhead:/ #

上面可以看到,我们的子进程确实被父进程调试了

image.png

使用ida调试后效果如上图

引用

TracerPid反调试实现与逆向

Linux 编程中的API函数和系统调用的关系

相关文章

  • TracerPid反调试

    前言 在很多逆向分析的时候,我们看字符串信息经常能看到TracerPid等关键字,这是一个反调试常用的手段。这次我...

  • 编译libc.so过time函数反调试

    根据论坛各位大神的文章,了解了Android的各种反调试机制,如tracerpid, time。 其中的trace...

  • 修改手机内核绕过ptrace

    前面一篇文章《Android反调试手段收集》在讲常用的反调试方法中有一条是检测进程的TracerPid的值正常情况...

  • 反调试检测之一TracerPid

    当我们使用Ptrace方式跟踪一个进程时,目标进程会记录自己被谁跟踪,可以查看/proc/pid/status看到...

  • 反调试与反-反调试

    1. 反调试与反-反调试 1.1 常用反调试 1.1.1 ptrace 为了方便应用软件的开发和调试,从Unix的...

  • sysctl反调试&反sysctl反调试

    目录 一、关于systcl二、利用systcl做反调试(一)、了解原理(二)、反调试代码三、反sysctl反调试四...

  • 反爬:网页反 debug 问题解决

    1. 网页反 debug 机制分析 反 debug 调试措施,是通过在代码中 添加 debugger 代码 实现,...

  • 反ptrace反调试

    一、前言 上次学习到ptrace反调试,我是将反调试的代码放在主程序的main函数内部,最近学习了一下dyld加载...

  • iOS代码保护,数据加密,反调试,反注入,代码混淆

    iOS代码保护 数据加密 反调试,反注入 代码混淆 明天继续写。。。。。。

  • 课程2

    如何在模拟器里面调试功能: 谷歌调试面板说明 反回两个平级标签 state 生命周期函数 布局的区别

网友评论

      本文标题:TracerPid反调试

      本文链接:https://www.haomeiwen.com/subject/xbxzvktx.html