Frida`脱壳

文件格式

dex文件格式及Frida脱壳_android

盗用一张大佬做的图

dex文件格式及Frida脱壳_frida_02

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

dex文件格式及Frida脱壳_app逆向_03

dex文件格式及Frida脱壳_android_04

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()