天天看點

安卓逆向——frida-serve脫殼案例安卓逆向——frida-serve脫殼案例

安卓逆向——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 讀進記憶體的方法

安卓逆向——frida-serve脫殼案例安卓逆向——frida-serve脫殼案例

執行後 會出現 幾個dex檔案,這裡需要一個一個用 jadx 打開看,

安卓逆向——frida-serve脫殼案例安卓逆向——frida-serve脫殼案例

打開檔案 可以看到 和上一篇的dex檔案分析是一樣的

安卓逆向——frida-serve脫殼案例安卓逆向——frida-serve脫殼案例

環境二 : Android8 

  OpenCommon:OpenCommon 是Android8 把 dex 讀進記憶體的方法

安卓逆向——frida-serve脫殼案例安卓逆向——frida-serve脫殼案例

執行後 會出來幾個dex檔案,出來的dex個數可能不一樣,但是隻要正确的dex檔案出來了就好

安卓逆向——frida-serve脫殼案例安卓逆向——frida-serve脫殼案例

打開檔案 可以看到 和上一篇的dex檔案分析是一樣的

安卓逆向——frida-serve脫殼案例安卓逆向——frida-serve脫殼案例

注意:不同系統可能存在不同的差別

代碼 :

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