Android逆向实战:大厂加固APP逆向分析
今天我们来分析一个加固的 APP。
APP6
APP6 地址:https://app6.scrape.center/
APP6 说明:
接口增加了加密参数,同时对源码进行了混淆,适合做抓包实时处理或可视化爬取或逆向分析。
先来看看被混淆的 APP6,使用 jadx-gui 打开,和之前的 APP 代码几乎一样,只是部分变量被混淆了,而且混淆的程度很低。
可以看到,之前的 page
和 limit
变量被混淆成随机的变量了,为什么这里是 f1748b
呢,因为 jadx-gui 自带反混淆,正常来说这些变量可能被混淆成了 a
,b
这种单个变量,但是这种变量满屏都是的话基本上没法看了,所以 jadx-gui 会自动将这些变量根据作用域重命名成几个字母数字组成的变量,方便我们逆向分析。
查看其他的代码,看到其实混淆没有很严重,基本上不影响逆向分析,转成 Python 代码的逻辑和上篇文章一致,具体代码参考上篇文章。
APP7
APP7 地址:https://app7.scrape.center/
APP7 说明:
接口增加了加密参数,同时对安装包进行了加固处理,适合做抓包实时处理或可视化爬取或逆向分析。
加固原理
APP7 的安装包被加固了,这个词可能有的小伙伴第一次听说,其实加固和 Windows 或者其他桌面平台的可执行文件的加壳原理类似,说白了就是加了一层加密或者混淆的壳将原来的真正业务代码加密保存起来了,在运行的时候才会解密出来,然后跳转过去执行真正的业务逻辑。
在 Android 中也是一样的,Android 的业务逻辑一般来说就两个地方,dex
文件和 so
文件。加固主要针对的就是 dex
文件,加固代码会将原有的 dex
文件加密保存到某个位置,然后将解密代码替换原有的 dex
。在 APP 运行的时候,先执行解密的 dex
文件,它会自动解密出原来的 dex
文件,然后跳转过去执行真正的 dex
文件。
APP 加固其实就是想要对抗静态分析,因为 Java 可以通过反编译的方式查看还原度很高的源代码,针对这种情况,一些比较核心的代码就要使用混淆或者加固的方案来将其保护起来,防止被人直接反编译。
加固 dex
好了,知识点铺垫了一些了,下面我们来看看 APP7 的具体情况,先使用 jadx-gui 打开看看。
打开后发现只有这么几个文件,而且竟然找不到 MainActivity
,这不可能,我们知道 MainActivity
是一个 APP 的入口点,正常来说肯定有的。那遇到这种情况,我们大概率是遇到被加固的 APP 了。既然找不到 MainActivity
,那先看看 AndroidManifest.xml
文件,这个文件中记录了 APP 的一些很重要的配置信息,包括入口点信息,权限信息等等。
我们发现了一个叫 StubApp
的类,App 入口点还是 MainActivity
,只不过被混淆了,现在找不到而已。我们先看看 Stubapp
这个类是做什么的。
应用启动流程
原来是继承了 Application
类,这个类在加固的时候很常见,它是 Android 应用的全局基类,代表应用进程的入口。每个应用运行时都会实例化一个 Application
对象,用于管理应用的生命周期和全局状态,并且每个应用进程只有一个 Application
实例。
一个 APP 启动的流程大概是这样的:
- 用户点击 APP 图标,初始化主线程(UI 线程),加载
Application
类。 Application
初始化,Application
构造函数 →attachBaseContext()
→onCreate()
。MainActivity
启动,onCreate()
→onStart()
→onResume()
。- 启动其他
activity
。
从上面的流程可知,attachBaseContext
是要优于 onCreate
执行的,那在这两个函数中,肯定会执行 dex
解密的流程,不然到时候执行 MainActivity
肯定会报错找不到类。
可能会有小伙伴疑惑,为什么我的变量名和你的不一样呢?因为 jadx-gui 可以对混淆的变量名进行重命名,在变量名上点击右键就可以将其重命名成自己方便理解的名字。例如我这里重命名了解密函数和 close
函数。
看到这里,我们大概了解 attachBaseContext
的执行逻辑了:
- 保存原来的
context
- 根据 当前的 CPU ABI 拼接壳的 so 文件路径
- 拷贝上一步中组合的文件路径中的文件拷贝到一个目录中
- 使用
System.load
加载 对应的 So
而且 onCreate
函数看起来也没有做一些解密工作,基本上 Java 层就没有什么信息可以分析了,dex
解密和加载肯定在 so 层了。
加密 dex 保存位置
这个时候,其实还有一个问题,加密后的 dex
保存在哪里呢?翻了翻其他的文件夹,没有找到可疑的文件。我把目光重新定位到了 dex
文件上,dex
文件中只有刚才截图中的几个类,为什么会有 3M 呢?使用 010editor
查看一下 dex
文件头。
010editor
自动分析了 dex
的文件结构,除了正常的文件头外,还有一个 injected_data
,这段数据很长, 2F14E4h
转成十进制就是 3085540,这么长的数据,我们看看它长什么样子,点红色的色块会自动跳到对应的数据区。
数据长这个样子,查看 71 68
这几个字节,猜测可能是某种自定义的文件魔数?类似于 dex
文件的 64 65 78 0A
,也就是说 so 可能把加密后的 dex
文件当做一个二进制文件拼接在壳的 dex
文件后面了。这种方案相对比较隐蔽,因为一般来说一个正常的 ELF 文件不会校验文件末端是否被新增了数据,只会根据文件各部分的大小读取对应的内容,所以可以使用这种方式来隐藏一些文件。例如图片文件中隐藏 torrent
,图片文件中隐藏 zip
等等
好了,现在分析到了这里,基本上我们把能分析的东西都看了一遍了,但是现在 dex
文件还是加密的状态,如何拿到解密后的 dex
文件呢?
dump dex 文件
可以拿到解密后的 dex
文件的方案有两个:
- 静态分析加密算法,找到解密密钥和解密算法,手动解密
- 动态运行 APP,找准时机
dump
解密后的dex
文件
以上两个方案中,静态分析相对困难许多,因为要分析 so 文件的执行逻辑,如果 so 文件使用 VMP 混淆的话难度呈指数上升,所以这里我们使用使用第二个方案,不要问我为什么不分析 so 文件,我水平不够哈哈哈。
按照正常加壳的逻辑,在某个时间点原始的 dex
文件会被解密,无论是否保存到某个位置,肯定是要先放到内存中的,那我们就有机会直接从内存中将其 dump 出来。说到 dump,其实说白了就是将一块内存区域的内容当做二进制直接保存为一个文件。
那应该如何 dump 呢?其实 dump 最主要的问题就是 dump 的时机和 dump 点。dump 的时机就是我们要知道什么时候这个 dex 解密完成了,dump 点就是我们要知道在 dex
文件解密完成以后要从内存的什么地址开始 dump。
现在我们知道了应该如何解决问题,那不知道选取 dump 时机和时间点怎么办?别着急,已经有现成的工具可以做到了,我们只需要学会使用工具即可。
DexHunter
我们先使用 DexHunter 试一下,它是一个款内存搜索工具,它会在内存中搜索 dex
的文件头的魔数,如果发现了就继续往后搜索文件头,拿到 dex
的文件大小以后就将整块内存都 dump 出来,就是一个完整的 dex
文件了。
作者提供了预编译的文件,由于时间过长它已经过期了,没事,我们 fork 他的仓库,自己重新跑一下构建脚本就可以了。原有的构建脚本还过期了,给它升级一下重新构建试试。
好了,编译完了,传到手机中运行试试。
拷贝到目录中,给执行权限,执行一下看看,dump 出来了四个文件,我们分别反编译看看是不是我们要的。先来反编译第一个比较大的 dump 文件,使用 jadx-gui 打开看看,直接搜索 MainActivity
。
可以正常搜索到,并且我们想要的所有逻辑都存在,可以确定这就是解密后 dex 文件了,只是 onCreate
方法被加密了,onCreate
方法只是一些初始化方法,在这个 APP 中影响不大,所以可以先忽略。
加固方案
搜索了一下 so 的文件名字符串,这还是一个大厂的加固,但是加固等级不是很高,通过内存 dump 的方式还是可以正常将 dex
文件 dump 出来的,所以难度相对比较低,适合学习 APP 的加固方案。
对了这个加固方案还有 so 中 so,就是 so 文件中还隐藏了真正的 so 文件,在运行的时候会自动释放并执行,也可以理解为 so 也被加了一层壳,而且 so 中还使用了 vmp
技术,分段加密分段解密,好几种反调试方案,整体来看分析的难度还是很高的,这很大厂哈哈。
不过我们可以先跳过 so 文件的分析,因为 dex 文件在解密后最终还是会给到 Android 虚拟机去执行,我们其实还可以在虚拟机执行之前 Hook 对应的 dex 文件解析函数,即可获取完整的 dex 文件,将其 dump 出来即可。
如果是遇到了另外一种指令抽取的加固方案,这种方案就失效了,因为指令抽取的加固级别不是 dex 级别了,而是函数级别,它会在 dex 加固之前将 dex 实际的函数体置空,即使是将 dex dump
也不行,拿到的函数体都是空函数,函数体会在函数被实际调用的时候自动填充回去,这种处理起来就会比较麻烦了。
本文章首发于个人博客 LLLibra146’s blog
本文作者:LLLibra146
更多文章请关注公众号 (LLLibra146):
版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!