安卓逆向面试题汇总 技术篇(2) 反调试相关

admin 2021年7月11日12:40:11评论219 views字数 3594阅读11分58秒阅读模式

首发安全客 链接:https://www.anquanke.com/post/id/246020

文章太长 所以分7个部分发出来

顺便一提,这个排版比安全客上全,我发现有的我忘写了,这个补上了。

2)安卓逆向反调试的手段有哪些

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

1.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("没被调试 返回值为:%dn",n_ret); return 0;}

定位方法:直接在ptrace函数下断点。

绕过方法:手动patch,或者用frida之类的工具hook ptrace直接返回0.

实例演示:

安卓逆向面试题汇总 技术篇(2) 反调试相关



2.TracerPid检测:

背景知识:

TracerPid是进程的一个属性值,如果为0,表示程序当前没有被调试,如果不为0,表示正在被调试, TracerPid的值是调试程序的进程id。

实现代码:

#define MAX_LENGTH 260
//获取tracePidint 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 != ''; 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的值

安卓逆向面试题汇总 技术篇(2) 反调试相关

安卓逆向面试题汇总 技术篇(2) 反调试相关

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

安卓逆向面试题汇总 技术篇(2) 反调试相关

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


3.自带调试检测函数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) 反调试相关


4.检测调试器端口 比如 ida 23946 frida 27042 之类的

背景知识

调试器服务端默认会打开一些特定端口,方便客户端通过电脑usb线,或者直接通过局域网进行连接。

实现代码:

//返回找到的特征端口数量int check_debug_port(){    //特征端口字符串数组  0x5D8A是23946的十六进制 69a2是27042十六进制    //这里为了提高精确度 加个 :    char* p_strPort_ary[] = {":5D8A", ":69A2"};    int n_port_num = 2;  //特征端口数量
//找到特征端口数量 返回值 int n_find_num = 0;
//初始化文件指针 路径 和缓冲区 FILE* fp = 0; char c_line_buf[MAX_LENGTH] = {0}; char* p_str_tcp = "/proc/net/tcp";
fp = fopen(p_str_tcp, "r"); if(NULL == fp ) { return -1; }
//读取文件 看当前文件包含了几个特征端口号 while(fgets(c_line_buf, MAX_LENGTH - 1, fp)) { for (int i = 0; i < n_port_num; ++i) { //如果从当前文本行 找到特定端口号 char* p_line = p_strPort_ary[i]; if(NULL != strstr(c_line_buf, p_line)) { n_find_num++; } } memset(c_line_buf, 0, MAX_LENGTH); }
fclose(fp); //返回找到的特征端口数量 return n_find_num;}

相关特征 定位方法

读取端口时,一般都会读取 /proc/net/tcp文件,所以可以搜索关键字,或者 popen(管道执行命令) fgets(读取文件行)这种api进行定位。

案例演示

这里启动 frida_server,然后查看/proc/net/tcp文件内容,果然发现了frida_server对应的端口。

安卓逆向面试题汇总 技术篇(2) 反调试相关

安卓逆向面试题汇总 技术篇(2) 反调试相关

安卓逆向面试题汇总 技术篇(2) 反调试相关

绕过手法:换个端口就可。

android_server 换端口这里注意 -p 和 端口之间是没有空格的  直接连接

/data/local/tmp/android_server -p8888 //运行android_server  以端口8888运行adb forward tcp:8888 tcp:8888         //转发端口 8888

frida-server 切换端口  这里切换成 6666端口

/data/local/tmp/frida_server -l 0.0.0.0:6666 //启动frida_server 监听6666adb forward tcp:6666 tcp:6666                //转发6666端口frida -H 127.0.0.1:6666 package_name -l hook.js   //注入js


5.根据时间差反调试

背景知识

在关键逻辑的开始和结束的地方,获取当前的秒数。

结束时间减去开始时间,如果超过一定时间,认定是在调试。因为程序运行速度很快的,卡到2-3秒执行完,除非你逻辑好多,算法很复杂,要不基本不大可能。

实现代码:

#define NUM_DEBUG 3int check_debug_time (){    //初始化时间变量    time_t t_start = 0;    time_t t_end = 0;
//获取开始时间值 返回值是一个秒数 long型 从1970年至今的秒数 time(&t_start);
//假设这里就是关键逻辑 登录模块 关键算法之类的 printf("假装这里有登录模块逻辑"); printf("假装这里有算法逻辑");
//获取结束时间值 time(&t_end); if (t_end - t_start > NUM_DEBUG) { return 0; }
return -1;}

绕过方法:手动nop掉。

相关特征 定位方法时间相关api下断点。

案例演示

安卓逆向面试题汇总 技术篇(2) 反调试相关


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

本文始发于微信公众号(移动安全王铁头):安卓逆向面试题汇总 技术篇(2) 反调试相关

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年7月11日12:40:11
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   安卓逆向面试题汇总 技术篇(2) 反调试相关http://cn-sec.com/archives/481691.html

发表评论

匿名网友 填写信息