Tabla de contenido
1. Suplemento de conocimientos
1. Proceso de operación de la aplicación después del refuerzo.
1. -->APP启动
2. -->壳dex先加载起来
3. -->壳负责把源dex文件读出来
4. -->壳把源dex文件解密
5. -->把解密后的dex加载进内存 源dex运行起来
2. El principio del bombardeo
Una vez que se inicia la aplicación reforzada, el archivo dex de origen final se cargará en la memoria para su ejecución. En este momento, el archivo dex es el dex después de que se descifre el programa de endurecimiento, que es el código fuente de la aplicación.
El propósito de descomprimir Android es volcar el archivo dex de la aplicación descifrado de la memoria. Para lograr este propósito, necesitamos saber la dirección dex y el tamaño del archivo dex del archivo dex en la memoria. El archivo de biblioteca libart.so del sistema Android proporciona una función OpenMemory exportada para cargar el archivo dex.
El primer parámetro de esta función apunta al archivo dex en la memoria.Si podemos conectar esta función, podemos obtener la dirección inicial del archivo dex cuando se carga en la memoria, y luego calcular el archivo dex guardado en el archivo. encabezado de acuerdo con el formato de archivo dex Length fileSize.
Comprenda el formato de archivo dex: https://www.jianshu.com/p/f7f0a712ddfe
posición inicial dex: el primer parámetro de OpenMemory
8个字节 magin ---> dex 035
4个字节 校验位
20个字节 签名
-----从32个字节开始------
4个字节 dex文件大小
Información disponible:
- posición inicial dex (el primer parámetro de OpenMemory)
- tamaño de archivo dex (posición inicial dex + 32 bytes)
Entonces podemos leer los datos en la memoria y escribirlos localmente
// 把内存里的数据读出来,从begin(dex在内存中的起始位置)开始读,取length长度(dex_size文件的大小)
file.write(Memory.readByteArray(begin, dex_size))
// 将这段读取出来的数据写入本地
var file = new File("/data/data/%s/" + dex_size + ".dex", "wb")
2. Demostración del proceso operativo
Antes de escribir el script de Frida, necesitamos encontrar el nombre de la función de exportación de OpenMemory en el archivo libart.so. Este nombre de función variará ligeramente dependiendo de la versión o arquitectura de Android.
¿Cuál es el nombre de esta función exportada?
Es equivalente a la interfaz proporcionada por el método en el archivo so al exterior OpenMemory
. La forma de llamada del método en el archivo so en el código Java es:
javaCode.OpenMemory的导出函数名
OpenMemory导出函数名与so文件中的OpenMemory方法存在映射关系
java代码中想要调用so库中的OpenMemory方法就以它的导出函数名调用
Se pueden exportar diferentes librat.so según se utilice libart.so de 64 bits o de 32 bits durante la operación de la aplicación.
adb pull /system/lib/libart.so C:\Users\v_mcsong\Desktop
adb pull /system/lib64/libart.so C:\Users\v_mcsong\Desktop
Utilice IDA de 32/64 bits para abrir el archivo librat.so y ver OpenMemory
el nombre del método de exportación. Las diferentes versiones de Android pueden tener diferentes nombres de método de exportación.
Es decir, la llamada _ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_
está en la biblioteca soOpenMemory
Guión de bombardeo:
Proyecto de código abierto de referencia: https://github.com/dstmath/frida-unpack/blob/master/frida_unpack.py
#-*- coding:utf-8 -*-
# coding=utf-8
import frida
import sys
def on_message(message, data):
base = message['payload']['base']
size = int(message['payload']['size'])
print(hex(base),size)
# print session
# dex_bytes = session.read_bytes(base, size)
# f = open("1.dex","wb")
# f.write(dex_bytes)
# f.close()
### 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]
print("dex 导出目录为: /data/data/%s"%(package))
device = frida.get_usb_device()
pid = device.spawn(package)
session = device.attach(pid)
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文件
console.log("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))
console.log("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()
Use jadx para abrir uno por uno y ver qué archivo dex es el código fuente de la aplicación.
Artículo de referencia: https://blog.51cto.com/yeshaochen/2496524