4af7bfbf02006731524589ee4c3a8e73.png

作者简介:叶绍琛,Unix/Linux/Android操作系统内核技术专家,大中华区前50位RHCA系统架构师,曾任网易互娱云计算平台技术负责人,ChinaUnix和51CTO技术社区技术专家。

1Frida技术原理

Android脱壳的目的是从内存中dump下解密的应用dex文件,为了实现这个目的我们需要知道dex文件在内存中dex地址与dex文件大小。Android系统的libart.so库文件中提供了一个导出的OpenMemory函数用来加载dex文件。

9be17c575fd6ab7e3848833662bd5b7f.png

这个函数的第一个参数指向了内存中的dex文件,如果我们可以Hook 这个函数,就可以得到dex文件加载进内存时的起始地址,再根据dex文件格式计算得到文件头保存的dex文件的长度fileSize。

2获取函数签名

在编写Frida脚本之前我们需要在libart.so文件中找到OpenMemory的函数签名,这个签名根据android版本或者架构的不同会有些许差异,下面介绍如何从运行待脱壳应用的设备中提取libart.so文件:

$ adb pull /system/lib/libart.so /本地目录

$ adb pull /system/lib64/libart.so /本地目录

获取libart.so文件后有两个方法可以获得函数签名,第一种是利用IDA pro 分析so文件,搜索OpenMemory函数:

4c4733b061687038a88547e46baf62a4.png

第二种方法实在linux系统下使用nm命令直接查看libart.so文件的函数签名:

$ nm libart.so | grep OpenMemory

73f5bb6c4aa4e9e06da413cdb8e368f8.png

3编写Frida脚本

Frida可以执行js脚本也可以执行python脚本:

· JS脚本:

5c64020b0fc9e02d77c5cbb4c7663ad9.png

· Python脚本:

bf07e3e092750bb0e157429cb931cc51.png

代码参考:Github/frida-unpack

4准备Frida环境

安装 Frida 客户端与服务端,建议使用Python的pip命令安装frida客户端:

$ pip install frida

$ pip install frida-tools

下载完毕后查看下载的客户端版本:

$ frida --version

根据客户端版本与设备CPU架构到Frida的github项目页面中上下载对应的服务端:

2d0fd9b47f46847c028a614c8ea0c922.png

客户端与服务端都下载完毕后,先把frida-server推送到设备中:

$ adb push frida-server /data/local/tmp

设置frida-server的权限并运行:

$ su

# cd /data/local/tmp

# chmod 777 frida-server

# ./frida-server

如果运行失败,可以尝试关闭android系统的SELinux:

# setenforce 0

我们打开另一个控制台,运行客户端命令查看与服务端的交互:

$ frida-ps -U

出现以下信息,则说明交互成功:

bea7b7fbd02ab2137ad460cd52cabef7.png

5执行Frida脱壳脚本

我们先将一个简单的测试程序丢到腾讯乐固上去加固,我们可以看到加固完成后的结果:

cb7cc38de1647fa859c41a58143ba2de.png

可以看到加固过后程序的原本代码被隐藏了,被替换成乐固的壳程序代码,将加固后的应用安装到手机上,赋予该应用读写sd卡权限:

eb96575a5bacad90c841b58c4b17f2d6.png

在sd卡中创建unpack目录,执行命令运行JS脚本:

$ frida -U -f -l --no-pause

-U参数表示我们使用了USB server进行链接,-f 参数表示在设备中启动一个指定的android程序,需要配合 --no-pause参数来使进程恢复。-l参数表示需要注入的js脚本。

Python脚本的运行就简单得多了:

$ python unpack.py

7d2b98c9c0ab9c7b7e3932cb549b831b.png

另外注意一个问题,如果应用运行过程中使用了64位的libart.so,脚本的这个地方需要进行一些小修改:

//dex起始位置  var begin = args[1]  

获取dex起始位置的语句需要改成:

var begin = this.context.x0

不然可能会出现这样的问题:

076c50d4836ac61c18e6ec144f319d6d.png

脱壳完成后从sd卡中将unpack文件夹pull到本地:

$ adb pull /sdcard/unpack .

434737ab7bb68a7adea97f180bba8563.png

可以看到脱壳成功了。由于这一类脱壳法是将内存中的dex文件直接dump下来的,对于指令抽取等针对方法体进行处理的加固方式并没有做对应的修复工作。

如果应用经过了指令抽取或者dex2c处理,这种方法拿到的dex文件就是残缺不全的,对于那些加固就需要通过对Android源码进行修改的脱壳方法比如dexhunter或者是比较新的FART脱壳机。

《Android安全指南》系列文章,未完待续。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐