UE4(5)逆向學習筆記(三)——UEDumper原始碼學習

MLKSJ發表於2024-09-11

目錄
  • 0.前言
  • 1.準備
  • 2.開始閱讀
    • 2.1 設定版本和Offset
    • 2.2 獲取GName
    • 2.3 使用GName
    • 2.4 獲取GUObjectArray
    • 2.5 使用GUObjectArray
    • 2.6 尋找dump主流程
      • 2.6.1 ObjectsManager::copyGObjectPtrs
      • 2.6.2 ObjectsManager::copyUBigObjects
      • 2.6.3 EngineCore::cacheFNames
      • 2.6.4 EngineCore::generatePackages
  • 3.疑問
  • 4.結尾

0.前言

筆記(二)中我們拿到了GWorld,GName和GUObjectArray的偏移值,接下來就以這些為輸入,來學習一下UEDumper的原始碼中是怎麼使用這些偏移的,以及UEDumper的工作流程。

UEDumper利用GWorld,GName和GUObjectArray,結合UE的反射機制dump出SDK
在瞭解了UE引擎的反射機制後,我猜測,UEDumper的基本工作流程是:

a.透過偏移+基址的方式獲取到目標程序中的GWorld,GName,GUObjectArray
b.遍歷GUObjectArray和GWorld中的所有例項
c.再透過GName查詢到例項中的各種名稱
d.輸出

1.準備

原始碼地址:https://github.com/Spuckwaffel/UEDumper
版本:UEDumper-1.11.1
我這邊工程可直接開啟並編譯,如此步有問題歡迎交流。

如果有對使用UEDumper有疑惑的,可先檢視此影片,或自己試試看。

2.開始閱讀

2.1 設定版本和Offset

  • 開啟工程後定位到UEdefinitions.h,或者從程式碼中的UE_VERSION宏F12過去


在此處設定好被Dump程式的UE版本

  • 再定位到Offset.h
    這裡需要填入GWorld,GName和GUObjectArray的偏移值


setOffsets函式里,程式碼將GWorld,GName和GUObjectArray的偏移值與對應的名稱關聯並存入了容器offsets中

2.2 獲取GName

搜尋函式名setOffsets
定位到如下位置:


在EngineCore類的構造裡,呼叫了setOffsets,將返回的偏移值們存入成員offsets中,再呼叫函式getOffsetForName和getOffsetAddress來獲取GName並存入成員gName。

2.3 使用GName

搜尋gName,定位到函式std::string EngineCore::FNameToString(FName fname);這個函式可以將fname(字串索引)轉化為string(字串)。


功能對應了我猜想中的:

c.再透過GName查詢到例項中的各種名稱

我們繼續看看怎麼查詢GName,下圖是我大概的理解。


註釋上有說道,此函式的演算法是來自UE引擎原始碼的,關於FNamePool,FName等型別的具體的結構留到UE原始碼學習的時候再瞭解。

2.4 獲取GUObjectArray

本來想透過追蹤函式FNameToString的引用來追蹤遍歷GUObjectArray的部分,但是引用的地方比較多且有上層包裝。
但是透過上面的分析,我知道了使用offset的時候需要呼叫getOffsetForName和getOffsetAddress,以此線索搜尋到下圖:


在ObjectsManager類的構造裡看到了熟悉的操作,獲取到GUObjectArray後將其保持在成員gUObjectManager.UObjectArray中。

2.5 使用GUObjectArray

搜尋gUObjectManager.UObjectArray,我看到了一些迴圈遍歷操作,也有透過index來查詢物件指標的操作。
但是ObjectsManager類終歸只是提供功能的模組,我現在更想找到程式執行的主流程程式碼。
觀察ObjectsManager類的定義時,我發現了函式setSDKGenerationDone,用來設定SDK生成完成時的狀態。

初始狀態為“正在獲取SDK”,setSDKGenerationDone會將狀態設定為“正在使用SDK並查詢新的ovject”

2.6 尋找dump主流程

搜尋全部setSDKGenerationDone。

發現除了ObjectsManager中的宣告和定義,只有一處呼叫了。

呼叫發生在DumpProgress::render()函式中,從日誌資訊來看,就是dump主流程了。

繼續閱讀程式碼,看到了之前見過的ObjectsManager和EngineCore


到第5步呼叫setSDKGenerationDone時,獲取SDK就已經完成了,下面再逐步探索前4個步驟。

2.6.1 ObjectsManager::copyGObjectPtrs

函式宣告提到,該函式的功能是將UObjectArray中的所有指標複製到一個大緩衝區中。

程式碼實現中用了一個迴圈,來遍歷UObjectArray陣列中的每一個物件指標。

2.6.2 ObjectsManager::copyUBigObjects

該函式的功能是將UObjectArray陣列中每個UObject物件複製到緩衝區中

同樣也是使用迴圈遍歷,每個UObject獲取到UObjectManager::UBigObject類物件中,儲存在容器gUObjectManager.linkedUObjectPtrs中。

2.6.3 EngineCore::cacheFNames

該函式的功能是遍歷上一步儲存的所有UObject物件,快取其名稱(FName)。

object->getName()內部即為2.3中看過的函式FNameToString

關於快取


2.6.4 EngineCore::generatePackages

該函式的功能是生成所有包。

函式比較大,就先不截圖了,主要功能包括了:

a.遍歷所有UObject物件
b.判斷和統計UObject物件型別
c.生成包檔案

3.疑問

一圈學習下來,居然沒有發現GWorld的用處,不過根據原理來看,GUObjectArray是包含了GWorld,下一步實戰的時候,可以將GWorld填錯試試看。

4.結尾

這次學習只能說大概瞭解了UEDumper的工作原理,其中很多演算法細節,型別結構等都還沒有弄清楚,大部分還是與UE原始碼相關,後面將繼續學習。

相關文章