动态分析Android程序

1 背景

复习过一阵后那种感觉又回来了😄😄😄,学过的东西还是得经常回顾,这样才能做到温故而知新,进而为师!!!

在分析经过加密混淆的Android程序时,仅使用静态分析技术往往达不到理想的效果,这时候就轮到动态分析技术出场了👏👏👏

动态分析分为动态跟踪动态调试。动态跟踪侧重于自动化分析,工具的表现形式一般是自主研发或由第三方提供的分析平台。在软件开发领域编写大型项目安全检测分析报告,以及在软件安全领域进行恶意代码与病毒的分析时,会广泛使用动态分析技术。动态调试需要分析人员参与进来,依靠调试器的能力完成分析工作。在进行动态调试时,除了调试器,还需要分析人员自主确定分析点。在开发软件时,一般可以进行源码级调试,对设置断点的地方可以通过阅读源码找到。在进行逆向分析时,通常只能进行反汇编级别的调试,分析人员需要通过阅读大量的反汇编代码来寻找突破口。

2 动态分析框架

2.1 简介

目前常用的开源跨平台动态分析框架是MobSF(下载地址为:https://github.com/MobSF/Mobile-Security-Framework-MobSF),同时支持Android、IOS、Windows平台上的软件分析,也同时支持静态分析、动态分析及Web API测试。

MobSF支持在Windows10、Ubuntu、macOS上进行安装,安装过程中还需要其他的辅助软件,安装操作可以参考MobSF的手册和相关经验。

在安装过程中,会向设备中安装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所示。

图1 启动Android Studio模拟器

接着,在git下来的MobSF文件夹下运行run.bat 127.0.0.1:8000,在浏览器中输入http://localhost:8000/即可打开Web界面,如图2所示。

图2 启动MobSF

然后,上传APK后即可进行体验MobSF的其他强大功能,如图3所示。

图3 MobSF分析APK

3 动态分析技巧

主流的动态分析框架除了提供一套完整、通用的动态分析方法,通常还具备高度的可配置特性与可扩展性,例如添加自定义分析模块、增加指定的API分析等功能。但对一些应用来说,还是有一些分析要点没有提供。在这个时候,就需要分析人员动手解决这些问题。

3.1 代码注入法

一个程序在发布时通常不会保留Log输出信息。如果想观察程序特定位置的输出信息,在默认情况下,使用动态分析框架是做不到的,需要手动进行代码注入。代码注入是指先反编译Android程序,然后在反汇编出来的smali文件中添加Log调用的代码,最后重新打包程序并运行,查看输出结果。

3.1.1 实例程序

本节实例为一个注册码验证模拟程序,如图4所示。输入用户名与注册码后,点击“注册”按钮,程序会判断注册码是否正确,并弹出相应的提示信息。

图4 运行实例程序

3.1.2 Jadx分析APK

首先,我们将APK文件拖入Jadx中,并找到AndroidManifest.xml文件,发现只有一个MainActivity,如图5所示。

图5 查看AndroidManifest.xml文件

接着,进入到com.ihui.sn文件夹下,查看MainActivity.java文件,如图6所示。

图6 Jadx分析APK的Java源码

经过分析后得知,只需要在反编译后的dex文件的第42行添加Log调用代码即可获得注册码

3.1.3 Apktool反编译APK

使用Apktool对APK文件进行反编译,,如图7所示。

图7 Apktool反编译APK文件

对输出文件进行分析,找到按钮点击事件处理代码。在MainActivity$1.smali文件的42行中,我们发现了与Jadx分析后的MainActivity.java文件中的特征函数equalsIgnoreCase,如图8所示。

图8 发现smali文件中的特征函数

仔细阅读图8中的反汇编代码,会发现.line 42处的if-eqz p1, :cond_1是程序的关键部分。该部分通过比较v0(输入的注册码)p1(计算所得的注册码)的值来判断注册码是否正确。因此,要想获取真实的注册码,我们只需要根据寄存器的合理范围在.line 42处添加Log.v()输出p1寄存器的值。加入反汇编代码后的MainActivity$1.smali文件如图9所示。

图9 加入Log.v()输出p1寄存器的值

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所示。

图10 logcat运行

将签名后apk文件安装到手机中,并点击注册按钮后,会在命令行中出现图11中的正确注册码。

图11 获得正确注册码

在手机上将apk卸载,并安装之前正确版本的apk文件,输入用户名iHui,注册码90b94c7569fe4a0dec2b5ee4598097e9即可注册成功,如图12所示。

图12 破解成功

【扩展】

  1. 其实这里的注册码就是根据用户名计算出来的MD5值
  2. 我们也可以使用Android Killer在合理的位置注入Toast函数进而获取注册码;
  3. 我们还使用Android Killer在合理的位置注入Log函数并查看日志进而获取注册码。

3.2 栈跟踪法

尽管LogCat配合代码注入的方法在分析程序时屡试不爽,但需要分析人员阅读大量的反汇编代码来寻找程序的“输出”点,且在此过程中可能需要多次手动注入Log输出代码。如果要分析大型程序,这显然是一件苦差事。

因此,我们使用栈跟踪法来进行代码注入,主要的操作是手动向反汇编后的smali文件中添加用于输出栈跟踪信息的代码。与注入Log输出的代码不同的是,采用栈跟踪法时只需要知道大概的代码注入点,而且注入代码后的反馈信息比Log注入要详细得多。

3.2.1 实例程序

如图13所示,运行实例程序stackTrace,弹出来Toast,提示who called me?

图13 运行实例程序

3.2.2 修改关键代码

现在我们的需求是确认这个Toast是什么时候被调用的。使用ApkTool反编译stackTrace实例程序,然后查找特征函数Toast的调用位置或者提示字符串的位置,发现在MainActivity.smali文件中有图14中的代码。

图14 利用特征函数Toast定位关键代码

通过分析图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所示。

图15 注入smali代码

3.2.3 栈跟踪分析

首先,使用ApkTool将修改完的代码重新打包成APK,并进行签名。栈跟踪信息是WARN级别的信息,且Tag被系统命名为System.err,因此,在命令行中输入adb logcat -s System.err:V *:W命令,也会输出同样的栈跟踪信息,输入结果如图16所示。

图16 栈跟踪信息

栈跟踪信息记录了从程序启动到printStackTrace()方法执行期间所有被调用过的方法。从下往上查看栈跟踪信息,找到第1条以com.ihui.stacktrace开头的信息,发现开始调用的是OnCreate()方法,然后依次是a()b()c()。这样一来,我们就得知了函数的执行流程。

3.3 Method Profiling

3.3.1 实例程序

本节的演示示例模拟多级方法调用,单击“MethodProfiling”按钮后,程序会执行一系列的方法,如图17所示。

图17 Method Profiling示例

3.3.2 打开Android Device Monitor

在Android SDK中有一个工具Device Monitor,可以用来运行一些动态分析工具,其中就包括Method Profiling。在Android Studio安装时的SDK/tools文件夹下有monitor.bat的文件,双击后即可打开Android Device Monitor,如图18所示。

图18 Android Device Monitor界面

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()。所有方法调用都以链表形式依次显示,显示的顺序与栈跟踪的输出信息相反。

图19 分析程序的方法调用

3.3.4 扩展

如果我们想要执行的Method Profiling代码在程序一开始就执行了,要想对其进行Method Profiling,需要先查找开始点与结束点,再手动注入代码。Method Profiling的调试支持本来就是由Android SDK提供的,android.os.Debug类提供了startMethodTracing()stopMethodTracing()来开启Method Profiling,示例Java代码如下所示。

1
2
3
android.os.Debug.startMethodTracing("iHui");
a();
android.os.Debug.stopMethodTracing();

如果手动注入Method Profiling时代码的起始点与结束点不容易确定,可以将代码的范围设置得大一些。
首先,在Activity的OnCreate()方法中注入startMethodTracing()的代码,反汇编成smali代码后如下所示。

1
2
const-string v0, "iHui";
invoke-static {v0}, Landroid/os/Debug;->startMethodTracing(Ljava/lang/String;)V

然后,在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完成初步分析。

图20 IDA Pro打开Method Profiling APK

图21 选择classes.dex文件

然后,单击菜单项“Debugger”->“Debugger options”,打开图22所示的调试器设置窗口。如果需要分析的程序在启动后停在入口处,可以勾选“Suspend on process entry point”复选框。单击“Set specific options”选项,设置adb的完整路径,以及要启动的Activity组件的信息,如图23所示。设置完成后,分别单击两个窗口上的“OK”按钮,返回IDA Pro的反汇编主界面。

图22 Debugger setup界面设置

图23 Dalvik debugger configuration界面设置

如图24所示,单击菜单项“Debugger”->“Process options”,设置端口为54321,单击“OK”按钮,返回IDA Pro主界面。

图24 Debug application setup: dalvik界面设置

如图25所示,单击菜单项“Debugger”->“Start Process”,或者单击工具栏中的绿色三角形按钮,启动动态调试。此时会弹出提示框,告诉我们执行代码可能会有风险,点击“OK”按钮,IDA Pro会以调试模式启动Method Profiling实例。

图25 启动动态调试

如图26所示,单击菜单项“View”->“Open subviews”->“Exports”,按“Ctrl+F”组合键,搜索关键字“onClick”,选中“MainActivity$1.onClick@VL”行。

图26 搜索关键字onClcik

如图27所示,选中“MainActivity$1.onClick@VL”行并双击,在方法的第1行按“F2”键设置断点。然后,在手机上点击程序界面上的“Method Profiling”按钮,程序会在IDA Pro中命中断点。

图27 设置断点

如图28所示,在IDA Pro中单击菜单项“Debugger”->“Debugger windows”->“Locals”,打开变量窗口,可以看到程序中的变量已经正确显示出来。
接下来,使用“Step into”和“Step over”按钮调试代码了。

图28 使用IDA Pro动态调试APK

【注】使用IDA Pro动态调试APK时需要使用emulator -avd Pixel_XL_API_27开启Android模拟器,不然会出现Waiting for Debugger的错误。

5 总结

本文主要介绍了基于Android DEX程序的动态分析,以及常用的分析与调试工具。MobSF除了可以在逆向分析中使用,也经常在正向软件开发中使用。对基于DEX的Dalvik指令的调试工具,例如Android Studio配合smalidea的动态调试没有介绍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
综上,快速定位关键代码的思路如下:

1.分析流程
搜索特征字符串
搜索关键API
通过方法名来判断方法的功能

2.快速定位关键代码
反编译APK程序,AndroidManifest.xml => 包名/系统版本/组件
程序的MainActivity(程序入口界面)
每个Android程序有且只有一个MainActivity
分析程序的执行流程
需重点关注的Application
Application执行时间
授权验证

3.定位关键代码的技巧
信息反馈法 (资源id/字符串)
特征函数法 (API函数)
顺序查看法 (分析程序执行流程/病毒分析)
代码注入法 (动态调式/插入Log/查看Logcat/分析加解密)
栈跟踪法 (动态调式/函数调用流程)
Method Profiling (方法剖析 => 动态调式/热点分析/函数调用流程)

6 参考文献

[1]丰生强. Android软件安全权威指南[M]. 电子工业出版社, 2019.


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,可以邮件至 xingshuaikun@163.com。

×

喜欢就点赞,疼爱就打赏