1. 지식 보충
1. 보강 후 APP 운영 과정
1. -->APP启动
2. -->壳dex先加载起来
3. -->壳负责把源dex文件读出来
4. -->壳把源dex文件解密
5. -->把解密后的dex加载进内存 源dex运行起来
2. 포격의 원리
강화 된 APP가 시작된 후 최종 소스 dex 파일이 실행을 위해 메모리에로드됩니다.이 때 dex 파일은 강화 프로그램이 복호화 된 후 앱의 소스 코드 인 dex입니다.
Android 언 패킹의 목적은 복호화 된 애플리케이션 dex 파일을 메모리에서 덤프하는 것입니다.이 목적을 달성하려면 메모리에있는 dex 파일의 dex 주소와 dex 파일 크기를 알아야합니다. Android 시스템의 libart.so 라이브러리 파일은 dex 파일을로드하기 위해 내 보낸 OpenMemory 함수를 제공합니다.
이 함수의 첫 번째 매개 변수는 메모리에있는 dex 파일을 가리 킵니다.이 함수를 후크 할 수 있으면 dex 파일이 메모리에로드 될 때 시작 주소를 얻은 다음 파일에 저장된 dex 파일을 계산할 수 있습니다. dex 파일 형식 길이 fileSize에 따른 헤더.
dex 파일 형식 이해 : https://www.jianshu.com/p/f7f0a712ddfe
dex 시작 위치 : OpenMemory의 첫 번째 매개 변수
8个字节 magin ---> dex 035
4个字节 校验位
20个字节 签名
-----从32个字节开始------
4个字节 dex文件大小
이용 가능한 정보 :
- dex 시작 위치 (OpenMemory의 첫 번째 매개 변수)
- dex 파일 크기 (dex 시작 위치 + 32 바이트)
따라서 메모리의 데이터를 읽고 로컬로 쓸 수 있습니다.
// 把内存里的数据读出来,从begin(dex在内存中的起始位置)开始读,取length长度(dex_size文件的大小)
file.write(Memory.readByteArray(begin, dex_size))
// 将这段读取出来的数据写入本地
var file = new File("/data/data/%s/" + dex_size + ".dex", "wb")
2. 운영 프로세스 데모
Frida 스크립트를 작성하기 전에 libart.so 파일에서 OpenMemory의 내보내기 함수 이름을 찾아야합니다.이 함수 이름은 Android 버전 또는 아키텍처에 따라 약간 씩 다릅니다.
이 내 보낸 함수의 이름은 무엇입니까?
so 파일의 메소드에서 외부로 제공하는 인터페이스와 동일하며 Java 코드 OpenMemory
의 so 파일에서 메소드 호출 형식은 다음과 같습니다.
javaCode.OpenMemory的导出函数名
OpenMemory导出函数名与so文件中的OpenMemory方法存在映射关系
java代码中想要调用so库中的OpenMemory方法就以它的导出函数名调用
64 비트 libart.so 또는 32 비트가 응용 프로그램 작동 중에 사용되는지 여부에 따라 다른 librat.so를 내보낼 수 있습니다.
adb pull /system/lib/libart.so C:\Users\v_mcsong\Desktop
adb pull /system/lib64/libart.so C:\Users\v_mcsong\Desktop
32/64 비트 IDA를 사용하여 librat.so 파일을 열어 OpenMemory
내보내기 방법 이름 을 확인 합니다. Android 버전에 따라 내보내기 방법 이름이 다를 수 있습니다.
즉, 호출 _ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_
이 so 라이브러리에 있습니다.OpenMemory
쉘링 스크립트 :
참조 오픈 소스 프로젝트 : 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()
jadx를 사용하여 하나씩 열면 어떤 dex 파일이 앱의 소스 코드인지 확인할 수 있습니다.