反调试常见步骤 以及 应对

 第一、自己附加进程,先占坑,ptrace(PTRACE_TRACEME, 0, 0, 0)!

通过查看进程状态中的tracerPid可以知道是否被调试了  cat /proc/pid/status


第二、签名校验不可或缺的一个选择,本地校验和服务端校验双管齐下!

全局搜索 sinature,注释掉


第三、借助系统api判断应用调试状态和调试属性,最基础的防护!

1.android.os.Debug.isDebuggerConnected();这个就是判断当前应用有没有被调试

2.,这是根据menifest.xml中的debug = true等来判断

针对第二种,直接mprop,setprop ro.secure = 0;ro.debuggable = 1; 就可以调试任何进程了

 

第四、轮训检查android_server调试端口信息和进程信息,防护IDA的一种有效方式!

检测端口号

使用IDA动态调试APK时,android_server默认监听23946端口,所以通过检测端口号可以起到一定的反调试作用。具体而言,可以通过检测/proc/net/tcp文件,或者直接system执行命令netstat -apn等。

这里就涉及到需要修改IDA的连接端口了,默认的23946

两种方式:

1../android_server -p12345;直接加上-p参数即可,注意端口号和参数之间没空格:端口就变成12345了

2.一次性解决问题,使用IDA打开android_server,搜索特定的字符串,修改23946的三个地方,具体方案在 http://www.cnblogs.com/uestcbutcher/p/7300429.html 第二段的方式中


第五、轮训检查自身status中的TracerPid字段值,防止被其他进程附加调试的一种有效方式

这个可能要干掉轮训的线程

 

6.时间相关反调试

通过计算某部分代码的执行时间差来判断是否被调试,在Linux内核下可以通过time、gettimeofday,或者直接通过sys call来获取当前时间。另外,还可以通过自定义SIGALRM信号来判断程序运行是否超时。

 

7.检测关键文件

(1)/proc/pid/status、/proc/pid/task/pid/status

在调试状态下,Linux内核会向某些文件写入一些进程状态的信息,比如向/proc/pid/status或/proc/pid/task/pid/status文件的TracerPid字段写入调试进程的pid,在该文件的statue字段中写入t(tracing stop):

(2)/proc/pid/stat、/proc/pid/task/pid/stat

调试状态下/proc/pid/stat、/proc/pid/task/pid/stat文件中第二个字段是t(T):

(3)/proc/pid/wchan、/proc/pid/task/pid/wchan

若进程被调试,也会往/proc/pid/wchan、/proc/pid/task/pid/wchan文件中写入ptrace_stop

 

8.检测android_server、gdb、gdbserver

在对APK进行动态调试时,可能会打开android_server、gdb、gdbserver等调试相关进程,一般情况下,这几个打开的进程名和文件名相同,所以可以通过运行状态下的进程名来检测这些调试相关进程。具体而言,可以通过打开/proc/pid/cmdline、/proc/pid/statue等文件来获取进程名。当然,这种检测方法非常容易绕过――直接修改android_server、gdb、gdbserver的名字即可。

 

9.signal

具体查看 http://www.cnblogs.com/uestcbutcher/p/7250492.html 第四种

信号机制在apk调试攻防中有着非常重要的作用,大部分主流加固厂商都会通过信号机制来增加壳的强度。在反调试中最常见的要数SIGTRAP信号了,SIGTRAP原本是调试器设置断点时发出的信号

 

10.检测软件断点 具体查看 http://www.cnblogs.com/uestcbutcher/p/7250492.html 第5种

上一节说了使用SIGTRAP反调试的原理,由此可以衍生出另一种很常见的反调试方法――检测软件断点。软件断点通过改写目标地址的头几字节为breakpoint指令,只需要遍历so中可执行segment,查找是否出现breakpoint指令即可。

 

11.进程间通信


大部分加固会新建进程或者新建线程,在这些新建的线程和进程中完成反调试操作,然而如果这些进程、线程相对独立的话,很容易通过挂起、杀死的方式直接使得反调试失效。为了保证反调试线程、进程的存活,就需要一种通信方式,定期确认反调试线程、进程依然存活,所以进程间通信是高级反调试不可或缺的方式。在Linux下有很多进程间通信的方式,比如管道、信号、共享内存、套接字(socket)等

 

12.dalvik 虚拟机内部相关字段


在dalvik虚拟机中自带了检测调试器的代码,其本质是检测DvmGlobals结构体中的相关字段:


 

 

检测调试器的函数:

1
2
3
4
5
6
7
8
9
10
/*
 * static boolean isDebuggerConnected()
 *
 * Returns "true" if a debugger is attached.
 */
staticvoidDalvik_dalvik_system_VMDebug_isDebuggerConnected(constu4*args,JValue*pResult)
{
    UNUSED_PARAMETER(args);
    RETURN_BOOLEAN(dvmDbgIsDebuggerConnected());
}

本质是检测该dalvik虚拟机中DvmGlobals结构体中的调试器状态字段:

1
2
3
4
booldvmDbgIsDebuggerConnected()
{
    returngDvm.debuggerActive;
}

13.IDA arm、thumb指令识别缺陷


众所周知,IDA采用递归下降算法来反汇编指令,而该算法最大的缺点在于它无法处理间接代码路径,无法识别动态算出来的跳转。而arm架构下由于存在arm和thumb指令集,就涉及到指令集切换,IDA在某些情况下无法智能识别arm和thumb指令,比如下图所示代码:

bx r3指令会切换指令集,而参数r3是动态计算出来的,IDA无法失败r3的值,而默认将bx r3后面的指令当成跳转地址,将后面地址的指令识别成了arm指令,而实际上其仍为thumb指令。

在IDA动态调试时,仍然存在该问题,若在指令识别错误的地点写入断点,有可能使得调试器崩溃

 

14.Ptrace


Ptrace是gdb等调试器实现的核心,通过ptrace可以监控、控制被调试进程的状态、信号、执行等。而每个进程在同一时刻最多只能被一个调试进程ptrace,根据这个原理,可以主动ptrace自己的关键子进程,这样可以在一定程度上防止子进程被调试。

为了防止fork出来的反调试子进程被直接挂起或杀死,可以通过Ptrace的PTRACE_PEEKTEXT、PTRACE_PEEKDATA、PTRACE_POKETEXT等参数来完成父子进程之间的通信,比如子进程中使用的解密密钥先存于父进程空间,父进程往ptrace的子进程中写入密钥后,再解密出关键数据。

总之,通过ptrace增加父子进程之间的联系,是十分有效并且广泛存在于各类加固的反调试方法

 

15.Inotify 监控文件


在Linux下,inotify可以实现监控文件系统事件(打开、读写、删除等),加固方案可以通过inotify监控apk自身的某些文件,某些内存dump技术通过/proc/pid/maps、/proc/pid/mem来实现内存dump,所以监控对这些文件的读写也能起到一定的反调试效果。

posted @ 2017-08-07 23:24  sudenbutcher  阅读(1065)  评论(0)    收藏  举报