安卓逆向——frida-serve脱壳案例
一、原理
- 1. -->APP启动
- 2. -->壳dex先加载起来
- 3. -->壳负责把源dex文件读出来
- 4. -->壳把源dex文件解密
- 5. -->把解密后的dex加载进内存 源dex运行起来
所以在APP启动时,把源dex加载到内存的方法,修改加载的方法,把加载dex的内容保存下来,就是源apk的dex。
安卓系统是调用底层的so文件,把dex读取到内存中,
在系统中的 /system/lib/libart.so 文件,拿到电脑端,用ida打开分析,找到对应版本的方法
这里还是 拿上次的案例做测试,软件
环境一 : Android6
OpenMemory :OpenMemory 是Android6 把 dex 读进内存的方法
执行后 会出现 几个dex文件,这里需要一个一个用 jadx 打开看,
打开文件 可以看到 和上一篇的dex文件分析是一样的
环境二 : Android8
OpenCommon:OpenCommon 是Android8 把 dex 读进内存的方法
执行后 会出来几个dex文件,出来的dex个数可能不一样,但是只要正确的dex文件出来了就好
打开文件 可以看到 和上一篇的dex文件分析是一样的
注意:不同系统可能存在不同的区别
代码 :
# -*- coding: utf-8 -*-
import frida
import sys
# https://blog.csdn.net/weixin_44032232/article/details/109676945
def on_message(message, data):
# print(message)
if message['type'] == 'send':
# print("*****[frida hook]***** : {0}".format(message['payload']).replace('\n',''))
print("*****[frida hook]***** : {0}".format(message['payload']))
else:
print("*****[frida hook]***** : " + str(message))
### libart.so
# 9.0 arm 需要拦截 _ZN3art13DexFileLoader10OpenCommonEPKhjS2_jRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileEbbPS9_NS3_10unique_ptrINS_16DexFileContainerENS3_14default_deleteISH_EEEEPNS0_12VerifyResultE
# 7.0 arm:_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_
# android 10: libdexfile.so
# #_ZN3art13DexFileLoader10OpenCommonEPKhjS2_jRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileEbbPS9_NS3_10unique_ptrINS_16DexFileContainerENS3_14default_deleteISH_EEEEPNS0_12VerifyResultE
# 获取包名
# package = sys.argv[1]
package = 'com.iCitySuzhou.suzhou001'
print("dex 导出目录为: /data/data/%s" % (package))
device = frida.get_usb_device(-1)
pid = device.spawn(package)
session = device.attach(pid)
OpenMemory_android6 = '_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9'
Open_android8 = '_ZN3art7DexFileC2EPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPKNS_10OatDexFileE'
src = """
Interceptor.attach(Module.findExportByName("libart.so", "_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_"), {
onEnter: function (args) {
//dex文件的起始位置
var begin = args[1]
//dex文件的前8个字节是magic字段 看dex的文件格式说明
//打印magic(会显示 "dex 035") 三个字符 可以验证是否为dex文件 dex 035 是换行的
send("magic : " + Memory.readUtf8String(begin))
//把地址转换成整型 再加32
//因为dex文件的第32个字节处存放的是 dex文件的大小
var address = parseInt(begin,16) + 0x20
//把address地址指向的内存值读出来 该值就是dex的文件大小
//ptr(address)转换的原因是 frida只接受 NativePointer类型指针
var dex_size = Memory.readInt(ptr(address))
send("dex_size :" + dex_size)
//frida写文件 把内存中的数据 写到本地
var file = new File("/data/data/%s/" + dex_size + ".dex", "wb")
//Memory.readByteArray(begin, length)
//把内存里的数据读出来,从begin开始读,取length长度(dex_size文件的大小)
file.write(Memory.readByteArray(begin, dex_size))
file.flush()
file.close()
var send_data = {}
send_data.base = parseInt(begin,16)
send_data.size = dex_size
send(send_data)
},
onLeave: function (retval) {
if (retval.toInt32() > 0) {
}
}
});
""" % (package)
script = session.create_script(src)
script.on("message", on_message)
script.load()
device.resume(pid)
sys.stdin.read()