內存解析Il2cpp函數地址

2021-02-13 看雪學院

本文為看雪論壇精華文章

看雪論壇作者ID:唱過阡陌

目的起因1. 相對u3d遊戲進行hook基本上離不開 Il2CppDumper 的使用,有時候想要找個函數什麼的就很麻煩,第一步就得使用上面這工具來找到函數地址。2. 第二就是每次都要用 Il2CppDumper 來找函數地址不能方便的實現 frida 腳本的自動化,既然都用到了 frida 當然是越方便越爽。
經過之前搞了一個通過 Il2CppDumper 拿到的 script.js 加上一點python 篩選,來實現對函數的批量斷點,方便實現對點擊事件或者是其他關鍵函數(Like:Show,Click,Button,Rewarded)多函數的批量斷點,方便我們分析代碼調用。
這還是不夠優雅,要讓他自動去拿到這些地址,我們就得讀一點點,在源碼層面去hook拿到一些我們需要的關鍵點。
分析

網上也有很多類似的相關文章,比如:

[unity]Real-time Dump:https://floe-ice.cn/archives/502

一套強勁的 Il2cpp Hook 框架:https://www.52pojie.cn/thread-978958-1-1.html

IL2CPP的原理:https://blog.csdn.net/feibabeibei_beibei/article/details/95922520

都可以去參照、去細品。


分析的入口點從加載 global-metadata.dat 入手。這是一個非常標誌性的入口,不管是在源碼還是在IDA中都很容易的定位:IDA中從字符串窗口搜索該關鍵詞也可以輕鬆的定位到一下代碼。IDA中F5也是隨意的改改名字,改改結構體就差不多一個樣了。


在源碼中讓後看,可以看下面就開始對此處分配出來的內存填數據了,後面我們用的時候也是無需但是未初始化的問題。這裡的s_ImagesTable是一個指向一個 Il2CppImage 列表的開頭,sizeof(Il2CppImage) = 52,可以去頭文件查看。這一步我們關注的只有 const char *nameNoExt 以及當前位置的指針位置由此寫出。

這些玩意其實就是我們用Il2CppDumper拿到的哪些dll。


然後下面就可以進入正題。使用命名空間,類名,方法名,參數個數拿到我們需要的函數 內存地址以及IDA中靜態分析的地址。下面介紹兩個主角函數(這裡引入的兩個函數都是libil2cpp.so的默認導出函數)。
位置:D:\Program Files\Unity\Editor\Data\il2cpp\libil2cpp\il2cpp-api.cpp* il2cpp_class_get_method_from_nameps:這裡做的事情其實,就是和做數學一樣,把未知量用已知函數去代替,帶入到我們能解決即可。結果展示:

看到此處的話目的算是勉強達到了,但是還有待優化把,比如一開始的這兩個位置需要我們手動IDA去找。新的問題


從上述結果中確實看到了我們可以成功的調用函數拿到函數的返回值,但是僅限於我們已知Il2CppImage*的時候有效,也就是僅僅在我們list_Images中列出的地址對應的子函數可以這麼操作(使用il2cpp_class_from_name()獲取對應的Il2CppClass),所以我們為了通用得考慮新的思路。兩個關鍵結構體:Il2CppImage 和 Il2CppAssembly。可以看到第三個指針指向的地址的第一個區域由指回來了,就是咋們的Il2CppImage*。上面這張圖不是重點,重點在上上張圖第二三個圈起來的部分,也就是TypeDefinitionIndex typeStart;
uint32_t typeCount;
這兩位,分別記錄了偏移位置和方法個數,偏移位置的開始在(還是去參考IDA拿到地址,同樣也是bss段的一個指針)。根據上述SeeHexA(0xcf80ec00)中可知偏移0x0,方法個數0x5c2(即1474個方法),接下來就可以寫出以下demo。上述故意方法數多加一,看到最後空命名空間和<Module>基本就穩了,就是這意思沒猜錯。
拓展讓 Il2CppDumperTool 脫離 Il2CppDumper 以及python腳本更易用。* UnityEngine.GameObject.SetActive(Boolean)* UnityEngine.Object.GetName(UnityEngine:Object):String* UnityEngine.Application.get_identifier():String* UnityEngine.PlayerPrefs.GetInt(String,Int32):Int32以上展示只是一個簡單的分析,其實就上述這樣東西還是非常不好用,上述的addr並不能找到所有的方法及其地址,後續就想著讓他更加智能一點,就嘗試手動去解析一下結構體,然後就得到了一下效果。首先是針對我們需要的三個參數的自動解析,主要涉及到ldr尋址:這裡為了實現找到我們需要的參數,主要是通過導出函數il2cpp_init的地址往下找找到bl進去然後再找到ldr,並解析到ldr加載的值,如果出現了「mscorlib.dll」,說明我們找到的bl是正確的,然後我們同理找下層的「global-metadata.dat」即可定位到函數 bool il2cpp::vm::MetadataCache::Initialize()。找到上述位置後,我們稍微看一下就可以發現特徵 IL2CPP_CALLOC 函數,老規矩通過這個bl指令去解析到函數地址,記錄所有調用的該函數的當前地址放進數組,接下來就是 通過數組訪問到 [1] [4] [5] 位置就是我們需要的三個初始參數,從當前位置往上解析它,第一條 mov r0,#xx 的立即數就是我們要找的,好了介紹到這裡,上效果:這種找法效果還行,能解決大部分問題,也不能解決全部問題,總有些奇葩adr之類的東西,這些就自己手動IDA查看了。

然後就是挨著列出:

也可以解析的更加詳細(其實還可以繼續深入解析出方法類型返回值類型的,暫時沒有繼續深入)用新的方法來解析找函數就不存在解析不到函數的問題了(隨意解析這個so裡面的任意函數)

實際用處隨便舉個例:斷點 public extern void SetActive(GameObject obj,bool value);
1. 有了gameObject就可以拿到transform,以及他的名字,再遍歷拿到父級名字,整個調用鏈你都清晰了。2. 有了gameObject用以上的函數API可以隨意的移動,顯示隱藏 ...‍

看雪ID:唱過阡陌

https://bbs.pediy.com/user-home-868525.htm

  *本文由看雪論壇 唱過阡陌 原創,轉載請註明來自看雪社區。

相關焦點

  • 用Unity做遊戲,你需要深入了解一下IL2CPP
    運行時庫則提供諸如垃圾回收,與平臺無關的線程,IO以及內部調用(C++原生代碼直接訪問託管代碼結構)這樣的服務和抽象層。AOT編譯器IL2CPP AOT編譯器實際的執行文件是il2cpp.exe。在Windows平臺你可以在Unity安裝路徑的Editor\Data\il2cpp目錄下找到。
  • 程序丨Unity Android APP il2cpp熱更新解決方案
    這是Unity Android APP il2cpp熱更解決方案的說明,Demo地址可點擊文末閱讀原文獲取。
  • C語言函數調用過程中的內存變化解析
    打開APP C語言函數調用過程中的內存變化解析 TOMORROW 星辰 發表於 2020-12-11 16:21:13 局部變量的作用域為什麼僅限於函數內?這個調用不是指C 語言上的函數調用的語法,而是在內存的視角下,函數的調用過程。本文將從C 語言調用實例,內存視角,反彙編代碼來探討C 語言函數的調用過程,也可以說是C 語言函數調用過程圖解。通過這個C 語言函數調用過程圖解,同學們將會知道,C 語言函數在調用時,內存空間是怎樣變化的。 要想理解這一個過程還好涉及到函數棧幀的概念。
  • Android免Root權限Hook系統函數修改程序運行時內存指令邏輯
    ,通過讀取maps文件,獲取到dex文件的內存起始地址,然後通過文件頭信息找到指定dex在內存中的數據結構,這裡還需要詳細了解dex文件的格式,不了解的同學可以點擊看這篇文章:Android中Dex文件格式解析,然後使用系統函數修改內存讀寫屬性,在通過指定方法名找到該方法在內存的指令地址,然後替換即可。
  • c++之內存分配、命名空間、強制類型轉換學習總結
    一、C++動態內存分配:在學習c語言的時候,我們一般都是使用庫函數malloc()來進行內存的申請分配,然後使用庫函數free()來進行釋放申請到的內存;現在在c++裡面採用了另外一種內存申請的方法:c++中通過
  • 內存初始化代碼分析(三):創建系統內存地址映射
    當然本文不能全部解析完該函數(那需要的篇幅太長了),我們只關注創建系統內存地址映射這部分代碼實現,也就是解析paging_init中的map_mem函數。同樣的,我們選擇的是4.4.6的內核代碼,體系結構相關的代碼來自ARM64。 二、準備階段在進入實際的代碼分析之前,我們首先回頭看看目前內存的狀態。
  • 周立功:動態分布內存——free()函數與realloc()函數
    要求每個程序負責回收各自的垃圾,方法是調用free()函數釋放不需要的內存。通常malloc()要與free()配套使用,當動態內存使用完畢時,如果不及時釋放的話,必然導致「內存洩露(即內存空間減少)」,進而影響程序的正常運行。
  • 周立功:動態分布內存——malloc()函數與calloc()函數
    程序在運行時分配的內存空間稱之為「堆」的存儲池,雖然計算機在硬體上不直接支持堆,但C函數庫(stdlib.h)分別提供了用於動態內存分配和釋放的函數malloc()和free(),即在運行時根據需要創建一個存儲單元,在不需要時釋放。
  • Linux平臺中調試C/C++內存洩漏方法 (騰訊和MTK面試的時候問到的)
    內存洩漏一般指的是堆內存的洩漏。堆內存是指程序從堆中分配的、大小任意的(內存塊的大小可以在程序 運行期決定)、使用完後必須顯示的釋放的內存。應用程式一般使用malloc、realloc、new 等函數從堆中分配到一塊內存,使用完後,程序必須負責相應的調用 free 或 delete 釋放該內存塊。否則,這塊內存就不能被再次使用,我們就說這塊內存洩漏了。1.
  • C++之拷貝構造函數的淺copy和深copy
    test.cpp: In function 『int main()』:test.cpp:25:9: error: no matching function for call to 『Test::Test()』    Test t;         ^test.cpp:25:9: note: candidates are:test.cpp:15:3: note
  • c++ 內存,虛函數,運算函數,三角函數
    棧內存:在函數中定義的一些基本類型的變量和對象的引用變量都在函數的棧內存中分配,存取速度比堆要快,僅次於直接位於CPU中的寄存器。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。堆內存:用來存放由new創建的對象和數組。在堆中分配的內存,由Java虛擬機的自動垃圾回收器來管理。
  • 為何C++拷貝構造函數參數必須為引用形式
    1.1  傳值      對於傳值方式,當對象或內置類型(eg:int、double、float等)數據傳遞給函數時候,函數將會為每個實參(即待傳給函數的參數)做一份拷貝操作,這也就是通常所說的「副本」,這樣一來,就需要分配額外的內存空間,所有值和子對象都被分別複製和存儲。
  • MicroBlaze:malloc 函數動態分配內存溢出
    首先說明一點,MicroBlaze C函數庫支持標準的內存管理函數,如malloc(),calloc(), free(),這些標準的C函數庫定義在libc.a中。動態內存分配從內存的程序的堆(heap)中提供。
  • JavaScript中的內存釋放
    01如何查找上級作用域在《JavaScript中的預解析》,有講到作用域鏈的概念,本次在正式講JavaScript
  • 每日一問(11) 什麼是虛函數
    別人都知道,我不知道 才是最尷尬的地方C++通過指針實現了多態,運行時函數重載決議,是他最有優秀地方,但是也是最讓人痛苦地方,內存模型假設存在讓對象生命周期管理更加複雜。問題3:什麼是move copy標準庫:如何使用疑問:既然虛函數表,在編譯時候確定了,並且放到只讀數據段,問題來了,如果繼承重寫需要修改裡面函數地址給怎辦?
  • 函數重載
    文章中關於報錯「無法解析的外部符號」這一問題得到的答案是:C和C++代碼在編譯成目標文件時對函數的命名不同。今天討論下為何會導致函數名不同。       還是上次的工程,工程結構如下       我們已經知道了兩種類型文件的同一函數生成的目標文件的不同,如下兩圖分別為」Main.cpp」和「Main.c」生成的兩個目標文件