1 背景
反调试,在我们脱壳的第一步。反调试虽然不能完全阻止攻击者,但是还是能加大攻击者的时间成本,一般与加壳结合使用,核心还是加壳部分。
反调试可以分为两类:一类是检测,另一类是攻击。
本文主要是对Android逆向中的反调试进行案例分析。
2 so逆向之IDA绕过AliCrackme反调试
2.1 Jdax反编译APK文件
来自阿里聚安全的AliCrackme.apk,如图1所示,随机输入密码ihui后报错验证码校验失败。
通过Jadx反编译APK并分析后,发现调用了校验逻辑Java层的securityCheck()对应的SO库crackme,如图2所示。
2.2 IDA静态分析
使用IDA打开APK解压出来的libcrackme.so文件,在模块列表中查找JNI_OnLoad或者JAVA_函数,发现二者皆有。
因此,我们首先分析动态注册函数JNI_OnLoad()函数的伪C代码。
根据伪代码分析逻辑,我们想找到动态注册函数RegisterNative,但是这里没有,最后一个函数调用了GetEnv,也就是说,Java的调用方法并不是通过动态注册的而是通过静态注册的,如图3所示。
接下来,我们分析静态注册函数securityCheck。
先根据头文件jni.h和基本经验修改函数的形参,分析代码运行逻辑知将input_str作为函数GetStringUTFChars的参数传进去并返回值给v5,接着在while循环中对比v6和v7即注册码和输入字符串,如果不相等直接break,然后返回0,如图4所示。
因此,我们就可以进行动态调试程序,在while循环中的适当位置加断点即可获得注册码。
2.3 IDA动态调试
通过反复调试发现在图5中断点出BLX R7的位置跳出报错FFFFFFFF,此处应该就是就是反调试检测位置了,如图5所示。
F7进来该函数,出现pthread_create新建一个线程不停的检测TracerPid这个字段是否不为0,不为0,就立即退出程序,如图6所示。
我们可以通过F2修改BLX R7为00 00 00 00直接替换掉该指令,因为so文件有固定格式,删除指令后多段内容的偏移值容易发生错乱,如图7所示。
在静态注册方法securityCheck()的while循环的开头处下断点,然后取消勾选三项,F9到断点后在手机上输入ihui并点击按钮,在IDA的[R0]处会出现输入的字符串ihui,如图8所示。
然后双击[R2]并按A键,将数据转化为字符串,查看v6的值即为注册码aiyou,bucuoo,如图9所示。
最后,重新打开APP输入注册码即可破解成功,其实这里就是将正确的字符和我们自己输入的字符进行比较。
2.4 另解:frida hook native层静态注册方法
之前的解法就是先nop掉JNI_OnLoad中的反调试,然后再动态调试到securitycheck方法中获取相关寄存器的值。图4中将输入的字符串与v7进行比较,v7又来自于v6,v6的值等于off_628c。
当然,我们已经知道这是错误的值,在动态运行的过程中会对其进行处理,但是我们可以hook这个偏移值。
首先通过导出表函数获取securitycheck函数的值,然后根据函数值算出so基址0x11A8,加上0x628c偏移接着使用readPointer读取地址,readUtf8String读取地址中的值,具体的代码如下:
1 | import frida, sys |
这种解法也是基于之前IDA动静分析so文件后得出的,如图10所示,可以说是殊途同归。
3 so逆向之IDA绕过FindTracer反调试
3.1 Jdax反编译APK文件
打开APP后,发现有一个信封的图标,点击后出现Everything fine.的提示信息,如图11所示。
使用Jdax打开FindTracer.apk后,通过分析AndroidManifest.xml后进而到MainActivity中,发现了程序的运行逻辑以及FindTracer类下面在so层实现的findTracer()函数,如图12所示。
3.2 IDA静态分析
使用IDA打开APK解压出来的libFindTracer.so文件,在模块列表中查找JNI_OnLoad或者JAVA_函数,发现只有JNI_OnLoad。
因此,我们分析动态注册函数JNI_OnLoad()函数的伪C代码。
根据伪代码分析逻辑,我们想找到动态注册函数RegisterNative,但是这里没有。在So load success处的else处的sub_147c()函数内部有FindClass和RegisterNatives的调用,如图13所示。
接下来,我们分析findTracer()函数,该函数是对反调试进行检测,如果出现反调试迹象即返回1,进而导致so加载失败,点击信封后提示信息会由Everything fine.变成Fuck, a tracer has been found!!!。
1 | bool findTracer() { |
因此,我们就可以进行动态调试程序,修改findTrace()的4个反调试检测函数的返回值为0即可绕过反调试。
3.3 IDA动态调试
通过3.2节的静态分析后,我们先进行动态调试前的准备工作,然后依次修改findTrace()的4个反调试检测函数的返回值为0,修改方法是在汇编代码处右键->Keypatch->Patcher,将MOVS R2, #1修改为MOVS R2, #0,依次类推,如图14所示。
最后,取消勾选三项,F9运行程序,点击信封处会出现Everything fine.的提示信息,成功绕过反调试。
4 参考文献
[1]https://mp.weixin.qq.com/s/Mz-zumsSICFuGoNj6C0cFg
[2]https://blog.csdn.net/YJJYXM/article/details/110857185
[3]https://blog.csdn.net/weixin_42011443/article/details/105897429
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,可以邮件至 xingshuaikun@163.com。