赞
踩
上一篇讲到了用工具FDex2进行脱壳,但有一些会脱不到,这篇用Frida进行脱壳。
加壳apk运行流程:app启动后-->壳dex先加载起来-->把源classes.dex读出来-->解密源classes.dex-->把源classes.dex给加载进内存-->源dex运行起来
以下两篇文章都对dex进行了详解
Dex文件格式详解 https://www.jianshu.com/p/f7f0a712ddfe
ART 加载dex文件 https://www.jianshu.com/p/f81242ad8cb7
数据名称 | 解释 |
header | dex文件头部,记录整个dex文件的相关属性 |
string_ids | 字符串数据索引,记录了每个字符串在数据区的偏移量 |
type_ids | 类似数据索引,记录了每个类型的字符串索引 |
proto_ids | 原型数据索引,记录了方法声明的字符串,返回类型字符串,参数列表 |
field_ids | 字段数据索引,记录了所属类,类型以及方法名 |
method_ids | 类方法索引,记录方法所属类名,方法声明以及方法名等信息 |
class_defs | 类定义数据索引,记录指定类各类信息,包括接口,超类,类数据偏移量 |
data | 数据区,保存了各个类的真是数据 |
link_data | 连接数据区 |
简单记录了dex文件的一些基本信息,以及大致的数据分布。长度固定为0x70,其中每一项信息所占用的内存空间也是固定的,好处是虚拟机在处理dex时不用考虑dex文件的多样性
字段名称 | 偏移值 | 长度 | 说明 |
magic | 0x0 | 8 | 魔数字段,值为"dex\n035\0"(固定的) |
checksum | 0x8 | 4 | 校验码 |
signature | 0xc | 20 | sha-1签名 |
file_size | 0x20 | 4 | dex文件总长度 |
...... | ...... | ..... | ...... |
字段太多就不都展示出来,可以看到file_size
这个就是我们要找的dex文件,因为源dex解密后会加载进内存,所以我们去Hook加载Dex的函数,把Dex从内存中dump出来。
下面这个函数就是把解密后的源dex加载进内存:
DexFile::OpenMemory(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
MemMap* mem_map,//nullptr const OatDexFile* oat_dex_file,
std::string* error_msg)
OpenMemory()是在安卓系统/system/lib/libart.so
里面,然后我们先把这个so文件拉到电脑用IDA打开
打开IDA选择静态调试,打开libart.so这个文件
这些就是so文件里面的函数,点击这个框,按ctrl+f进行搜索,输入OpenMemory这个函数,右键进行编辑,
_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_
这个字符串就是OpenMemory函数在内存中对外的方法名,我们打开IDA就是为了找这个方法名。
现在来写脚本:
- import frida
- import sys
-
- package = "com.iCitySuzhou.suzhou001"
-
- open_memory_6 = "_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_"
- #OpenMemory 在libart.so中 art虚拟机(安卓5) davlink虚拟机(安卓4)
- #Hook OpenMemory的导出方法名
- #用IDA 打开libart.so 查看OpenMemory的导出方法名
- #OpenMemory的第一个参数是dex文件在内存中的起始位置
- #根据dex文件格式 从起始位置开始 第32个字节 是该dex文件的大小
- #知道dex起始位置和整个文件大小,只需要把这段内存dum出来即可
- #适用于 安卓 6 7 8 9
-
- #文件的起始位置 文件的大小 知道了文件的结束位置
-
-
-
- def on_message(message, data):
- if message['type'] == 'send':
- print("[*] {0}".format(message['payload']))
- else:
- print(message)
-
-
- src = """
- //找so文件某个方法地址,找openMemory的内存地址
- var openMemory_address = Module.findExportByName("libart.so", "_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_");
- send('openMemory address:'+openMemory_address)
- //hook openMemory地址
- Interceptor.attach(openMemory_address, {
- //一进入openMemory,就会调用onEnter方法
- onEnter: function (args) {
- //dex文件的起始位置
- var dex_begin_address = args[1]
- //dex文件的前8个字节是magic字段 看dex的文件格式说明
- //打印magic(会显示 "dex 035") 三个字符 可以验证是否为dex文件
- console.log("magic : " + Memory.readUtf8String(dex_begin_address))
- //把地址转换成整型(十进制) 再加32
- //因为dex文件的第32个字节处存放的是 dex文件的大小
- var address = parseInt(dex_begin_address, 16) + 0x20
- //把address地址指向的内存值读出来 该值就是dex的文件大小
- //ptr(address)转换的原因是 frida只接受 NativePointer类型指针
- var dex_size = Memory.readInt(ptr(address))
- console.log("dex_size :" + dex_size)
-
- //frida写文件 把内存中的数据 写到本地
- var timestamp = new Date().getTime();
- var file = new File("/data/data/%s/" + timestamp + ".dex", "wb")
- //Memory.readByteArray(begin, length)
- //把内存里的数据读出来,从begin开始读,取length长度
- file.write(Memory.readByteArray(dex_begin_address, dex_size))
- file.flush()
- file.close()
- send("dex begin address:"+parseInt(dex_begin_address,16))
- send("dex file size:"+dex_size)
- },
- onLeave: function (retval) {
- if (retval.toInt32() > 0) {
- }
- }
- });
- """%(package)
-
-
- print("dex 导出目录为: /data/data/%s"%(package))
- device = frida.get_usb_device()
- pid = device.spawn(package)
- session = device.attach(pid)
- script = session.create_script(src)
- script.on("message" , on_message)
- script.load()
- device.resume(pid)
- sys.stdin.read()

先把frida-server运行起来
再做端口转发到PC,Windows运行
- adb forward tcp:27043 tcp:27043
- adb forward tcp:27042 tcp:27042
hook需要app运行,先把app打开,再运行脚本,
我们去手机/data/data/com.iCitySuzhou.suzhou001
目录下查看,会多了几个dex文件
把这些dex文件拉到电脑用jadx打开看下,一个个查看后,找到了源dex
到这里用frida脱壳也成功了。。。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。