安卓应用加固之反动态调试技术总结

允我心安 提交于 2019-11-28 11:34:43

0x00 前言

动态调试是比静态分析更为高效地一种破解手段。因此在破解安卓应用之前,一般会先对应用进行动态调试,了解应用大致运行流程和各个类之间的逻辑关系。

反动态调试可以从以下两个个方向着手:

1.运行环境检测:检测应用的运行环境是否安全,是否可能存在被调试的风险

2.动态调试指令检测:检测应用的运行过程中是否受到动态调试指令的控制

 

本文完全参考自网友  爱吃菠菜  的反调试总结,由于我的资料是pdf文档,已经找不到出处,在此对网友 爱吃菠菜 说一声抱歉。

 

0x01 运行环境检测

1.调试端口检测

不同调试器默认使用不同的调试端口,且这些端口默认值往往不被修改。

 

2.调试器进程检测

不同调试器会在系统中创建不同进程对应用进行劫持以达到动态调试目的。

 

3.父进程名检测

针对so文件,破解者可以自己编写一个APK对so库进行调试。

(1)正常启动的apk程序:父进程是zygote
(2)调试启动的apk程序:在AS中用LLDB调试发现父进程还是zygote
(3)附加调试的apk程序:父进程是zygote
(4)vs远程调试 用可执行文件加载so:父进程名为gdbserver
父进程名非zygote的,判定为调试状态。
 

4.自身进程名检测

原理同上条。正常的APK进程名一般为入口类的目录。形如:com.xxx.xxx.xxxx.xxxMainActivity。在开发时,该值是已知的,因此可对该值进行检测。

 

5.apk线程检测

正常APK运行时一般会有多个线程在运行,可以在开发阶段分析自己的源码,正常运行时至少会有多少线程同时运行。针对如果破解者仍是自己编写APK调用so库调试分析的情况,此时的APK中一般只有一条线程。

 

6.apk进程fd文件检测

分析系统中/ proc/pid/fd路径下的文件差异进行判断。

1)APK本身启动的进程和非APK启动的进程,fd数量不一样

2)APK正常启动和APK动态调试启动,进程fd数量也不一样

 

7.安卓系统自带的动态调试检测函数

android.os.Debug.isDebuggerConnected();

 

8.tracepid

/proc/pid/status文件中有一个tracepid字段,若进程被调试,则tracepid值不为0,否则为0。可以对该值进行检测以判断该进程是否被调试。

可以将statue字段写为t,停止调试

 

10.stat

如果是被调试状态,则

/proc/pid/stat

/proc/pid/task/pid/stat

第二个字段是t(T)

 

11.wchan

/proc/pid/wchan

/proc/pid/task/pid/wchan

ptrace_stop

 

12.ptrace

每个进程同一时刻只能被一个进程ptrace。

可以主动ptrace自己,让别的进程无法对自己ptrace;也可以写多个进程,让其互相之间ptrace以免被调试器ptrace

 

13.代码执行时间间隔检测

在两段不同代码处插入时间函数,获取当前时间。对两个时间差进行检测,如果差值过大,则说明两段代码运行时间间隔过长,可能是被单步调试。

 

14.mem、pagemap监测

第三代壳技术原理:通常壳会在程序运行前完成对text的解密,所以脱壳可以通过dd与gdb_gcore来dump  /proc/pid/mem或/proc/pid/pagemap,获取到解密后的代码内容。针对这种运行时解密再加密的壳,有的脱壳技术需要用到动态调试来dump内存中的数据。
可以通过Inotify系列的API来监控mem或pagemap的打开或访问事件,一旦发生就结束进程来阻止dump。

 

 

0x02  动态调试中检测

 

1.断点指令检测

如果函数被下软件断点,则断点地址会被改写为bkpt指令,可以在函数体中搜索bkpt指令来检测软件断电。

 

2.函数hash值检测

so文件中函数的指令是固定,但是如果被下了软件断点,指令就会发生改变(断点地址被改写为bkpt断点指令),可以计算内存中一段指令的hash值进行校验,检测函数是否被修改或被下断点。

 

3.单步调试陷阱

调试器从下断点到执行断点的过程分析:
1)保存:保存目标处指令
2)替换:目标处指令替换为断点指令
3)命中断点:命中断点指令(引发中断 或者说发出信号)
4)收到信号:调试器收到信号后,执行调试器注册的信号处理函数。
5 )恢复:调试器处理函数恢复保存的指令
6)回退:回退PC寄存器
7 )控制权回归程序.
 
主动设置断点指令/注册信号处理函数的反调试方案:
1)在函数中写入断点指令
2)在代码中注册断点信号处理函数
3)程序执行到断点指令,发出信号,分两种情况:
  (1)非调试状态
  进入自己注册的函数,NOP指令替换断点指令,回退PC后正常指令。
  (执行断点发出信号—进入处理信号函数—NOP替换断点—退回PC)
  (2)调试状态
  进入调试器的断点处理流程,他会恢复目标处指令失败,然后回退PC,进入死循环。
 

4.利用IDA先截获信号的特性进行检测

IDA会首先截获信号,导致进程无法接收到信号,导致不会执行信号处理函数。将关键流程放在信号处理函数中,如果没有执行,就是被调试状态。

 

5.利用IDA解析缺陷攻击IDA调试器

IDA调试器原理:IDA采用递归下降算法来反汇编指令,而该算法最大的缺点在于它无法处理间接代码路径,无法识别动态算出来的跳转。而arm架构下由于存在arm和thumb指令集,就涉及到指令集切换,IDA在某些情况下无法智能识别arm和thumb指令,进一步导致无法进行伪代码还原。

在IDA动态调试时,仍然存在该问题,若在指令识别错误的地点写入断点,有可能使得调试器崩溃。
 
 
待更.....之后我会把相应反调试技术的实现代码补充上

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!