Android反调试和对抗

反调试

1.自己ptrace自己

代码非常简单,在so中加上这行代码即可:

1
ptrace(PTRACE_TRACEME, 0, 0, 0);

其中PTRACE_TRACEME代表:本进程被其父进程所跟踪。
一个进程只能被ptrace一次,通常在调试的时候都会attach被调试应用的进程,如果我们先占坑,父进程attach自己,那么其他的附加调试就会失败。

2.检测Tracerpid的值

在自己的应用中的native层加上一个循环检查自己status中的TracerPid字段值,如果非0或者是非自己进程pid(如果采用了第一种方案的话,这里也是需要做一次过滤的);那么就认为被附加调试了。

3.调试器端口检测

以IDA为例,读取/proc/net/tcp,查找IDA远程调试的默认23946端口(或者执行命令netstat -apn),如果处于监听状态说明被存在调试可能。

4.调试器名称检测

遍历进程,查找类似android_server,gdbserver,gdb等调试器进程

5.多进程反调试

目标程序创建了多个进程,互相ptrace,不同进程分工明确,守护或者反调试。只要有一个进程出现异常,集体挂掉,颇有一番兄弟有难同当的感觉。

对抗
多进程都是通过fork出来的,因此我们修改/bionic/libc/bionic/fork.c里面的fork函数来使得目标进程fork失败

6.检测系统关键文件

进程被ptrace后通常会有一些特征,这些特征也常常作为反调试的判断依据:

status

/proc/pid/status 和 /proc/pid/task/pid/status:普通状态下,TracerPid这项应该为0;调试状态下为调试进程的PID。
upload successful

stat

/proc/pid/stat 和 /proc/pid/task/pid/stat:调试状态下,括号后面的第一个字母应该为t

upload successful

wchan

/proc/pid/wchan 和 /proc/pid/task/pid/wchan:调试状态下,里面内容为ptrace_stop
upload successful

7.代码执行时间检测

调试状态和非调试状态代码执行时间不一样,如果两个时间值相差过大,则说明中间的代码流程被调试了。因为调试者停下来一步步观察了这一段代码的执行情况,因此这部分代码的执行时间远远超出了普通状态的执行时间。

8.使用inotify对文件系统进行监视

/proc/pid/maps
/proc/pid/mem
/proc/pid/pagemep

防dump

通过对目标进程文件/proc/pid/mem文件操作,可以获得其内存的数据。
而inotify可以监控文件系统的变化,当文件被打开、删除,读写等操作时,同时用户相应变化。
因此可以通过监控/proc/pid/mem 与/proc/pid/pagemep来防止内存dump。

参考资料:

  1. Android反调试技术整理与实践(https://gtoad.github.io/2017/06/25/Android-Anti-Debug/)