java层保护
1 代码混淆
apk应用在被逆向分析时,java层代码就像被扒光一样,而native代码分析难度大,但是需要扎实的c/c++基础。这个时候大家就可以考虑一下代码混淆技术稍作保护(毕竟看着好多abc也挺烦的)。
Android开发中提供了Proguard这一工具来进行代码混淆。
这里只做简单介绍,Proguard是一个开源项目,他能够对Java类中的代码进行压缩(Shrink),优化(Optimize),混淆(Obfuscate),预检(Preveirfy)。
- 压缩(Shrink):在压缩处理这一步中,用于检测和删除没有使用的类,字段,方法和属性。
- 优化(Optimize):在优化处理这一步中,对字节码进行优化,并且移除无用指令。
- 混淆(Obfuscate):在混淆处理这一步中,使用a,b,c等无意义的名称,对类,字段和方法进行重命名。
- 预检(Preveirfy):在预检这一步中,主要是在Java平台上对处理后的代码进行预检。
- Proguard在Android Studio中都已经内置,每次新创建一个工程时,都会自动在gradle中被添加。
2调试器检测
利用Java层API来判断Android程序是否是处于被调试下。进行动态调试的时候,需要进行jdb连接:jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700,当接连成功之后,android.os.Debug.isDebuggerConnected()这个方法就会ture。这就跟系统的android.os.Debug.isDebuggerConnected()方法有关。解决也很简单,使用反编译工具方便程序,然后搜索isDebuggerConnected() 关键字, 然后Nop 改掉函数的调用即可。还有就是判断当前应用属性:ApplicationInfo.FLAG_DEBUGGABLE。在反编译后AndroidMainfiest,xml 中添加:android:debuggable=“true”。
native层
1、 ptrace检测
每个进程同时刻只能被1个调试进程ptrace,再次p自己会失败。所以可以主动ptrace自己,根据返回值判断自己是否被调试了;或者多进程ptrace。
2进程名检测
远程调试要在手机中运行android_server gdbserver gdb等进程,遍历进程,查找固定的进程名,如果能找到就说明调试器在运行。
有的时候不使用apk附加调试的方法进行逆向,而是写一个.out可执行文件直接加载so进行调试,这样程序的父进程名和正常启动apk的父进程名是不一样的。正常启动的apk程序父进程是zygote。如果不是,则证明为调试状态。还有就是自身进程名,正常进程名一般是apk的com.xxx这样的格式。
3apk线程检测
正常apk进程一般会有十几个线程在运行(比如会有jdwp线程),自己写可执行文件加载so一般只有一个线程,可以根据这个差异来进行调试环境检测
4安卓系统调试检测函数
这个跟上面有点重复。分析android自带调试检测函数isDebuggerConnected()在native的实现, 尝试在native使用。
(1)dalvik模式下:
找到进程中libdvm.so中的dvmDbgIsDebuggerConnected()函数,调用他就能得知程序是否被调试。dlopen(/system/lib/libdvm.so)dlsym(_Z25dvmDbgIsDebuggerConnectedv)
(2)art模式下:
art模式下,结果存放在libart.so中的全局变量gDebuggerActive中,符号名为_ZN3art3Dbg15gDebuggerActiveE。但是貌似新版本android不允许使用非ndk原生库,dlopen(libart.so)会失败。所以无法用dalvik那样的方法了。
5轮询检测法
轮询TracePid
这个主要是参考了https://www.cnblogs.com/jiaoxiake/p/6790803.html,可以进去了解下。
6 断点检测
如果函数被下软件断点,则断点地址会被改写为bkpt指令, 可以在函数体中搜索bkpt指令来检测软件断点。