9.1.3 使用 IDA Pro 静态分析 so 文件
上面介绍了如何分析 Java 代码,那么 so 文件应该怎么处理以及使用何种工具来分析呢?接下来就介绍一个分析利器——IDA Pro。
1. IDA Pro 是什么
Android 应用程序的开发语言主要是 Java 语言,但是由于 Java 层的代码很容易被反编译,而反编译 C/C++代码的难度比较大,所以现在很多 Android 应用程序的核心部分都使用 NDK进行开发。使用 NDK 开发能够编译 C/C++代码,最终生成 so 文件。 so 文件是一个二进制文件,无法直接分析,所以需要用到一个反编译工具 IDA Pro。 IDA Pro 能够对 so 文件进行反汇编,从而将二进制代码转换为汇编代码,而且利用 IDA Pro 神奇的 F5 功能还能将汇编代码反编译成类 C/C++代码。本节使用的 IDA Pro 的版本为 7.0 版本。下面就介绍一下如何利用 IDA Pro 静态分析 so 文件的步骤。
2.如何使用 IDA Pro 静态分析 so 文件的步骤
( 1)如图 9-9 所示,打开 IDA Pro,将 so 文件拖到 IDA Pro 窗口中,在弹出的“ Load a new file”窗口中选择“ ELF for ARM(Shared object)[elf.ldw]”选项,然后单击 OK,等待一段时间之后,我们就可以看到反汇编后的代码了。

下面介绍几个主要窗口,其中 IDA View-A 窗口用于显示汇编代码; Hex View-1 窗口用于显示机器码(十六进制格式); Functions window 窗口用于保存各个函数的名字,找到对应的函 数 名 字 后 双 击 , 即 可 定 位 到 对 应 函 数 的 汇 编 代 码 。 例 如 , 如 果 想 要 查 看 类com.ss.android.common.applog.UserInfo 中的 Native 函数 getPackage 的代码,可以在 Functions window 或者 Exports 窗口中找到 Java_com_ss_android_common_applog_UserInfo_getPackage 函数后双击,如图 9-10 所示。
( 2)在这里,可以看到反汇编后得到的都是 ARM 汇编代码,因此要想深入了解 so 文件的逆向过程,必须学习 ARM 汇编语言,了解 ARM 汇编语言每个寄存器的作用和传参约定等。当然,此处 IDA Pro 还有一个强大的插件——Hex-Rays Decompiler,通过将光标定位到函数内并按下 F5 键,就可以把汇编代码转换为类 C/C++代码了,如图 9-11 所示。


( 3)另外,如果关注函数内部的跳转流程,可以在 IDA View-A 汇编界面中按下空格键,这样就可以跳到 Graph View 界面,程序的各种跳转十分清晰,如图 9-12 所示。

( 4)按下快捷键 Shift+F12 可以跳转到 Strings window 窗口,其中显示了 so 文件的所有字符串,可以很方便地定位到关键代码,如图 9-13 所示。

( 5)单击上面的关键字符串,就可以跳到字符串的实际地址,然后按下 X 快捷键,可以查看字符串的交叉引用,接着可以定位到关键函数,这个操作使用频率很高,如图 9-14 所示。

以上基本就是静态分析 so 文件的所有步骤了,定位到关键函数之后可以按 F5 键转换成对应的类 C/C++代码来分析函数的实现逻辑,而复杂的逻辑需要一定的 ARM 汇编编程能力并结合后面会介绍到的动态调试。静态分析+动态调试基本上就能还原所有的 Native 函数逻辑了。
9.2 动态分析 Android 应用
静态分析 APK 只能分析一些简单的逻辑,当面对复杂逻辑的时候,一般需要“动静结合”,即静态分析+动态调试或者 Hook。静态分析一般是为了定位关键函数, Hook 是为了打印函数的实参和返回结果,而动态调试可以单步确定实现逻辑。这样一步步执行下来,就可以以最快的速度还原原来的函数逻辑和算法。
9.2.1 使用 IDA Pro 动态调试 APK
具体如何动态调试 APK,下面就来进行详细的阐述说明。1.环境准备
( 1)使用 Root 过的手机或者重打包 APK 修改 AndroidManifest.xml 的 application 标签android:debuggable=”true”。
原理: IDA Pro 的远程调试都有一个服务端,比如 Android 的就是 android_server,这是一个可执行文件,运行在手机端。 android_server 负责调试本地准备调试的 App,然后和 IDA PC端进行交互,实现远程调试。 android_server 的调试原理实际上就是 Linux 的 ptrace 原理。如果要用 ptrace 调试进程,则 ptrace 必须要与调试的进程权限一样或者拥有 Root 权限。 ptrace 怎样才能与被调试的过程权限一样呢? Android 下有一个 run-as 程序,可以切换 shell 的进程环境,执行 run-as pkg 命令就可以切换到该包名的权限环境,之后再在该环境下运行 android_server,而执行 run-as pkg 命令需要 pkg 的 android:debuggable=”true”配置才行,因此手机没被 Root 的时候,只能重打包 APK 修改 android:debuggable=”true”后才能调试。手机被 Root 后,直接在Root 权限下执行 android_server 就行了。
( 2)把 android_server 这个 elf 文件 adb push 到手机的/data/local/tmp 里,执行 chmod 777。
android_server 被赋予读/写权限,执行这个文件./android_server 就会监听调试端口,等待被调试。
( 3)开启一个命令行窗口执行 adb forward tcp:23946 tcp:23946,进行端口转发。
( 4)手机安装准备调试的应用,这里演示的是 Android Stduio sample 中的 testJni 示例。
到此,环境已准备完毕,上面这些步骤其实都可以使用脚本实现。
2. IDA attach
启动 IDA Pro,打开 Debugger→Attach→Remote Armlinux/Andoid Debugger。
如图 9-15 所示,填写 Hostname 为 127.0.0.1 或 localhost(推荐), Port 为 23946 或自定义转发的端口,其他设置保持默认值,单击 OK 按钮。

如图 9-16 所示,在弹出的 Choose process to attach to 窗口中找到 App 的进程名,单击 OK按钮。

等待分析完成后即可进入调试界面,如图 9-17 所示。

3.定位函数
方法一: 如图 9-18 所示,在 Modules 窗口中找到要调试的 so 文件,双击该文件,这时就会出现 exported 的函数名字,再找到要调试的函数,双击即可定位到相应的汇编代码处。

方法二: 如图 9-19 所示,使用快捷键 Ctrl+S 打开 Choose segment to jump 窗口,选择 so文 件 , 这 里 可 以 使 用 快 捷 键 Ctrl+F 进 行 搜 索 , 同 时 需 要 记 下 so 文 件 的 起 始 地 址( 750A4000)。

用另一个 IDA instance 打开 so 文件,找到对应函数的位置,在图 9-20 中可以看到偏移量为 000005EC。

绝对地址=基址+偏移地址=750A4000+000005EC =0x750a45ec。
按下快捷键 G,输入 0x750a45ec 即可跳转到正确的函数。然后使用快捷键 F2 或者单击前面的小圆点会跳至下一个断点。
4.调试
使用如下快捷键可以进行调试。
F7:单步进入。
F8:单步跳过。
F9:继续运行,直到断点位置。
9.2.2 使用 Xposed Hook Java 代码
Xposed 框架是一个可以在不修改 APK 的情况下影响程序运行(修改系统)的框架服务,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运行。下面就来讲讲如何使用 Xposed Hook Java 代码。
1.什么是 Hook
Hook 是钩子的意思,那我们在什么时候使用这个钩子呢?在 Android 操作系统中,系统维护着自己的一套事件分发机制。应用程序(包括应用触发事件和后台逻辑处理)根据事件流程一步步地向下执行。如图 9-21 所示,钩子的意思就是在事件传送到终点前截获并监控事件的传输,像一个钩子钩上事件一样,并且能够在钩上事件时处理一些特定的事件。

Hook 的这个本领使它能够将自身的代码融入被钩住( Hook)程序的进程中,成为目标进程的一部分。 API Hook 技术是一种用于改变 API 执行结果的技术,能够使系统的 API 函数执行重定向。在 Android 系统中,使用了沙箱机制,普通用户程序的进程空间都是独立的,程序的运行互不干扰。这就使我们希望通过一个程序改变其他程序的某些行为的想法不能直接实现,但是 Hook 的出现给我们开拓了解决此类问题的道路。当然,根据 Hook 对象与 Hook 后处理事件的方式, Hook 还分为不同的种类,比如消息 Hook、 API Hook 等。
2. Hook 权限要求
关于 Android 中的 Hook 机制,大致有如下两个方式。
要 Root 权限,直接 Hook 系统,可以干掉所有的 App。
免 Root 权限,但是只能 Hook 自身,对系统其他 App 无能为力。
配合多开技术,可以实现免 Root 权限 Hook 任意 App,若大家有兴趣,可以研究一下Lody 开源的 VirtualApp。而本节主要介绍的是基于 Xposed 的 Hook 技术。
3. Xposed 框架实现 Hook 的原理介绍
Zygote 是 Android 的核心,每运行一个 App, Zygote 就会复刻一个虚拟机实例来运行App, Xposed 框架深入 Android 核心机制,通过改造 Zygote 来实现一些很厉害的功能。 Zygote的启动配置位于/init.rc 脚本中,在系统启动的时候开启此进程,对应的执行文件是/system/ bin/app_process,这个文件完成类库加载及一些函数调用工作。
在系统中安装了 Xposed 框架后,会对 app_process 进行扩展,也就是说, Xposed 框架会拿自己实现的 app_process 文件覆盖掉 Android 原生提供的 app_process 文件。当系统启动的时候,就会加载由 Xposed 框架替换过的进程文件,并且 Xposed 框架还定义了一个 jar 包,系统启动的时候也会加载这个包:
/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar
这个 jar 包负责对 Hook 模块内定义好的方法进行 Hook 操作,之后当我们启动所有 App 的时候都会产生一个 fork zygote 的过程,于是所有进程都会被 Hook,这是一个全局 Hook。
4. Xposed 框架运行的条件
( 1)已 Root 的手机或者模拟器,建议最好使用的是 Android 4.x 的系统。
( 2) Xposed Installer( Xposed 安装程序)
Xposed 框架就是一个 APK 文件,也就是上面下载的 Xposed 安装程序,下载后用下面的命令将其安装到手机上或者模拟器上:
1. adb install de.robv.android.xposed.installer_v32_de4f0d.apk
5.激活 Xposed 框架
安装完 Xposed 框架之后,单击框架,可看到需要激活 app_process 和 XposedBridge.jar,如图 9-22 所示。
选择安装/更新,会提示重启,确定即可。
重启后,会发现 app_process 和 XposedBridge.jar 已经被激活。

6.第一个 Xposed Module Hello world ( 1)创建一个 Android 工程
一个 Xposed 模块本质上是一个正常的 APK,只是这个 APK 没有与用户交互的 Activity 界面,它仅包含一些 meta 数据和文件,并且安装该 APK 后桌面上没有相应的图标。所以你只需要创建一个空的 Android 工程,不需要添加任何 Activity。
( 2)将 Android 工程变成 Xposed 模块
在工程中添加 Xposed Framework API。 Xposed 模块为了使用 Xposed 框架的 API,需要下载相应的 XposedBridge.jar 包,直接在 GitHub 上下载即可。
另外,可以通过下面的网址查看 Xposed 框架 API: https://bintray.com/rovo89/de.robv.android.xposed/api
在 Android Studio 工程的 app/build.gradle 文件中添加依赖项:
1. rep}
注意: 使用 provided 时不要使用 compile。 compile 会将整个 API 类编译进你的 APK 中,导致出现问题。 provided 只提供了 API 类的引用, API 类真正的实现代码则在 Xposed 框架中。
在大多数情况下, repositories 已经存在,并且已经有一些依赖,所以只需要将 provided 这一行添加到存在的 dependcies 模块中即可。
如果需要查看 API 资源,则添加下面两行命令:
1. provided 'de.robv.android.xposed:api:53'
2. provided 'de.robv.android.xposed:api:53:sources'
如 图 9-23 所示 , 确 保 关闭 Instant Run 项 , 选 择 File → Settings → Build, Execution, Deployment→Instant Run,否则你的 APK 中将不包含这些类。
AndroidManifest.xml 文件配置如下:
1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2. package="test.xposedmoduletest">
3.
4. <application
5. android:allowBackup="true"
6. android:icon="@mipmap/ic_launcher"
7. android:label="@string/app_name"
8. android:roundIcon="@mipmap/ic_launcher_round"
9. android:supportsRtl="true"
10. android:theme="@style/AppTheme">
11. <activity android:name=".MainActivity">
12. <intent-filter>
13. <action android:name="android.intent.action.MAIN" />
14.
15. <category android:name="android.intent.category.LAUNCHER" />
16. </intent-filter>
17. </activity>
18. <meta-data
19. android:name="xposedmodule"
20. android:value="true" /><!--应用为模块-->
21. <meta-data
22. android:name="xposeddescription"
23. android:value="测试" /><!--模块描述-->
24. <meta-data
25. android:name="xposedminversion"
26. android:value="53" /> <!--版本信息-->
27. </application>
28.
29. </manifest>

下面介绍 Hook Module Hello world 的实现。
如图 9-24 所示,创建一个类,比如 test.xposedmoduletest.TestModule。

可以让 Xposed 框架在下面几个位置调用你模块中的函数: Android 系统启动的时候(使用IXposedHookZygoteInit 接口)、一个新 App 被加载的时候(使用 IXposedHookLoadPackage 接口)、一个资源被初始化的时候(使用 IXposedHookInitPackageResources 接口)。
所有的进入点都是 XposedMod 接口的子接口。在本示例中,进入点是“一个新 App 被加载的时候”,需要实现 IXposedHookLoadPackage 接口。
然后进行 assets/xposed_init 配置。新建 assets/xposed_init 文件,内容为 test.xposedmoduletest.TestModule,即我们定义的类路径。
( 3)安装 APK 模块
在第一次安装模块的时候,需要打开 Xposed Installer 激活模块,单击模块,之后就可以看到如图 9-25 所示的界面,勾选模块,重启手机,这里推荐软重启( restart zygote)。

重启手机之后,可以看到 adb logcat 已经输出了很多日志,每个 App 启动都会打印包名,如图 9-26 所示。

















暂无评论内容