Frida`脱壳
文件格式
盗用一张大佬做的图
dex
文件头包含以下各个字段:
magic
: 包含了 dex 文件标识符以及版本,从 0x00
开始,长度为 8 个字节
checksum: dex
文件校验码,偏移量为: 0x08
,长度为 4 个字节。
signature: dex sha-1
签名,偏移量为 0x0c
, 长度为 20 个字节
file_szie: dex
文件大小,偏移量为 0x20
,长度为 4 个字节
header_size: dex
文件头大小,偏移量为0x24
,长度为 4 个字节,一般为 0x70
js
脚本
var openMemory_address = Module.findExportByName('libart.so', '_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_')
//OpenMemory在libart.so中,art虚拟机(android5), daclink虚拟机(android4)
//Hook OpenMemory的导出方法名
//用ida打开libart.so,查看OpenMemory的导出方法名
//OpenMemory的第一个参数是dex文件在内存中的起始地址
//根据dex文件格式,从起始位置开始,第32个字节是该dex文件的大小
//知道dex起始位置和整个文件大小,只需要把这段内存dump出来即可
//适用于 安卓6 7 8 9
Interceptor.attach(openMemory_address, {
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){
}
}
})
python
脚本
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*]{0}".format(message['payload']))
else:
print(message)
package = 'com.***.***'
jscode = """
var openMemory_address = Module.findExportByName('libart.so', '_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_')
//OpenMemory在libart.so中,art虚拟机(android5), daclink虚拟机(android4)
//Hook OpenMemory的导出方法名
//用ida打开libart.so,查看OpenMemory的导出方法名
//OpenMemory的第一个参数是dex文件在内存中的起始地址
//根据dex文件格式,从起始位置开始,第32个字节是该dex文件的大小
//知道dex起始位置和整个文件大小,只需要把这段内存dump出来即可
//适用于 安卓6 7 8 9
Interceptor.attach(openMemory_address, {
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('输出目录为:/data/data/%s'%(package))
device = frida.get_remote_device()
# //先hook住
pid = device.spawn(['com.***.***'])
process = device.attach(pid)
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running CTF')
script.load()
# // 然后重启
device.resume(pid)
sys.stdin.read()