2018年10月23日 星期二

Android Unity 破解與反編譯

在Android平台上,目前大部分的遊戲為了加速開發時間,都會使用Unity這款遊戲引擎,因此要分析遊戲的邏輯,就需要將Unity的C#程式碼整個抓下來,不幸的是,越來越多的遊戲會因為防止被任意修改或破解,會加密Unity的主程式,這樣會讓破解遊戲更佳困難,因此本篇的主軸在於遊戲防護的方法與解法。

一、Unity主程式


Unity主程式會放在assets\bin\Data\Managed的路徑下 (直接使用zip將apk解壓縮),進入這個資料夾後,我們可以找到Assembly-CSharp.dll,此檔就是Unity的主程式。



如果將Assembly-CSharp.dll直接反編譯,會發現反編譯的工具會出現檔案識別錯誤(執行檔或DLL開頭2 Bytes 是 0x4D 0x5A),透過16禁制編輯器打開,觀察檔案表頭,就會知道這個DLL被加密了,而且不僅只有表頭(有些反反編譯只會破壞表頭),整個程式都被加密過了,因此需要分析它的解密程式,或者直接從記憶體將解密完的DLL抓下來。



二、反反編譯 (Anti-Debugger)


在遊戲執行前,會透過lib\armeabi-v7a\libmono.so (apk完整路徑)將Assembly-CSharp.dll動態載入到記憶體,因此反反編譯就要在它載入完成前,解密DLL (不解密DLL會崩潰)。



不過我們還需要知道它是用libmono.so的哪個函式加載的,因此需要查找相關開啟檔案的函式。

從函式名稱和參數發現mono_image_open_from_data_with_name,是個不錯的分析的進入點。



原本想要攔截(Hooking) mono_image_open_from_data_with_name函式,並將DLL直接抓下來,不過怎麼執行遊戲都會崩潰,因此將它的記憶體印出來,發現它原來已經被Hooking了。

因此我們需要透過IDA將原來的程式碼交叉比對,發現在函式前12 Bytes被修改了,然而程式會先跳到0x14a63629,然後再返回此函式,我們可以大膽假設0x14a63629,就是解密Assembly-CSharp.dll。

a. 00 f0 9f e5 – arm語法為ldr pc,[pc]

b. 29 36 a6 14 – 是記憶體中的0x14a63629位址

c. 29 36 a6 14 – 跟b一樣 (多覆蓋了4 Bytes,防止其他人覆蓋前8 Bytes)

d. 使用IDA,將正確的語法顯示出來



透過cat /proc/pid/maps查找0x14a63629的解密程式放在哪裡,發現解密完成後,它會刪除解密的程式檔案(這個解密程式也是在運行時才會解密= =),不過並不會影響我們抓取Assembly-CSharp.dll。



三、抓取解密Assembly-CSharp.dll


從上一節的分析經驗,我們不能Hooking前面,那反過來我們可以在mono_image_open_from_data_with_name結束函式前,將它加載的結果(Assembly-CSharp.dll)抓下來,就能達到我們要的目的。

在抓取Assembly-CSharp.dll前,我們要知道它的參數相關資訊,像是mono_image_open_from_data_with_name的傳入第六個參數name,我們可以用來判斷是不是Assembly-CSharp.dll,然後再抓取回傳結果,它的回傳結果是一個結構,結構資訊如下:

1. raw_data – 載入記憶體的內容

2. raw_buffer_len – 載入內容大小



我們耗費不少時間在要如何抓取解密完的Assembly-CSharp.dll,終於來到最後關頭了,我們可以從mono_image_open_from_data_with_name函式返回結果前,Hooking R3 (_MonoImage),就可以得到我們要的結果啦!



最後終於成功反編譯Assembly-CSharp.dll,可以好好的玩弄這隻遊戲程式了,並且分析遊戲的相關資訊和任意修改程式的邏輯。