:::info
**免責聲明**
本教學文檔中提供的所有資訊僅供學術研究、技術交流和教育目的使用。文中所涉及的逆向工程技術旨在幫助讀者了解 Unity 應用程式的內部工作原理和安全機制。
嚴禁將本文中的技術用於任何商業或非法用途,包括但不限於破解、盜版、製作外掛或侵犯任何個人或實體的合法權益。
讀者應對自己的行為負全部責任。因使用本文技術而導致的任何法律糾紛或損失,作者概不負責。
:::
## 什麼是逆向工程?
學過一點 Unity 之後,大家應該都可以理解製作一個遊戲需要什麼東西。
從最基本的遊戲素材(可能是圖片、影片或是音檔等等),到控制遊戲的腳本(在 Unity 中可能是 C#),以及更多遊戲 animator, 粒子特效的設定都是不可或缺的
但是這些東西在我們打包之後都跑到哪裡了?
通常遊戲素材可能會比較容易找出來,但是那些腳本和設定通常很難直接看出來。
只會多出來一個 `.exe` 執行檔和一堆 `.dll` 之類的檔案,為什麼用這些東西就可以成功執行遊戲?
---
### 步驟
要把遊戲打包出來的 package ,還原出所有資料通常會需要以下幾個步驟

---
#### 步驟 1:初步偵查(查檔案結構與去殼)
* 查看遊戲是什麼格式,例如 `.apk`、`.exe`、`.unity3d` 等。
* 有些檔案實際上是壓縮包,可以解開查看內容。
* 若遊戲有加殼或加密,必須先解除,才能進一步分析。
#### 步驟 2:素材提取
* Unity 遊戲的素材會被打包成特定格式的檔案,例如圖片、音效、動畫、UI 等。
* 可以將這些資源提取出來,方便檢視或分析。
#### 步驟 3:反編譯
* Unity 遊戲的程式碼通常是用 C# 撰寫的。
* 如果是 Mono 模式,可以直接還原為接近原始碼的程式。
* 如果是 IL2CPP 模式,程式碼已經轉成 C++ 編譯,需要更多步驟才能看懂。
#### 步驟 4:反混淆
* 有些遊戲會把程式碼做混淆處理,讓程式名稱看不懂。
* 這會讓分析變得比較困難,需要花時間理解程式邏輯。
* 有時可以從執行流程或錯誤訊息中推測出原本的意圖。
接下來我們會依依介紹這些步驟要怎麼做。
---
## 初步偵查
### 工具
[DIE 工具下載](https://github.com/horsicq/DIE-engine/releases)
**適用平台** : `Windows`, `MacOS`, `Linux`
我們這邊會使用 Detect it Easy (簡稱 DIE),來幫忙查殼,從上面的載點選擇適合自己電腦的版本下載下來
### 選擇要反編譯的遊戲
接著我們選一款遊戲來做示範,這是一個之前蠻有名的手機遊戲 ["**元氣騎士**"](https://zh.wikipedia.org/zh-cn/%E5%85%83%E6%B0%94%E9%AA%91%E5%A3%AB),是用 Unity 開發的一款遊戲。
:::info
通常選擇較新較小的遊戲破解的成功率會比較大
:::

首先,我們先上網查到這個遊戲的隨便一個載點(因為遊戲本身就是免費的,應該很好找)
假設我們下載到了 `soul-knight.apk` 這個檔案 (Andriod 程式檔案)
我們可以先打開 DIE 然後選取這個 apk 檔案進行解析(或是把 apk 拖曳過去 )

點選 `信息` > `保存`,就可以把訊息存成純文字檔案
:::spoiler 範例
```
APK
操作系统: Android(15)[通用, Data, 包]
语言: Kotlin
库: Android Jetpack(1.15.0)
工具: Android SDK(API 35)
存档记录[classes.dex]: DEX
操作系统: Android(8.0)[Dalvik, 32 位, 主模块]
编译器: R8
工具: Android SDK(API 26)
保护器: DexGuard
存档记录[classes2.dex]: DEX
操作系统: Android(8.0)[Dalvik, 32 位, 主模块]
编译器: R8
工具: Android SDK(API 26)
存档记录[lib/arm64-v8a/libandroidx.graphics.path.so]: ELF64
操作系统: Android(5.0)[AARCH64, 64 位, DYN]
链接程序: LDD(14.0.7)
编译器: Android clang(14.0.7)
语言: C/C++
工具: Android NDK(r25c(9519653))
工具: Android SDK(API 21(Android 5.0))
存档记录[lib/arm64-v8a/libdatastore_shared_counter.so]: ELF64
操作系统: Android(5.0)[AARCH64, 64 位, DYN]
链接程序: LDD(14.0.7)
编译器: Android clang(14.0.7)
语言: C/C++
工具: Android NDK(r25c(9519653))
工具: Android SDK(API 21(Android 5.0))
存档记录[lib/armeabi-v7a/libandroidx.graphics.path.so]: ELF32
操作系统: Android(5.0)[ARM, 32 位, DYN]
链接程序: LDD(14.0.7)
编译器: Android clang(14.0.7)
语言: C/C++
工具: Android NDK(r25c(9519653))
工具: Android SDK(API 21(Android 5.0))
存档记录[lib/armeabi-v7a/libdatastore_shared_counter.so]: ELF32
操作系统: Android(4.4-4.4.4)[ARM, 32 位, DYN]
链接程序: LDD(14.0.7)
编译器: Android clang(14.0.7)
语言: C/C++
工具: Android NDK(r25c(9519653))
工具: Android SDK(API 19(Android 4.4-4.4.4))
存档记录[lib/x86/libandroidx.graphics.path.so]: ELF32
操作系统: Android(5.0)[386, 32 位, DYN]
链接程序: LDD(14.0.7)
编译器: Android clang(14.0.7)
语言: C/C++
工具: Android NDK(r25c(9519653))
工具: Android SDK(API 21(Android 5.0))
存档记录[lib/x86/libdatastore_shared_counter.so]: ELF32
操作系统: Android(4.4-4.4.4)[386, 32 位, DYN]
链接程序: LDD(14.0.7)
编译器: Android clang(14.0.7)
语言: C/C++
工具: Android NDK(r25c(9519653))
工具: Android SDK(API 19(Android 4.4-4.4.4))
存档记录[lib/x86_64/libandroidx.graphics.path.so]: ELF64
操作系统: Android(5.0)[AMD64, 64 位, DYN]
链接程序: LDD(14.0.7)
编译器: Android clang(14.0.7)
语言: C/C++
工具: Android NDK(r25c(9519653))
工具: Android SDK(API 21(Android 5.0))
存档记录[lib/x86_64/libdatastore_shared_counter.so]: ELF64
操作系统: Android(5.0)[AMD64, 64 位, DYN]
链接程序: LDD(14.0.7)
编译器: Android clang(14.0.7)
语言: C/C++
工具: Android NDK(r25c(9519653))
工具: Android SDK(API 21(Android 5.0))
存档记录[okhttp3/internal/publicsuffix/publicsuffixes.gz]: 存档
格式: GZIP
```
:::
看起來有點複雜,但我們可以問問看 chatGPT 有什麼重要的訊息。
:::info
#### DexGuard:高強度商業級保護
比 R8/ProGuard 更進一步,DexGuard 提供:
字串加密:無法從靜態分析看到明文 URL、錯誤訊息等
類別/方法加密:核心邏輯可能根本不在 classes.dex
反調試與反篡改:增加分析難度,可能會故意閃退
執行時解密載入程式碼:利用 Java 層的加載器載入記憶體中解密的真實邏輯
結論:這是一個被強殼保護的 App,靜態分析效果有限。
#### Unity 引擎痕跡缺失
未找到 libil2cpp.so 或 libunity.so,推測:
可能被加密藏起來(常見於商業遊戲)
被更名並儲存在 assets/ 等非標準位置
DIE 沒有辨識出(機率較低)
:::
---
### 結論
- 保護強度高:使用 DexGuard,靜態反編譯將極度困難
- Unity 元件被隱藏:必須進行更深層的資源搜尋與記憶體分析
- 傳統工具無效:Il2CppDumper 尚無用武之地
---
### 下一步
這邊就可以發現,想要逆向一款熱門遊戲並不是這麼簡單XD,這款遊戲被許多工具給保護著
並且有時候一個遊戲的資源不會隨著 apk 一起被下載下來,要等到第一次打開時才會透過網路下載
當然我們可以選擇繼續去對他執行後面的步驟,但是大概率會遇到很多困難
所以我們先換個思路,不如我們去找看看 **舊版本** 的遊戲檔案,也許他那時候還沒有進行加密
---
## 初步偵查 (Second Try)
在網路上搜索一番之後,我找到了稍微舊版一點的元氣騎士檔案 `Soul_knight_6.7.0.apk`。
一樣先對他進行分析

### 關鍵分析
#### Unity 與 IL2CPP 明確標示
IL2CPP / Unity 引擎:DIE 成功偵測到 Unity 引擎與 IL2CPP 技術棧,省去了推測流程。
libil2cpp.so 存在:直接位於 lib/arm64-v8a/ 中,確認 Unity 遊戲的核心 C# 邏輯被轉為原生程式庫,可進行靜態分析。
#### Java/Kotlin 層經過 ProGuard 混淆
使用了 ProGuard(或 R8),對 Java/Kotlin 程式碼進行混淆。
影響較小,因為我們的重點在原生的 Unity 程式碼 (libil2cpp.so)。
#### 未使用強加密殼
未偵測到 DexGuard、梆梆、愛加密等商業級加殼工具。
表示程式碼沒有經過高強度保護,可以直接開始逆向。
---
重點就是他沒有被強殼保護著,所以我們可以破解他的機率大幅提升
所以我們就繼續使用這個檔案來進行後續的教學
## 素材提取
首先有幾個觀念需要釐清,混淆通常是只針對 **程式碼**,讓人就算可以反編譯也看不懂他在做什麼
而對遊戲素材的保護方式通常是使用**壓縮**或是**加密**,或者是通常沒有特別去保護
所以我們開始嘗試直接使用 Asset Ripper 來進行素材的提取
### 工具
[Asset Ripper 工具下載](https://assetripper.github.io/AssetRipper/articles/Downloads.html)
**適用平台** : `Windows`, `MacOS`, `Linux`
這邊的工具就比較多樣了,比如說 AssetStudio 等等,但是我會優先選擇跨平台支援性比較好的來做介紹
### 解壓縮 apk
其實大部分的應用程式,他們的本體都是一個資料夾,而 apk 就是一個壓縮過後的資料夾,而大部分的解壓縮工具都可以直接打開他
Windows 端可以直接使用一些工具像 `7-zip`, `bandizip`
Mac / Linux 端可以去查看看怎麼使用 `unzip` 這個指令
解壓縮完成之後,我們會得到一個像這樣的資料夾

其中最重要的就是 `asset` 和 `lib` 這兩個了,分別會用來提取素材和程式碼
### 使用工具
接下來輪到使用工具的時間了,我們要做的事情很簡單,就是把上面提供的 Asset Ripper 下載下來
Windows 版本的話可以直接點擊 `.exe` 打開使用
Mac / Linux 的話可以參考這篇[教學](https://assetripper.github.io/AssetRipper/articles/RunningOnMac.html)
打開之後我們會看到這樣的介面

點擊左上角 `File` > `Open Folder`
然後選取我們剛剛解壓縮完的 apk 資料夾中的 `asset` 資料夾,把它打開
等待一段時間讓他匯入之後

中間按鈕會變成綠色,點進去就可以看到有哪些資料被找到了

這邊就會列出他找到的所有資源,如果列出來的資源很少,很可能就是在提取的時候遇到了問題,可以從終端機介面中查看有沒有出現 ERROR
可以從這個頁面先進行預覽,然後 export 你想要的出來
點擊 `Export` > `Export All Files`
然後 AssetRipper 就會把素材通通匯出到 AssetRipper 執行檔所在的資料中了
匯出的資料夾中會有兩個資料夾,Assets 和 Assemblies,素材會放在 Assets 中,大家就可以開始盡情地探索自己想要用看看的素材了

上面這個就是在 `Assets/skin/character/assassin/skin_0/assassin_0.png` 中找到的 sprite
## 你有一張 Sprite,卻不會讓角色動起來?
### Step 1:把 Sprite 丟進 Unity!
先把你準備好的 Spritesheet(建議是 `.png`)拖進 Unity 的 `Assets` 資料夾中。
這張圖應該長這樣(每一格是角色的一個動作):

### Step 2:切割sprite
1. 點選剛剛拖進來的圖片
3. 到右邊的 Inspector,把:
- `Sprite Mode` 改成 `Multiple`
- `Pixels Per Unit` 保持預設即可


4. 點「Open Sprite Editor」按鈕(第一次使用可能會跳出 Appky 視窗 → 點 Apply)
### 在 Sprite Editor 裡:
- 點上方的「**Slice**」按鈕
- 把 `Type` 改成 **Automatic**
- `Pivot` 可選擇 Center 或 Bottom(看你之後動畫要怎麼定位)
- 按下 `Slice` → 再按右上角 `Apply`
> Auto Slice 會自動根據圖像間的空隙偵測邊界,把每個角色動作自動切開,不需要你手動輸入尺寸~適合用在每格之間**有空隙**的 Spritesheet。

如果你的圖片長這樣,就是切好拉~

### Step 3:建立動畫檔,讓角色動起來!
1. 在 `Project` 面板中,展開剛剛的圖片(點下小箭頭)
2. 選取你要做成動畫的多張格子(按住 Shift 選取,例如走路的 6 張)

4. **右鍵點擊其中一張 → 選擇 `Create > Animation`**

6. Unity 會跳出儲存視窗,幫你建立 `.anim` 檔案
7. 點一下 `Create`,就完成囉!
---
### Step 4:讓動畫有地方住!建立角色物件
動畫做好了,但如果沒有角色 GameObject,動畫也沒地方可以「演
所以我們來自己手動生一個角色!
### 建立角色物件:
1. 在 `Hierarchy` 空白處右鍵 → `Create Empty`,幫它命名,例如 `Player`

2. 選取 `Player` → 在右邊的 Inspector 按下 `Add Component`
- 加一個 `Sprite Renderer`(這樣才看得到角色)
- 一開始你可以先隨便給它一張靜態的 Sprite


3. **直接把step 3 創建的`.anim` 檔案拖曳到角色 GameObject 上**

Unity 會幫你:
- 自動新增 `Animator` 元件
- 自動建立並綁定一個 Animator Controller
- 自動設好動畫播放
#### 真的就是拖一下,全部搞定!
### Step 5:按下 Play!角色活起來啦!
點上方的 ▶️ 播放鍵,你會看到角色開始動作

若沒看到動畫,請檢查:
- `.anim` 有成功套用到 GameObject 上
- `Sprite Renderer` 有正常顯示(不是透明或沒圖片)
---
:::warning
我發現我不會破解他的 `global-metadata.dat`
等我成功之後再來把教學補完
:::
~~## 反編譯
有了素材雖然可以自己重新設定讓他動起來,但如果可以直接使用原本遊戲寫好的當然會更好
而反編譯顧名思義就是把原本運行遊戲的 Machine Code,想辦法還原成人能看懂的程式碼
當然,還原出來的程式碼不會和原本的一樣,像是註解不能被反編譯出來~~
~~其實剛剛的 Asset Ripper 這個工具就可以幫忙做到反編譯
我們只需要在 Open Folder 時選擇整個 apk 資料夾即可
但是我們會遇到這個錯誤~~

~~簡單來說就是 `global-metadata.dat` 這個檔案被加密了,他是和 `libil2cpp.so` 一樣重要的檔案,如果被加密就沒辦法直接的進行反編譯~~
:::spoiler 額外資訊
簡單補充一下 Unity 後端,現在手機遊戲通常都是 IL2CPP
:::info
**Unity 編譯後端比較(Mono vs IL2CPP)**
| 項目 | **Mono** | **IL2CPP** |
| --------------------- | --------------------- | -------------------------------------- |
| **編譯後端** | Mono(保留 .NET 結構) | IL2CPP(轉成 C++ 再編譯成原生程式) |
| **遊戲內檔案** | `Assembly-CSharp.dll` | `libil2cpp.so` + `global-metadata.dat` |
| **Asset Ripper 提取內容** | 原始 DLL 檔案(可直接反編譯) | 重建後的 C# 程式碼檔案(.cs) |
| **混淆處理方式** | DLL 通常保留混淆,需手動閱讀與命名 | 還原出來的 .cs 仍保持混淆(如 a.a()、b.b()) |
:::
## 其他資源
### 查殼
[Detect it Easy](https://github.com/horsicq/Detect-It-Easy)
### 反編譯
[dnspyEx](https://github.com/dnSpyEx/dnSpy/releases/tag/v6.5.1)
### 素材提取
[AssetRipper](https://assetripper.github.io/AssetRipper/)
[AssetStudio 活躍分支](https://github.com/zhangjiequan/AssetStudio)
[UABE](https://github.com/SeriousCache/UABE)
### 反混淆
[NETReactorSlayer](https://github.com/SychicBoy/NETReactorSlayer)
影片推薦:https://www.bilibili.com/video/BV1go4y1k7gr?spm_id_from=333.788.videopod.sections&vd_source=684e470d1f116a6ed25bbb1df06dce26