Unity3D是一款非常出名的遊戲引擎,許多知名遊戲就是基於該引擎進行開發的。它最大的一個特點是一次製作,多平臺部署,而這一核心功能是靠Mono實現的。可以說Mono是Unity3D核心的核心,是Unity3D跨平臺的根本。但是在2015年釋出Unity5的時候,Unity3D官方推出了il2cpp。根據Unity官方給出的解釋,推出il2cpp的原因主要是以下幾點:
1. C#的執行效率還是遠落後於C/C++
2. mono版本授權受限,無法使用.NET的許多新特性
3. mono VM在各平臺的移植和維護都非常耗時
4. 更穩定可靠的垃圾回收機制
那麼,在遊戲程式碼的安全性上,mono和il2cpp兩種模式會有哪些區別呢?使用mono編譯後的遊戲,會將C#指令碼程式碼編譯成IL中間語言後打包到客戶端中。
以android端為例,相關指令碼程式碼會放在apk裡assets\bin\Data\Managed\目錄下的Assembly-CSharp.dll中。使用il2cpp編譯後的遊戲,會將C#指令碼程式碼編譯成native程式碼,最後是在lib目錄下對應架構的libil2cpp.so檔案裡。
可能有些開發者會覺得,程式碼放在so裡面,反彙編分析起來也很累人,那麼遊戲的安全性應該能夠大大增強了吧?
但是,il2cpp編譯後會生成一個global-metadata.dat的檔案,這個檔案包含了大量的符號資訊,有了這些符號資訊,可以大幅降低逆向成本。更有熱心開發者,開發了一個名為Il2CppDumper的工具(https://github.com/Perfare/Il2CppDumper),可以直接將il2cpp.so還原為dll檔案,並且還能夠自動生成il2cpp.so對應的ida.py指令碼,進一步方便攻擊者進行分析和修改。
下面我們演示下該工具的使用過程和最終效果。首先,按照工具的使用說明和提示,執行該工具,下圖是執行結果。
從上圖可知,工具已經成功生成了對應的dll檔案,我們使用dnspy對dll檔案進行解析,得到的結果如下圖所示。
從上圖可以看到,該遊戲所有的函式名都可以完整的解析出來,攻擊者可以根據函式的名稱判斷該函式的功能,然後透過IDA和IL2CppDumper.exe生成的ida.py,對函式進行分析和修改,從而實現破解版的功能,比如下圖,是MainCityResidenceStrategy類的GatherTempMoney函式的內容。
那麼,我們應該如何防範這類工具呢?從工具的使用來看,它需要2個檔案,一個是libil2cpp.so,一個是對應的global-metadata.dat。如果對任意一個檔案做處理,它是不是就無法正常工作了呢?為了驗證這個問題,我們首先只對libil2cpp.so進行加固,再用該工具進行驗證,結果如下圖所示。
從圖裡可知,so加固之後,工具是沒法正常工作的。然後我們只對global-metadata.dat進行保護,這時候工具的輸出結果如下圖所示。
從上圖可知,單獨對dat檔案做保護,工具也是無法正常執行的。
因此,我們可以得出結論,要防範IL2CppDumper.exe這類工具,有2方面的工作可以進行,一是對libil2cpp.so檔案做保護;二是對global-metadata.dat檔案做保護。對於so保護,網易易盾已經有成熟的保護方案,這裡不再詳細介紹,主要講下global-metadata.dat檔案保護方案。
若要對global-metadata.dat進行保護,那麼在引擎載入該檔案的時候,我們必須將檔案恢復到原始狀態,否則遊戲會載入報錯。那麼很自然的可以想到,只要我們在引擎載入該檔案之前,給它解密回去,不就搞定了嗎?該方案確實可行,市面上很多保護方案也是這麼做的,我們可以將該方案稱為第一代global-metadata.dat檔案保護方案。對global-metadata.dat載入過程瞭解的同學都知道,該檔案的載入入口是MetadataLoader::LoadMetadataFile函式,只要我們對該函式進行一定的處理,就完成對global-metadata.dat的保護了。但是,該方案還是有一個明顯的缺點,就是解密之後,會有完整的global-metadata.dat檔案內容暴露在記憶體中,攻擊者還是可以藉助一些工具,將解密後的檔案dump到本地中。
還有一種隱藏global-metadata.dat檔案的方案,它是將該檔案加密後隱藏在apk內部,改變檔案的原始儲存路徑,然後透過hook的方式在載入的時候恢復回去,本質上還是屬於第一代globalmetadata.dat檔案保護方案。
針對第一代保護方案的缺陷,易盾透過對il2cpp的深入探索,研究出了第二代global-metadata.dat保護方案。該方案相對第一代的優勢是,能杜絕檔案在記憶體中全部的暴露,可以做到邊執行邊解密,而非一次性全部解密。使用該方案後,即使攻擊者dump出記憶體中的global-metadata.dat檔案,它也不是完整的解密後的檔案,安全性得到很大的提升。有了該方案,再配合上對libil2cpp.so檔案的加固,遊戲防破解能力能得到明顯的提升。下面用一張圖展示這2種方案的區別。
從mono的dll整體加密、方法加密以及畸形化保護,再到il2cpp的global-metadata.dat整體加密和方法加密,易盾一致致力於對Unity3D引擎保護方案的研究。