1 背景
复习过一阵后那种感觉又回来了😄😄😄,学过的东西还是得经常回顾,这样才能做到温故而知新,进而为师!!!
在分析经过加密混淆的Android程序时,仅使用静态分析技术往往达不到理想的效果,这时候就轮到动态分析技术出场了👏👏👏
动态分析分为动态跟踪与动态调试。动态跟踪侧重于自动化分析,工具的表现形式一般是自主研发或由第三方提供的分析平台。在软件开发领域编写大型项目安全检测分析报告,以及在软件安全领域进行恶意代码与病毒的分析时,会广泛使用动态分析技术。动态调试需要分析人员参与进来,依靠调试器的能力完成分析工作。在进行动态调试时,除了调试器,还需要分析人员自主确定分析点。在开发软件时,一般可以进行源码级调试,对设置断点的地方可以通过阅读源码找到。在进行逆向分析时,通常只能进行反汇编级别的调试,分析人员需要通过阅读大量的反汇编代码来寻找突破口。
2 动态分析框架
2.1 简介
目前常用的开源跨平台动态分析框架是MobSF(下载地址为:https://github.com/MobSF/Mobile-Security-Framework-MobSF),同时支持Android、IOS、Windows平台上的软件分析,也同时支持静态分析、动态分析及Web API测试。
MobSF支持在Windows10、Ubuntu、macOS上进行安装,安装过程中还需要其他的辅助软件,安装操作可以参考MobSF的手册和相关经验。
- https://mobsf.github.io/docs/#/zh-cn/
- https://cloud.tencent.com/developer/article/1858069#:~:text=%E5%AE%89%E8%A3%85%E6%AD%A5%E9%AA%A4%20%E4%B8%8B%E8%BD%BD%E5%A5%BD%E9%A1%B9%E7%9B%AE%E4%B9%8B%E5%90%8E%EF%BC%8C%E5%8F%AF%E4%BB%A5%E9%87%8D%E5%91%BD%E5%90%8D%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6%E5%A4%B9%E5%90%8D%E7%A7%B0%20MobSf%2C%20%E6%89%93%E5%BC%80%20cmd%20%E7%AA%97%E5%8F%A3%E8%BF%9B%E5%85%A5%E8%AF%A5%E9%A1%B9%E7%9B%AE%E7%9B%AE%E5%BD%95%E3%80%82,%E5%B0%86%E9%A1%B9%E7%9B%AE%E5%86%85%E7%9A%84%20requirements.txt%20%E6%89%93%E5%BC%80%EF%BC%8C%E6%9C%80%E5%90%8E%E4%B8%80%E8%A1%8C%20libsast%3D%3D1.2.2%20%E6%94%B9%E4%B8%BA%20libsast%3D%3D1.3.4
在安装过程中,会向设备中安装MobSF使用的一些组件,包括用于抓取网络流量的HTTPS证书、Xposed框架,以及Droidmon、RootCloak、JustTrustMe等Xposed模块。
MobSF已经提供了对Android 4.4的支持,而且设备必须被Root过。
2.2 启动MobSF
在完成2.1中的步骤后,我首先使用命令emulator -avd Pixel_XL_API_27 -writable-system -no-snapshot启动Android Studio模拟器,其中-writable-system表示使用在模拟会话期间创建可写系统映像,-no-snapshot表示禁止自动加载和保存操作,从而导致模拟器执行完整启动序列并在关闭时丢失状态。一切都会在运行时自动配置,MobSF需要AVD版本5.0到9.0进行动态分析,官方建议使用Android 7.0及更高版本,启动后的模拟器如图1所示。
接着,在git下来的MobSF文件夹下运行run.bat 127.0.0.1:8000,在浏览器中输入http://localhost:8000/即可打开Web界面,如图2所示。
然后,上传APK后即可进行体验MobSF的其他强大功能,如图3所示。
3 动态分析技巧
主流的动态分析框架除了提供一套完整、通用的动态分析方法,通常还具备高度的可配置特性与可扩展性,例如添加自定义分析模块、增加指定的API分析等功能。但对一些应用来说,还是有一些分析要点没有提供。在这个时候,就需要分析人员动手解决这些问题。
3.1 代码注入法
一个程序在发布时通常不会保留Log输出信息。如果想观察程序特定位置的输出信息,在默认情况下,使用动态分析框架是做不到的,需要手动进行代码注入。代码注入是指先反编译Android程序,然后在反汇编出来的smali文件中添加Log调用的代码,最后重新打包程序并运行,查看输出结果。
3.1.1 实例程序
本节实例为一个注册码验证模拟程序,如图4所示。输入用户名与注册码后,点击“注册”按钮,程序会判断注册码是否正确,并弹出相应的提示信息。
3.1.2 Jadx分析APK
首先,我们将APK文件拖入Jadx中,并找到AndroidManifest.xml文件,发现只有一个MainActivity,如图5所示。
接着,进入到com.ihui.sn文件夹下,查看MainActivity.java文件,如图6所示。
经过分析后得知,只需要在反编译后的dex文件的第42行添加Log调用代码即可获得注册码。
3.1.3 Apktool反编译APK
使用Apktool对APK文件进行反编译,,如图7所示。
对输出文件进行分析,找到按钮点击事件处理代码。在MainActivity$1.smali文件的42行中,我们发现了与Jadx分析后的MainActivity.java文件中的特征函数equalsIgnoreCase,如图8所示。
仔细阅读图8中的反汇编代码,会发现.line 42处的if-eqz p1, :cond_1是程序的关键部分。该部分通过比较v0(输入的注册码)与p1(计算所得的注册码)的值来判断注册码是否正确。因此,要想获取真实的注册码,我们只需要根据寄存器的合理范围在.line 42处添加Log.v()输出p1寄存器的值。加入反汇编代码后的MainActivity$1.smali文件如图9所示。
3.1.4 利用logcat查看正确注册码
首先,使用apktool b outdir命令对修改后的代码重新打包,然后使用java -jar signapk.jar iHui.x509.pem iHui.pk8 test.apk test-signed.apk命令对打包后的apk进行签名。
接着,在命令行中输入adb logcat -s SN:v命令,准备获取正确的注册码,如图10所示。
将签名后apk文件安装到手机中,并点击注册按钮后,会在命令行中出现图11中的正确注册码。
在手机上将apk卸载,并安装之前正确版本的apk文件,输入用户名iHui,注册码90b94c7569fe4a0dec2b5ee4598097e9即可注册成功,如图12所示。
【扩展】
- 其实这里的注册码就是根据用户名计算出来的MD5值;
- 我们也可以使用Android Killer在合理的位置注入Toast函数进而获取注册码;
- 我们还使用Android Killer在合理的位置注入Log函数并查看日志进而获取注册码。
3.2 栈跟踪法
尽管LogCat配合代码注入的方法在分析程序时屡试不爽,但需要分析人员阅读大量的反汇编代码来寻找程序的“输出”点,且在此过程中可能需要多次手动注入Log输出代码。如果要分析大型程序,这显然是一件苦差事。
因此,我们使用栈跟踪法来进行代码注入,主要的操作是手动向反汇编后的smali文件中添加用于输出栈跟踪信息的代码。与注入Log输出的代码不同的是,采用栈跟踪法时只需要知道大概的代码注入点,而且注入代码后的反馈信息比Log注入要详细得多。
3.2.1 实例程序
如图13所示,运行实例程序stackTrace,弹出来Toast,提示who called me?
3.2.2 修改关键代码
现在我们的需求是确认这个Toast是什么时候被调用的。使用ApkTool反编译stackTrace实例程序,然后查找特征函数Toast的调用位置或者提示字符串的位置,发现在MainActivity.smali文件中有图14中的代码。
通过分析图13中代码可知,Toast是由.line 27处的代码调用的,现在只需要在它的下面添加用于输出栈跟踪信息的代码,相应的Java代码如下:
new Exception(“print stacktrace……”).printStackTrace();
将其转化为smali语法的反汇编代码,具体如下:
new-instance v0, Ljava/lang/Exception;
const-string v1, “print stacktrace……”
invoke-dircet {v0, v1}, Ljava/lang/Exception;->(Ljava/lang/String;)V
invoke-virtual {v0}, Ljava/lang/Exception;->printStackTrace()V
将上述smali代码写入MainActivity.smali的.line 27后,如图15所示。
3.2.3 栈跟踪分析
首先,使用ApkTool将修改完的代码重新打包成APK,并进行签名。栈跟踪信息是WARN级别的信息,且Tag被系统命名为System.err,因此,在命令行中输入adb logcat -s System.err:V *:W命令,也会输出同样的栈跟踪信息,输入结果如图16所示。
栈跟踪信息记录了从程序启动到printStackTrace()方法执行期间所有被调用过的方法。从下往上查看栈跟踪信息,找到第1条以com.ihui.stacktrace开头的信息,发现开始调用的是OnCreate()方法,然后依次是a()、b()、c()。这样一来,我们就得知了函数的执行流程。
3.3 Method Profiling
3.3.1 实例程序
本节的演示示例模拟多级方法调用,单击“MethodProfiling”按钮后,程序会执行一系列的方法,如图17所示。
3.3.2 打开Android Device Monitor
在Android SDK中有一个工具Device Monitor,可以用来运行一些动态分析工具,其中就包括Method Profiling。在Android Studio安装时的SDK/tools文件夹下有monitor.bat的文件,双击后即可打开Android Device Monitor,如图18所示。
3.3.3 开始方法剖析
在“Devices”窗口选择指定设备下的com.ihui.methodprofiling程序,点击“Device”窗口上方工具栏中的“Start Method Profiling”按钮,开启Method Profiling。与此同时,“Start Method Profiling”按钮上的文字会变成“Stop Method Profiling”。
切换到程序的运行界面,单击“MethodProfiling”按钮,待Toast函数执行后单击“Stop Method Profiling”停止,稍等片刻便会自动弹出TraceView窗口。
在TraceView窗口的“Name”找到前缀是com.ihui.methodprofiling的表项,单击后发现每一个方法都是有“Parents”和“Children”两个子项,图19中显示“Parents”表示com.ihui.methodprofiling.MainActivity.b()被com.ihui.methodprofiling.MainActivity.a()调用,“Children”表示com.ihui.methodprofiling.MainActivity.b()调用了com.ihui.methodprofiling.MainActivity.a()。所有方法调用都以链表形式依次显示,显示的顺序与栈跟踪的输出信息相反。
3.3.4 扩展
如果我们想要执行的Method Profiling代码在程序一开始就执行了,要想对其进行Method Profiling,需要先查找开始点与结束点,再手动注入代码。Method Profiling的调试支持本来就是由Android SDK提供的,android.os.Debug类提供了startMethodTracing()与stopMethodTracing()来开启Method Profiling,示例Java代码如下所示。
1 | android.os.Debug.startMethodTracing("iHui"); |
如果手动注入Method Profiling时代码的起始点与结束点不容易确定,可以将代码的范围设置得大一些。
首先,在Activity的OnCreate()方法中注入startMethodTracing()的代码,反汇编成smali代码后如下所示。
1 | const-string v0, "iHui"; |
然后,在Activity的OnStop()方法中注入stopMethodTracing()的代码,反汇编成smali代码后如下所示。
1 | invoke-static {}, Landroid/os/Debug;->stopMethodTracing()V |
字符串iHui为trace文件名,上面的代码执行后,会在SD卡的根目录中生成iHui.trace文件。这个文件包含了a()方法执行过程中的所有方法调用与CPU占用时间等信息,可以使用Android Device Monitor工具打开,注意从Android Studio3.2以后traceview工具被弃用。
4 使用IDA Pro动态调试APK
IDA Pro从6.6版本开始加入了动态调试APK的功能。使用IDA Pro既可以调试DEX代码,也可以调试Native代码,甚至可以启动两个IDA Pro实例,同时完成两种代码的联合调试。
本节以Method Profiling实例中DEX代码为例进行分析。
首先,如图20所示,右键选中Method Profiling APK,以IDA Pro(64-bit)方式打开,在弹出的对话框中选择要分析的目标,然后选择图21中的classes.dex文件,点击“OK”按钮。使用默认设置,单击“OK”按钮,等等IDA Pro完成初步分析。
然后,单击菜单项“Debugger”->“Debugger options”,打开图22所示的调试器设置窗口。如果需要分析的程序在启动后停在入口处,可以勾选“Suspend on process entry point”复选框。单击“Set specific options”选项,设置adb的完整路径,以及要启动的Activity组件的信息,如图23所示。设置完成后,分别单击两个窗口上的“OK”按钮,返回IDA Pro的反汇编主界面。
如图24所示,单击菜单项“Debugger”->“Process options”,设置端口为54321,单击“OK”按钮,返回IDA Pro主界面。
如图25所示,单击菜单项“Debugger”->“Start Process”,或者单击工具栏中的绿色三角形按钮,启动动态调试。此时会弹出提示框,告诉我们执行代码可能会有风险,点击“OK”按钮,IDA Pro会以调试模式启动Method Profiling实例。
如图26所示,单击菜单项“View”->“Open subviews”->“Exports”,按“Ctrl+F”组合键,搜索关键字“onClick”,选中“MainActivity$1.onClick@VL”行。
如图27所示,选中“MainActivity$1.onClick@VL”行并双击,在方法的第1行按“F2”键设置断点。然后,在手机上点击程序界面上的“Method Profiling”按钮,程序会在IDA Pro中命中断点。
如图28所示,在IDA Pro中单击菜单项“Debugger”->“Debugger windows”->“Locals”,打开变量窗口,可以看到程序中的变量已经正确显示出来。
接下来,使用“Step into”和“Step over”按钮调试代码了。
【注】使用IDA Pro动态调试APK时需要使用emulator -avd Pixel_XL_API_27开启Android模拟器,不然会出现Waiting for Debugger的错误。
5 总结
本文主要介绍了基于Android DEX程序的动态分析,以及常用的分析与调试工具。MobSF除了可以在逆向分析中使用,也经常在正向软件开发中使用。对基于DEX的Dalvik指令的调试工具,例如Android Studio配合smalidea的动态调试没有介绍。
1 | 综上,快速定位关键代码的思路如下: |
6 参考文献
[1]丰生强. Android软件安全权威指南[M]. 电子工业出版社, 2019.
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,可以邮件至 xingshuaikun@163.com。