这里比较常用的反调试手段有

ptrace检测

背景知识:ptrace是linux提供的API, 可以监视和控制进程运行,可以动态修改进程的内存,寄存器值。一般被用来调试。ida调试so,就是基于ptrace实现的。

因为一个进程只能被ptrace一次, 所以进程可以自己ptrace自己,这样ida和别的基于ptrace的工具和调试器或就无法调试这个进程了。
实现代码:

int check_ptrace()
{
 // 被调试返回-1,正常运行返回0
 int n_ret = ptrace(PTRACE_TRACEME, 0, 0, 0);
 if(-1 == n_ret)
 {
     printf("阿偶,进程正在被调试\n");
     return -1;
 }

 printf("没被调试 返回值为:%d\n",n_ret);
 return 0;
}

定位方法:直接在ptrace函数下断点。
绕过方法:手动patch,或者用frida之类的工具hook ptrace直接返回0.
实例演示

 

TracerPid检测:

背景知识:TracerPid是进程的一个属性值,如果为0,表示程序当前没有被调试,如果不为0,表示正在被调试, TracerPid的值是调试程序的进程id。
实现代码:

#define MAX_LENGTH 260

//获取tracePid
int get_tarce_pid()
{
    //初始化缓冲区变量和文件指针
    char c_buf_line[MAX_LENGTH] = {0};
    char c_path[MAX_LENGTH] = {0};
    FILE* fp = 0;

    //初始化n_trace_pid 获取当前进程id
    int n_pid = getpid();
    int n_trace_pid = 0;

    //拼凑路径 读取当前进程的status
    sprintf(c_path, "/proc/%d/status", n_pid);
    fp = fopen(c_path, "r");

    //打不开文件就报错
    if (fp == NULL)
    {
        return -1;
    }

    //读取文件 按行读取 存入缓冲区
    while (fgets(c_buf_line, MAX_LENGTH, fp))
    {
        //如果没有搜索到TracerPid 继续循环
        if (0 == strstr(c_buf_line, "TracerPid"))
        {
            memset(c_buf_line, 0, MAX_LENGTH);
            continue;
        }

        //初始化变量
        char *p_ch = c_buf_line;
        char c_buf_num[MAX_LENGTH] = {0};

        //把当前文本行 包含的数字字符串 转成数字
        for (int n_idx = 0; *p_ch != '\0'; p_ch++)
        {
            //比较当前字符的ascii码  看看是不是数字
            if (*p_ch >= 48 && *p_ch <= 57)
            {
                c_buf_num[n_idx] = *p_ch;
                n_idx++;
            }
        }
        n_trace_pid = atoi(c_buf_num);
        break;
    }

    fclose(fp);
    return n_trace_pid;
}

相关特征

定位方法:一般检测TracerPid都会读取 /proc/进程号/status 这个文件所以可以直接搜索 /status 这种字符串,这里也会用到getpid, fgets这种API,所以也可以通过这两个api定位。

绕过手法:

1) 直接手动patch, nop掉调用

2) 编译内核,修改linux kernel源代码,让 TracerPid永久为0. 修改方法 https://cloud.tencent.com/developer/article/1193431

实例演示:

这里用android studio 调试app 查看app进程对应的 status,status里查看TracerPid的值

 

可以看到TracerPid的值 是调试器的进程id。

 

没被调试的时候,TracerPid的值是0。

 

自带调试检测函数

android.os.Debug.isDebuggerConnected()

背景知识:自带调试检测api, 被调试时候返回 true, 否则返回 false。

import static android.os.Debug.isDebuggerConnected;

public static boolean is_debug()
{
    boolean b_ret = isDebuggerConnected();
    return b_ret;
}

相关特征 定位方法:直接搜索isDebuggerConnected函数名即可。

绕过手法frida之类的工具直接hook函数,直接返回false.

 

根据时间差反调试

背景知识:在关键逻辑的开始和结束的地方,获取当前的秒数。结束时间减去开始时间,如果超过一定时间,认定是在调试。因为程序运行速度很快的,卡到2-3秒执行完,除非你逻辑好多,算法很复杂,要不基本不大可能。

绕过方法:手动nop掉。

案例演示

这里不用说的太全,说几个常见的就行了。说全了时间也不太够。

来源:www.anquanke.com/post/id/246020

发表回复

后才能评论