Android反调试常常出现在init.array
和JNI_Onload
中。除了反调试外,应用还可能在上述两个地方初始化变量。
1)代码执行时间检测
读取系统时间,检测关键代码执行耗时。类似函数:time
,gettimeofday
。
|
|
2)运行环境检测
进程的状态信息能通过procfs系统反馈给用户空间,调试会使进程状态发生变化。
- TCP端口/android_server检测
- 读取
/proc/%d/wchan
:ep_poll
表示未调试,ptrace_stop
表示调试状态 - 读取
/proc/pid/status
:State属性值T
表示调试状态,TracerPid属性值表示正在调试此进程的pid
。非调试情况下State为S
或R
, TracerPid为0
3)ptrace
ptrace()
是Linux的系统调用,也是IDA等调试器实现的基础。它提供了一个进程跟踪另一个进程寄存器、内存等的能力,一个进程只能被一个进程ptrace()
。
ptrace()
应用自身fork()
子进程相互ptrace()
使用形式:
ptrace(PTRACE_TRACEME, 0, 0, 0)
syscall(__NR_ptrace, 0, 0, 0)
简单的反调试会直接调用ptrace()
,只需在进程被ptrace()
前让调试器挂载即可;有的反调试会根据ptrace()
的返回值(0表示成功)来做进一步的处理。
4)模拟器检测
用户层行为和数据检测、模拟器特有属性值,以及模拟器体系结构特征。
- 电池状态和电流、模拟器默认电话号码检测,检测设备IDS是不是
000000000000000
、imsi id是不是310260000000000
,手机运营商等 - API Demo,Dev tool:一般模拟器上才有的应用
- 读取/system/build.prop文件
- 检查模拟器特有文件:/dev/socket/qemud、/dev/qemu_pipe、/sysrtem/bin/qemud、/dev/qemu_pipe、/dev/qemu_trace等
5)断点检测
ARM程序下断点,调试器完成两件事:
- 保存目标地址处指令
- 将目标地址处指令替换成断点指令
指令集 | 指令 |
---|---|
ARM | 0x01, 0x00, 0x9f, 0xef |
Thumb | 0x01,0xde |
当命中断点时,系统产生SIGTRAP信号,调试器收到信号后完成下面操作:
- 恢复断点处原指令
- 回退被跟踪进程的当前PC
这时控制权回到被调试程序,正好执行断点位置指令。这就是ARM平台断点的基本原理。
断点指令会替换内存中原有指令,因此通过检测内存中的断点指令,可以检测调试:
|
|
6)inotify文件系统监控
inotify是内核用于通知用户态文件系统变化的机制,当文件被访问、修改、删除等时,用户态可以快速感知。
1.调用inotify_init()
初始化实例并返回文件描述符,每个文件描述符关联一个事件队列:
|
|
2.拿到这个文件描述符后告诉内核,哪些文件发生哪些事件时需要通知。通过inotify_add_watch()
实现:
|
|
fd
是文件描述符,path
表示监控项的目标路径(可以是文件目录等)。mask
表示关注事件的掩码,例如:IN_ACCESS
代表访问,IN_MODIFY
代表修改等。
与之对应,可以通过inotify_rm_watch()
来删除一个watch:
|
|
每当监视的文件发生变化时,内核便给fd
关联的事件队列里面塞一个文件事件。文件事件用inotify_event
结构表示,可以通过read()
来读取:
|
|
|
|
通过监视
/proc/pid/maps
文件的打开事件,可防针对360加固的dump脱壳。