ida动态调试破解alicrackme与反调试对抗(代码片段)

Tr0e Tr0e     2023-01-15     741

关键词:

前言

在前面的文章中 IDA动态调试破解EXE文件与分析APK流程 介绍了 IDA 对 APK 进行动态调试分析的简单流程,然而实际上很多 APP 为了防止被动态调试分析,经常会做一些反调试的防护措施。本文将通过 2014 年阿里安全挑战赛的第二题 AliCrackme_2(APK下载地址),来进一步学习 APK so 文件的动态调试和反调试技术的对抗。

APK破解(上)

显然,上面的程序希望我们输入正确的验证码/密码,故破解的目标就是获取到正确的密码(即 Flag 值)。

1.1 静态分析

1、先拖进 jadx 查看其验证码校验逻辑,发现程序调用了 libcrackme.so 的 securityCheck() 函数对输入密码进行校验,如果正确则启动一个新的活动,否则则提示验证码校验错误:
2、将 libcrackme.so 文件导出并拖进 IDA 进行静态分析:
3、按 F5 转换成伪代码:
发觉 v6 变量 off_628C 可能有东西,双击跟进看看:

4、进行 JNI 函数方法名还原,可以较清晰地分析出程序的逻辑:
C 伪代码的解释我已添加在备注中,可以看出接下来的目标就是通过 IDA 动态调试来获取真实的 v6 变量(真实的密码)的值。

1.2 动态分析

1、在 securityCheck() 函数如下位置设置断点:
2、在真机 Nexus5 中安装目标 APK,运行 IDA 调试服务并转发端口:
3、设置 IDA Debugger:

4、启动 APP 并在 IDA 进行进程附加:

5、然而发现附加进程后,一点击运行程序按钮(或者 F9 快捷键),程序立马闪退……
可以猜测该 APP 使用了反调试机制防止程序被动态调试分析,想进一步调试分析,必须绕过其反调试机制。

反调试对抗

接下来来通过 IDA 动态调试分析 APP 的反调试检测机制,并进行对抗和破解,使得目标 APP 能够被我们正常地调试。

2.1 Ptrace

【反调试原理】IDA 是使用 android_server 在 root 环境下注入到被调试的 APP 程序进程中,那么对抗调试就可以利用 Linux 系统的 ptrace 来实现,当 Android 应用被调试时应用内存里的 TracerPid 字段就不为 0(即在其 status 文件中有一个字段 TracerPid 可以标识是被哪一个进程 trace 了),只要是不为 0 的时候,就会直接的退出程序,达到反调试的目的。

来验证一下上述调试过程中,真机上目标 APP 进程的 ptrace 字段:

进入设备:adb shell
获取Root权限:su
获得APP的进程ID:ps | grep 软件的包名
查看进程的信息及TracerPid值: cat /proc/进程ID/status

结果如下,可以看到程序进程被进程 id 为 16453 的进程所附加:
Linux 系统中 ptrace 系统调用提供了一个进程 (tracer) 可以控制另一个进程 (tracee) 运行的方法,并且 tracer 可以监控和修改 tracee 的内存和寄存器,主要用作实现断点调试和系统调用追踪。具体可参见看雪的文章:[原创] Linux ptrace详细分析系列(一),此处不展开介绍。

2.2 全局调试

既然目标 APP 在 so 文件进行了反调试,那么要想破解反调试机制,就需要对 so 文件进行调试(静态分析你要是能分析出来也行……)。但是前面的调试过程已经以失败告终,无法正常运行程序,那该怎么办?

进行常规 IDA 调试过程,只要一运行目标 APP 程序就会强制退出了调试界面,说明目标 APP 循环检测被调试状态的函数执行的时机非常早。幸运的是,有两个地方是 so 动态加载完毕前执行的:

  1. .init_array 是一个so最先加载的一个段信息,时机最早,现在一般 so 解密操作都是在这里做的;
  2. JNI_OnLoad 是 so 被 System.loadLibrary 调用的时候执行的,它的时机早于 native 方法的执行。

那么知道了这两个时机,下面我们先来看看是不是在JNI_OnLoad函数中做的策略,所以我们需要先动态调试JNI_OnLoad函数。我们既然知道了 JNI_OnLoad 函数的时机,如果阿里把检测函数放在这里的话,我们不能用之前的方式去调试了,因为之前的那种方式时机太晚了,只要运行就已经执行了 JNI_OnLoad 函数,所以就会退出调试页面。

1、接下来我们就尝试断在 JNI_OnLoad 函数指令处,先在 JNI_OnLoad 函数设置断点并在 debugger 设置中勾选如下选项:
2、但是由于被调试程序一运行就会执行 static 中的语句,因此需要让程序停在加载 so 文件之前,这里可以添加 watiForDebugger,或者使用更加简单的方法,使用 debug 方式来启动 APP:

adb shell am start -D -n com.yaotong.crackme/.MainActivity 

运行该命令后,目标 APP 将处于一个等待 Debugger 的状态:

3、IDA 开始对进程进行附加:

4、接着执行如下命令让 APP 运行起来:

进入设备: adb shell
获取Root权限:su
获得APP的进程ID:ps | grep com.yaotong.crackme (直接关注上一步进程附加的 ID 也可以获得)
监听进程的ID:adb forward tcp:8700 jdwp:44954495为上面获得的APP进程ID)
运行进程:jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700

执行过程如下:
5、返回 IDA 按 F9 开始运行程序:
此时 APP 由等待 Debugger 状态进入如下页面:

6、继续按一下运行程序的按钮(或者 F9 快捷键),程序即会运行到刚才我设置的 JNI_OnLoad 函数断点处:
【注意】程序没有直接闪退的原因是此处的断点位置在检测调试的代码前面,故我们接下来就可以往下逐步运行程序来调试分析反调试的代码的逻辑和位置!

7、在进一步进行调试分析反调试检测代码前,我想补充一个点,实际上上面加载 JNI_OnLoad 函数断点的调试过程我在首次执行时失败,执行jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700命令时遇到如下报错:
报错的原因是目标 APP 不可被调试!解决的办法是修改 APP AndroidMenifest.xml 文件, 在 <application 里给APP加上可调试权限,android:debuggable="true",重新打包 APP 后重现编译、签名、安装,如下图所示:
8、可见在 Android 真机上调试程序有一个前提,就是这个 apk 包必须有 debuggable=true 的属性才行,而除了自己开发的 apk 能够控制打包属性之外,其他的程序发行之后显然不会设这个值为 true 的(打包 APK 时 release 发布版本默认为 false 不可调试,只有 debug 版本可调试)。为了调试 APK 而每次都去修改配置文件并重打包也太麻烦了,实际上为了调试这些第三方的 apk,我们可以从整个手机系统入手 —— 因为除了每个 apk 中的 debuggable 标志以外,这个标志还可以在系统中全局指定,换句话说,只要把系统里的 debuggable 值设为 true(即将/default.prop中默认字段是 0 的ro.debuggable的值修改为 1,前提是手机需要 root ),那么不管 apk 的这个属性是什么值,该手机上的 APP 都可以被调试了。修改 ro.debuggable 的步骤:

下载 mprop 工具:https://github.com/wpvsyou/mprop
adb push mprop /data/local/tmp # 将下载的mprop 放入 /data/local/tmp 当中
adb shell
su
cat default.prop | grep debug # 查看default.prop里面的配置值,此处是 0
getprop ro.debuggable         # 获取ro.debuggable 此处应该是 0
cd /data/local/tmp
chmod 755 mprop               # 修改权限
./mprop ro.debuggable 1       # 修改 ro.debuggable 1 的值为 1
cat default.prop | grep debug # 查看default.prop里面的配置值,此处是应该还是 0
getprop ro.debuggable         # 获取 ro.debuggable 此处应该是 1

ok,修改完成后,再次看下是不是可以调试了,执行命令adb shell getprop | findstr debuggable查看手机的ro.debuggable参数值:

再次强调】上面修改ro.debuggable参数值的过程需要在已 root 的手机中进行,同时手机再次重启后需要再次进行一次修改操作。

2.3 反反调试

好了,介绍完将真机设置全局可调试的模式后,我们返回到 JNI_OnLoad 函数断点处,继续分析程序检测调试状态的代码逻辑。

此处先小结一下上述在 JNI_OnLoad 函数设置断点并拦截程序的过程(即 IDA 调试具有反调试机制的程序的步骤):

1)启动 android_server
2)端口转发adb forward tcp:23946 tcp:239463)打开 IDA,设置 JNI_OnLoad 函数断点并在 debug options 中设置 load so 的时机;
4)adb shell am start -D -n 包名/类名;出现 Debugger 的等待状态;
(说明:以启动模式启动,是停在加载so文件之前,包名可以在 Androidmanifest 文件中找到)
5)IDA Debugger 附加上对应的进程(留意目标 APP 的进程 ID);
6)运行命令:
  adb forward tcp:8700 jdwp:44954495为上面 IDA 附加的目标APP进程ID)
  jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700
7)点击 IDA 运行按钮,或者F9快捷键。

1、按 F8 单步步过,继续往下运行程序,但是当我们每次到达 BLX R7这条指令执行完之后,JNI_OnLoad 就退出了:

2、再次进入调试,看见运行到 BLX 跳转指令时 R7 寄存器中是 pthread_create 函数,这个是 Linux 中新建一个线程的方法:
所以猜测阿里的反调试就在这里开启一个线程进行轮训操作,去读取/proc/[pid]/status文件中的 TrackerPid 字段值,如果发现不为 0,就表示有人在调试本应用,在 JNI_OnLoad 中直接退出。其实这里可以再详细进入查看具体代码实现的,但是这里限于篇幅问题,不详细解释了,后续将单独写一篇文章学习反调试机制,本文的重点是能够动态调试即可。

3、定位到关键的反调试检测代码位置后,该怎么绕过反调试检测?其实很简单,我们只要把BLX R7这段指令干掉即可,如果是 smali 代码的话,可以直接删除这行代码即可,但是 so 文件不一样,它是汇编指令,如果直接删除这条指令的话,文件会发生错乱,因为本身 so 文件就有固定的格式,比如很多 Segement 的内容,每个 Segement 的偏移值也是有保存的,如果这样去删除会影响这些偏移值,会破坏 so 文件格式,导致 so 加载出错的。所以这里我们不能手动的去删除这条指令,我们还有另外一种方法,就是把这条指令变成空指令,在汇编语言中,nop 指令就是一个空指令,它什么都不干,所以这里我们直接改一下指令即可,ARM 汇编中对应的 nop 指令是:00 00 00 00。首先恢复到静态分析的状态(退出动态分析),将鼠标定位到BLX R7指令处,切换到 “Hex View-1” 窗口可以看到该指令对应的十六进制表示形式为 37 FF 2F E1

4、继续选中BLX R7指令然后选择如下“Change byte…”选项:
修改37 FF 2F E100 00 00 00

5、最后将所作的修改保存到 so 文件中( IDA 中修改 SO 文件保存修改覆盖到 so 文件而非临时工程文件的方式:edit->patch program-> apply patches to input file... ):
至此已完成自毁程序 so 文件的修改,使得反调试的检测核心逻辑代码被破坏,新的 so 文件中将无法正常调用反调试检测代码。

APK破解(下)

完成上述工作,我们来将新的 so 文件替换掉原来自毁程序 APK 里面的 so 文件,并尝试对其正常的动态调试分析。

3.1 重新编译

1、将上面获得的新的 so 文件替换掉原来自毁程序 APK 里面的 so 文件:

2、在 AndroidKiller 中重新编译、打包、重签名自毁程序 APK:

接着将新的自毁程序 APK 一键安装到 Nexus5 测试机即可。

3.2 动态调试

新的自毁程序 APK 讲道理可以正常地被 IDA 动态调试分析而不会闪退了,下面来试试。

1、直接双击开始运行 APP 程序(无需再将 APP 处于一个等待 Debugger 的状态),然后正常运行 android_server 并转发 23946 端口,然后 debugger 勾选如下选项即可:

2、取消 JNI_OnLoad 函数的断点(该函数反调试的逻辑已被破坏,无需再拦截并调试该函数),直接在 securityCheck() 函数设置如下断点:
3、IDA 开始正常附加 APP 程序进程,点击运行按钮或 F9 继续运行程序,程序不再闪退!!!!往输入框输入 1234 字符串并提交,发现程序被成功拦截在刚才设置的断点处了:
4、切换到 IDA View-PC 窗口查看断点并 F8 单步步过运行程序,观察到 if 判断语句对应的汇编指令 R2 寄存器存放的字符串为 “aiyou,bucuoyoo”,也 就是我们想要的目标密码/flag:
5、将 flag 值输入自毁程序,成功破解密码:

总结

本文篇幅相对较长,但也算学习了不少东西。从如何设置测试机真机全局可调试、再到 APK 反调试机制的动态分析和绕过、最后再到重编译程序并动态调试破解密码,整个实践流程下来耗费了不少时间(太菜了)……最后说一下,Android 反调试机制不局限于本例所提及的 TracerPid 字段检测,后面我将继续单独对 Android 反调试机制及其对抗机制进行学习。本文至此 Over!!!

本文参考文章:

  1. [原创]超级详细的实战分析一个Crackme的过程(很细致,推荐);
  2. 安卓逆向实践5——IDA动态调试so源码(分析很到位);
  3. Android逆向之旅—动态方式破解apk进阶篇(IDA调试so源码)(姜维大佬的文章)。

android签名验证与反调试机制的对抗技术(代码片段)

...目录前言签名验证1.1签名机制1.2签名验签1.3签名绕过反反调试2.1tracerPid检测2.2进程名称检测2.3关键文件检测2.4调试端口检测2.5ptrace值检测2.6时间差异检测2.7内置函数检测2.8调试断点检测总结前言Android的APK文件为了防止被篡改和... 查看详情

android签名验证与反调试机制的对抗技术(代码片段)

...示用户或者直接退出运行。同时有些APP为了防止被攻击者动态调试和分析,还做了反调试机制。本文来学习记录下Android签名验证机制与反调试机制的实现原理和其对抗技术。签名验证Android系统使用JAR包的签名机制对APK进行... 查看详情

安卓ida动态调试总结

安卓ida动态调试总结不出问题的理想步骤(带反调试的版本)需要的软件:ida7.0/6.8ddms(或者sdk/tools/monitor.bat)root真机一部(因为android_server是基于arm架构,而大多数模拟器基于x86,所以最好是真机)或者AVD模拟器待调试apk(test.a... 查看详情

ida动态调试破解exe文件与分析apk流程(代码片段)

...APK2.1IDA调试步骤2.2Apk调试实例IDA调试EXE3.1IDA静态分析3.2IDA动态调试总结前言在前一篇文章:JEB动态调试与篡改攻防世界Ph0en1x-100中介绍了如何借助JEB调试工具对APK的smali源码进行调试分析,本文主要来看如何使用IDA来调试A... 查看详情

逆向分析反调试程序

1.先运行下正常2.打密码密码错误3.用OD调试发现报异常说明做了反调试(OD不要用插件不然他会反反调试看不到)4.(这里要用到win7了因为win10报异常不知道在哪里)win10效果win7效果可以看出异常地址在哪里5.知道异常地址在哪了... 查看详情

ida动态调试破解exe文件与分析apk流程(代码片段)

...APK2.1IDA调试步骤2.2Apk调试实例IDA调试EXE3.1IDA静态分析3.2IDA动态调试总结前言在前一篇文章:JEB动态调试与篡改攻防世界Ph0en1x-100中介绍了如何借助JEB调试工具对APK的smali源码进行调试分析,本文主要来看如何使用IDA来调试A... 查看详情

反调试技术常用api,用来对付检测od和自动退出程序

在调试一些病毒程序的时候,可能会碰到一些反调试技术,也就是说,被调试的程序可以检测到自己是否被调试器附加了,如果探知自己正在被调试,肯定是有人试图反汇编啦之类的方法破解自己。为了了解如何破解反调试技术... 查看详情

javascript奇淫技巧:反调试

...享几种JS代码反调试技巧,目标是:实现防止他人调试、动态分析自己的代码。检测调试,方法一:用console.log检测代码:varc=newRegExp("1");c.toString=function()alert("检测到调试")console.log(c);原理:当console.log输出调试信息时,会将非字... 查看详情

检查进程是否被调试

...:http://www.cnblogs.com/this-543273659/archive/2013/03/04/2943380.html在调试一些病毒程序的时候,可能会碰到一些反调试技术,也就是说,被调试的程序可以检测到自己是否被调试器附加了,如果探知自己正在被调试,肯定是有人试图反汇编... 查看详情

过反调试

重所周知,有破解就必有防破解,二者本为一体破解技术就不要我多介绍了,下面我来介绍反调试技术也就是所谓的防破解技术反调试技术可以简单通俗的理解为:防止OD分析软件的技术,也就是反调试技术那么反调试技术又有... 查看详情

java层反调试

安卓程序动态调试需要满足两个条件。1.在AndroidMainfest.xml文件中,在application标签下,Android:debuggable=true。2.系统默认调式,在build.prop(boot.img),ro.debugable=1。一:实例演示java层反调试以“百度加固”为例。1.将样本拖入jdax-gui中,... 查看详情

windows下反(反)调试技术汇总(代码片段)

反调试技术,恶意代码用它识别是否被调试,或者让调试器失效。恶意代码编写者意识到分析人员经常使用调试器来观察恶意代码的操作,因此他们使用反调试技术尽可能地延长恶意代码的分析时间。为了阻止调试器的分析,当... 查看详情

利用异常实现反调试(代码片段)

0×01介绍一些文章已经介绍过通过检测异常来对抗调试器的技术。这个思想很简单:根据设计本意,调试器会处理特定的异常。如果一个异常包裹在try块中,只有当没有附加调试器的时候,异常处理程序才会执行。因此,可... 查看详情

ios逆向笔记之反调试以及反反调试和反反反调试ptrace篇

...考技术A1.ptrace(processtrace进程跟踪)为了方便软件的开发和调试,UNIX早期版本就提供了一种对运行进程进行跟踪和控制的手段,那就是系统调用ptrace.通过ptrace可以实现对另一个进程实现调试和跟踪.同时,ptrace提供了一个非常有用的参... 查看详情

ollydbg动态调试与逆向破解traceme.exe

...捷操作TraceMe调试分析破解校验总结前言OllyDbg是一个新的动态追踪工具,是将IDA与SoftICE结合起来的产物,Ring3级调试器,非常容易上手,另外由于OllyDbg是一个通用的32位汇编分析调试器且操作界面非常直观简单ÿ... 查看详情

javascript加密代码反调试

JavaScript奇技淫巧:加密JS代码反调试JS代码混淆加密,已被很多人使用,因为它真的很有用、很实用,可以用于保护代码、防护分析、复制、盗用,还可以用于小游戏过审、APP加固等方面。混淆加密后的JS代码,可能被他人分析... 查看详情

javascript加密代码反调试

JavaScript奇技淫巧:加密JS代码反调试JS代码混淆加密,已被很多人使用,因为它真的很有用、很实用,可以用于保护代码、防护分析、复制、盗用,还可以用于小游戏过审、APP加固等方面。混淆加密后的JS代码,可能被他人分析... 查看详情

《逆向工程核心原理》学习笔记:反调试技术(代码片段)

...emInformation()4、NtQueryObject()5、ZwSerInformationThread()6、ETC三、动态反调试技术1、异常(1)SEH(2)SetUnhandledExceptionFilter()2、TimingCheck3、陷阱标志4、0xCC探测(1)API断点(2)比较校验和四、高级反调试技... 查看详情