某Guard的SO加固探秘(有趣的GNUHash)

查看 25|回复 3
作者:ngiokweng   
1. 前言
最近瑣事一堆,而且也有點懶惰,分析周期拉得挺長的。
動調分析了很多次,每次都有新發現,這也使得文章中很多前面的部份是後面補充上去的。
注:本文只分析該加固的具體流程,以及修復的思路。
2. 簡單處理反調試
只處理anti ida debug的部份,能順利動調就足夠了。
通過hook strtstr來bypass,然後以frida -U -f XXX -l test.js --pause的方式啟動APP,之後IDA再attach。
function hook_strstr() {
    // 更多比較函數: strcmp、strncmp、memcmp
    var pfn_strstr = Module.findExportByName("libc.so", "strstr");
    Interceptor.attach(pfn_strstr, {
        onEnter: function (args) {
            var str1 = Memory.readCString(args[0]);
            var str2 = Memory.readCString(args[1]);
            if (str1.indexOf("TracerPid:") != -1) {
                Memory.writeUtf8String(args[0], "TracerPid:     0");
                console.log(`[hook_strstr] ${str1} ---> ${args[0].readCString()}`)
            }
        },
        onLeave: function (retval) {
        }
    });
}
然後就可以愉快地動調分析了^^,主要邏輯都在.init_array中。
3. init_array_func1
最開始會調用check_emu_and_get_lib_info()檢查模擬器,並且獲取一些lib信息( 包括libc.so、liblog.so、libstd++.so、libOpenSLES.so、libmediandk.so ),這些信息被保存在g_somedataX中,如libc.so的信息就在g_somedata1。


image.png (35.49 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

3.1 模擬器檢查 & 保存lib信息
進入check_emu_and_get_libc_info()函數,看看具體實現。
首先調用了get_infos(),其中會通過openat系統調用打開/proc/self/maps,返回的fd保存在infos中。同時也把read、close等系統調用保存到infos中。


image1.png (19.76 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

然後調用get_maps_item()遍歷/proc/self/maps。
maps_item是諸如12c00000-12c40000 rw-p 00000000 00:00 0  XXX這樣的字符串。


image2.png (47.55 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

遍歷/proc/self/maps的目的是為了獲取指定幾個lib的信息,下面以libOpenSLES.so為例看看它是如何處理的。
通過以下方式,將hex字串的基址轉換為num形式,記為libOpenSLES_base。


image3.png (46.69 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

轉換完基址後,進行了一些合法性檢查。


image4.png (44.18 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

然後獲取了該so的e_machine,若是62或3,就代表是EM_X86_64或EM_386,一般模擬器就是這兩個架構之一。
檢測到後會記錄在g_mb_emu_flag。


image5.png (36.04 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

至於上述轉換的基址最終會被保存在g_somedataX全局變量中,如libc.so的就保存在*(_QWORD *)(g_somedata1 + 88)。


image6.png (16.99 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

3.2 對lib的簡易預鏈接
回到init_array_func1()繼續分析。
下面是對libc.so進行了類似prelink_image()的操作,即遍歷了libc.so的.dynamic,相關數據被保存在g_somedata1中。


image7.png (52.93 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传



image8.png (51.63 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

除了對libc.so外,還有對libstdc++.so和liblog.so進行了上述操作。
3.3 got表替換
之後解密了一些諸如dlopen、dlsym、dlerror、dlclose等字串。
遍歷libc.so的jmprel表( 重定向表 ),記錄所有dl系列的函數地址,如dlopen函數地址被保存在g_dlopen中。


image9.png (60.92 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传



image10.png (36.28 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

獲取完dl系列的函數後,之後又是一堆的內聯形式的字符串解密,解密了一堆函數名存放在各個變量中。


image11.png (24.09 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

然後初始化了一個SBOX,大概是用於之後某處的加/解密,應該不用太關注。


image12.png (66.33 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

再之後調用了mprotect系統調用,賦予libil2cpp.so前0x1000可讀可寫的權限。


image13.png (8.47 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

繼續向下看,上面解密的部份字符串如下所示,可以看到基本上都是一些函數名,分佈在不同lib中。


image14.png (40.75 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

接下來以_Znwm為例繼續分析後續的流程。
_Znwm應該是屬於libstdc++.so的函數,因此當遍歷到_Znwm時會跳到如下地方,然後從g_somedata3 + 88獲取libstdc++.so的基址。


image15.png (30.61 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

繼續向下單步執行,會走到下圖這裡,看到5381這個關鍵值。
如果有看過AOSP的SymbolName::gnu_hash(),會發現這個正是其中GNU HASH的初始值,後續的循環邏輯也與源碼中一致。


image16.png (26.03 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

之後的計算過程也與soinfo::gnu_lookup()中大同小異,該函數作用於linker的relocate(),它通過特殊的GNU HASH邏輯,能快速計算出指定符號名對應的符號索引,之後linker就能通過符號索引取得對應符號的地址。
下圖的邏輯基本上就是對soinfo::gnu_lookup()的模擬,符號索引記為n。


image17.png (55.52 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

base_是libstdc++.so的基址,symbol是symtab[n]( 這個symtab是libstdc++.so的符號表 ),因此下圖執行後,base_就是_Znwm的真實地址。


image18.png (14.43 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

取得_Znwm的真實地址後,會其賦給libil2cpp.so的某處。


image19.png (15.56 KB, 下载次数: 0)
下载附件
2025-6-19 22:26 上传

這個「某處」在.got表,原本是tan函數,由此可知.got中的一大堆tan函數應該是作為占位函數的存在。


image20.png (34.19 KB, 下载次数: 0)
下载附件
2025-6-19 22:27 上传



image21.png (34.83 KB, 下载次数: 0)
下载附件
2025-6-19 22:27 上传

比較特別的是,一些函數如memcpy、memset等,它雖然同樣會按上述GNU HASH的方式取得其真實地址,但在替換時卻不會使用,而是將替換為自實現的memcpy、memset,一定程度上增加了安全性。


image22.png (27.47 KB, 下载次数: 0)
下载附件
2025-6-19 22:27 上传

最後遍歷完所有需要替換的函數時,會再次調用mprotect系統調用收回寫權限。


image23.png (16.46 KB, 下载次数: 0)
下载附件
2025-6-19 22:27 上传

注:_Znwm其實是operator new。


image24.png (24.94 KB, 下载次数: 0)
下载附件
2025-6-19 22:27 上传

4. init_array_func2
tan_new()是指原本是tan()函數,但在init_array_func1()中被替換為_Znwm()函數( new )。
g_from_initarray2全局變量中會保存一些殼so的信息,以及後續解密會用到的參數等。


image25.png (30.83 KB, 下载次数: 0)
下载附件
2025-6-19 22:27 上传

init_function()中把一堆函數賦給了result,最終result被保存在全局變量g_func_array中。


image26.png (62.97 KB, 下载次数: 0)
下载附件
2025-6-19 22:27 上传

一開始無法直接看出unknow_func()的作用,大概只能看出其中解密了一句有意思的字串nichoushazaichouxiashishi。


image27.png (27.5 KB, 下载次数: 0)
下载附件
2025-6-19 22:27 上传

而在後續通過對init_array_func3()的分析,可以知道unknow_func()干了以下事情:
[ol]
  • 保存殼so的.dynamic中的某幾項。
    [/ol]


    image28.png (13.42 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    如殼so的符號表被保存在g_from_initarray2 + 96。


    image29.png (22.21 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    [ol]
  • unknow_func()中的一堆計算是為了生成一個異或值,用於init_array_func3()裡解密符號表。該異或值被保存在g_from_initarray2 + 120。
    [/ol]


    image30.png (52.51 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    5. init_array_func3
    init_array_func3()分成了3部份,前2部份是主要邏輯,最後1個函數大概只是在收尾。


    image31.png (17.98 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    5.1 init_something1
    init_something1()如下,把一些函數、g_func_array等賦給了a1,而a1等下又會作為第0個參數被傳入main_func()。


    image32.png (45.72 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    5.2 main_func
    main_func()一開始會間接調用0x2AD8380( 記為decrypt1 ),其中解密了子so的strtab、rela、.dynamic、代碼段等信息。


    image33.png (33.38 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    5.2.1 子so信息解密
    接下來先分析decrypt1()的具體實現。開始是一大堆加/解密table的初始化。


    image34.png (54.69 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    然後是第1處的字符串解密邏輯,解密的起始位置是0x458。


    image35.png (33.37 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    解密前/後如下所示。


    image36.png (24.79 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传



    image37.png (24 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    然後是第2處字符串解密邏輯,這處的邏輯會被多次調用。


    image38.png (24.84 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    第3處字符串解密邏輯。


    image39.png (23.28 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    第4處字符串解密邏輯。


    image40.png (36.49 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    第5處字符串解密邏輯。


    image41.png (22.35 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    大概共有5處字符串解密邏輯,所有均為內聯的形式( 不像傳統加固那樣具有一個統一個字符串解密函數 )。
    之後會在下圖JUMPOUT處解密子so的重定向表、.dynamic信息、代碼段等信息。


    image42.png (19.51 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    JUMPOUT裡的第1處解密邏輯如下,這裡解密的是子so的部份重定向信息。


    image43.png (37.54 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    接著是第2處解密邏輯,這裡不單只會解密子so的重定向表,還會解密子so的.dynamic信息、代碼段等信息。


    image44.png (37.31 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    5.2.2 子so預鏈接
    回到main_func()。
    之後會根據decrypt1()中解密的.dynamic信息進行預鏈接,相關數據被保存在soinfo變量中。
    注:雖然解密的子so.dynamic信息中包含符號表,但實際上這裡的預鏈接並沒有存儲子so的符號表信息,後續「解密子so符號表」中解密&使用的符號表都是殼so的( 從g_from_initarray2 + 96中獲取 )。


    image45.png (55.21 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:27 上传

    而d_tag == 1( DT_NEEDED )的情況會在之後單獨處理,這裡先將子so的所有DT_NEEDED庫名保存在m_addr( 由malloc而來的一片內存空間 )中。
    m_addr可以理解成一個數組,每個元素的大小為0xAC,第1個屬性是庫名,之後就是一些預鏈接信息。


    image46.png (52.94 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    而後會調用prelink_DT_NEEDED(),該函數大概是對子so的依賴庫進行prelink_image()操作。


    image47.png (18.22 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    執行prelink_DT_NEEDED()前,m_addr如下,只有庫名。


    image48.png (37.4 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    執行後,m_addr多了對應庫的預鏈接的信息,如第1個紅框是app_process符號表的地址,第2個紅框是字符串表。


    image49.png (49.46 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    接著就是子so的重定向工作。
    5.2.3 子so的DT_JMPREL重定向
    下面是第1處重定向邏輯,用的重定向表是上面預鏈接時的DT_JMPREL(23)。
    當sym為0時,重定向過程如下。其中soinfo[3]是base,my_rela是自定義的重定向表中的一項元素。
  • *(base + *my_rela)本身指向一個大偏移值,如0x26FD768,這個值應該也是在decrypt1()裡解密的。
  • my_rela[2]是一個小偏移值,如0x10。



    image50.png (20.88 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    my_rela是類似如下的三元組,大致可以對應常規的,不同的是r_info中沒有type信息( 如0x403、0x402等重定向類型 )。
    LOAD:00000074BD5BBC8C DCQ 0x26F8778
    LOAD:00000074BD5BBC94 DCQ 0
    LOAD:00000074BD5BBC9C DCQ 0x10
    當sym不為0時,會先從strtab獲取sym對應的字符串,假設是"free",然後調用get_target_addr()嘗試尋找函數地址,有以下幾種情況:
    [ol]
  • 目標函數在g_func_array中,直接從其中返回對應函數地址。
  • 目標函數是自實現的,如memcpy、memset等,直接返回對應自實現的函數地址。
  • 目標函數是dl系列的,直接返回g_dlopen、g_dlsym等全局變量( 這些全局變量是在之前已經賦值的 )。
  • 找不到時,默認返回0。
    [/ol]


    image51.png (31.28 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    若get_target_addr()成功返回對應函數地址,則直接在下面這裡進行重定向。


    image52.png (21.31 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    若get_target_addr()返回0,則會遍歷保存在m_addr的子so依據庫,然後進行GNU HASH看看目標符號是否在指定so中。


    image53.png (44.67 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    通過GNU HASH成功找到符號偏移,加上基址就是目標函數的真實地址。
    最終根據my_rela將該函數地址賦給對應地方,完成重定向( 類似0x401重定向 )。


    image54.png (27.98 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    5.2.4 子so的DT_RELA重定向
    然後是調用relocate()進行第2處重定向邏輯,用的重定向表是上面預鏈接時的DT_RELA(7),記這個重定向表為rela。


    image55.png (20.58 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    relocate()的實現就跟linker的實現比較一致了,可以看到熟悉的0x401、0x101等重定向類型了。


    image56.png (45.76 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    通過以下腳本檢查rela重定向表的類型,會發現只有0x403重定向。
    import struct
    type_map = {}
    def parse_my_rela_item(item):
        global g_libil2cpp_data
        # '
    5.2.5 清理現場
    可以選擇在這個時機dump一些子so的解密數據,如子so的字符串表、.dynamic信息和重定向表。
    relocate()後,會清空子so字符串表,起始偏移是0x45C,循環裡清空了0xA20字節,之後又單獨清空了8字節,共0xA28字節,由這裡可以看出字符串表的真實範圍是由0x45C → 0xE84。


    image57.png (23.77 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    然後清空子so的.dynamic信息,範圍由0x26FFB28 → 0x26FFCB8


    image58.png (28.75 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    然後清空rela,範圍由0xE84 → 0x552C74。


    image59.png (21.21 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    最後清空my_rela( 大概是自定義的重定向表 ),範圍由0x552C74 → 0x55FF04。


    image60.png (22.71 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    5.2.6 解密子so符號表
    完成子so的預鏈接和重定向後,會解密子so的符號表( 以及解密對應的符號名 )。
    而上面提到,在對子so進行預鏈接時並不包括符號表的部份,因此這裡解密的其實是殼so的符號表,以及殼so的字符串表。
    so文件每個符號表項的結構定義如下:
    struct Elf64_Sym {
      [4] Elf64_Word      st_name;  // Symbol name (index into string table)
      [1] unsigned char   st_info;  // Symbol's type and binding attributes
      [1] unsigned char   st_other; // Must be zero; reserved
      [2] Elf64_Half      st_shndx; // Which section (header tbl index) it's defined in
      [8] Elf64_Addr      st_value; // Value or address associated with the symbol
      [8] Elf64_Xword     st_size;  // Size of the symbol
    }
    根據st_other是否0x10來判斷當前符號( Elf64_Sym )是否需要解密。


    image61.png (52.22 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    Elf64_Sym中有兩個東西需要解密:
    [ol]
  • st_name指向的字符串。
  • st_value符號偏移值。
    [/ol]
    首先會解密st_name指向的字符串,同樣是通過多處內聯的形式進行解密。


    image62.png (26.86 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传



    image63.png (23.36 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    然後就是解密st_value,解密邏輯是個簡單的異或,而該異或值是*(g_from_initarray2 + 120),它是在init_array_func2()的unknow_func()中計算出來的,固定是0xFDE673F1。


    image64.png (18.31 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    一般的符號的解密流程如上所述,而在遇到il2cpp_domain_get_assemblies時會做一些特殊處理,不再是異或0xFDE673F1,而是直接賦予一個函數偏移。


    image65.png (50.88 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    另一種特殊情況是st_other == 0x30。


    image66.png (51.39 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:29 上传

    發現該符號名解密後是JNI_OnLoad( 當然這並不代表st_other == 0x30就是獨指JNI_OnLoad的情況 )。


    image67.png (8.83 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:29 上传

    由此大概可以知道,st_other是分類標誌,0x10代表libil2cpp.so的符號( 如il2cpp_XXX ),0x30代表其他符號等等。
    5.2.7 main_func最後
    調用了do_something3(),在其中把libFairGuard.so中某個函數賦給了一個全局變量。然後是一些清理堆棧的操作。
    一開始沒看懂do_something3()的作用,後來看乐子人大佬的文章才發現裡面貌似hook了libil2cpp.so的某個函數,目的是為了解密global-metadata.dat。


    image68.png (43.08 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:29 上传

    最後間接調用了一堆函數,應該就是子so的初始化函數。


    image69.png (29.69 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:29 上传

    6. so修復
    簡單記錄下修復思路,按該思路是可以完美修復的。
    ( 完整的修復腳本就不放出來了,免得又被和諧了 )
    6.1 前置準備
    根據上述分析,在合適的時機dump以下數據。
  • 子so的字符串表( 0x45C → 0xE84 ),記為strtab。
  • 子so的DT_RELA( 0xE84 → 0x552C74 ),記為rela。
  • 子so的DT_JMPREL( 0x552C74 → 0x55FF04 ),記為my_rela。
  • 子so的動態段信息( 0x26FFB28 → 0x26FFCB8 ),記為dyn。
  • 殼so的字符串表( 0x2ACE9F8 → 0x2AD0360 ),記為k_strtab。
  • 殼so的符號表( 0x2AC89C8 → 0x2ACE9F8 ),記為k_symtab。
  • 子so的代碼段等信息,記為0x55FF04_0x289B0A0。

    上面分析時提到my_rela與一般的重定向項不同,因此為了讓linker能順利識別,需要手動轉換一下。
    轉換思路是將my_rela每項都變為0x403或0x402重定向,前者直接轉換即可,後者比較麻煩,要修改為對應的符號索引。
    觀察後會發現殼so的符號表中存在一堆沒用的空符號,可以利用這些空符號。
    記轉換後的my_rela記為my_rela_convert。
    6.2 正式開始用010Editor進行修復
    載體為最原本的libil2cpp.so。
    殼so的.dynamic的size為0x320,而實際大小只有0x1B0,大概是為了兼容子so的.dynamic。


    image70.png (25.04 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:29 上传

    因此可以直接將dyn複製到對應位置( 0x2B0C010 )。
    將其中DT_STRTAB(5)那項改為殼so的字符串表( 0x2ACE9F8 )、將其中DT_SYMTAB(6)那項改為殼so的符號表( 0x2AC89C8 )。
    目前這兩個位置( 0x2ACE9F8、0x2AC89C8 )仍是一些加密數據,後面會將其覆蓋為對應的解密數據。


    image71.png (49.87 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    然後修復DT_JMPREL(0x17)和DT_RELA(7)對應的兩張不同的重定向表。
    前者的起始偏移是0x552C74,將my_rela_convert複製到這裡。


    image72.png (97.64 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    後者的起始偏移是0xE84,將rela複製到這裡。


    image73.png (83.62 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    然後將k_strtab複製到0x2ACE9F8。


    image74.png (65.05 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    然後將strtab( 子so字符串表 )複製到0x2AD0360( 殼so字符串表結束位置 )。
    注:字符串表的大小可以從.dynamic裡獲取。


    image75.png (67.69 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    將k_symtab複製到0x2ac89c8( 殼so符號表 )。


    image76.png (48.49 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    將0x55FF04_0x289B0A0複製到0x55FF04位置,其中包含解密後的子so代碼段、數據段等信息。


    image77.png (53.23 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    這樣修復完後,雖然還不能替換到原APP中,但已經可以使用Il2cppDumper,只需手動查找CodeRegistration和MetadataRegistration即可。
    Input CodeRegistration: 0x26F8D30
    Input MetadataRegistration: 0x26F9A80
    若追求完美修復( 即替換APP的libil2cpp.so且不會閃退 ),可檢查以下地方( 結合linker日志來分析 ):
    [ol]

  • strtab_size_是否正確。

  • .dynamic信息中需要修改的有:DT_NEEDED、DT_HASH、DT_VERNEED、DT_VERNEEDNUM。

  • .got表是否有寫權限,如沒有,則手動修改phdr table ( 將PT_GNU_RELROy那段修改為RW的loadable seg )

  • 將子so的JNI_OnLoad替換為真實的JNI_OnLoad,原本的JNI_OnLoad會用到殼so在init_array初始化的一些變量,若子so調用了原本的JNI_OnLoad會crash。
    嘗試過添加殼so的init_array函數,但在執行殼so的init_array函數時又會有其他環境問題。

  • 把解密後的global-metadat.dat dump出來,然後替換到指定位置( 該位置可從maps裡查看 )。
    [/ol]
    7. About U3D
    用dump出來的global-metadata.dat,加上完美修復後的libil2cpp.so,可以直接使用Il2cppDumper。


    image78.png (12.43 KB, 下载次数: 0)
    下载附件
    2025-6-19 22:28 上传

    8. 結語
    經過之前分析某盾,本以為修復so會花很長時間,沒想到比想像中快得多,反而是加固流程的分析花了比較多的時間。總的來說該加固整體的感覺和某盾是挺像的,難度也在伯仲之間。
    最後特別感謝乐子人大佬的文章,對修復so的幫助很大。

    下载次数, 下载附件

  • xixicoco   

    乐子人大佬牛逼啊
    longshenger   


    xixicoco 发表于 2025-6-19 22:55
    乐子人大佬牛逼啊

    搜了一下,没找到这位大佬啊,用户名,帖子关键字,都没查着
    ngiokweng
    OP
      


    longshenger 发表于 2025-6-19 23:35
    搜了一下,没找到这位大佬啊,用户名,帖子关键字,都没查着

    在「看雪」上
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部