Unity3D-重新編譯Mono加密DLL

Kaitiren發表於2015-09-23

Unity3D-重新編譯Mono加密DLL。安卓應用總是讓人頭疼,遊戲遭到破解與反編譯是研發的人最不願意看到的。自己的辛苦勞動成果被人隨意竊取與利用,對這些咬牙切齒的痛恨。所以我們需要加強自身的反破解技術力量。不過這世上沒有破解不了的東西,道高一尺魔高一丈,我們做的只是讓破解更加困難而已。讓那些破解的人付出點代價才能得到他們想要的,如果他們覺得代價太高,看不清前面的道路,他們就有可能放棄,然後我們的目的達到了。

遊戲本身加密方式有很多,對apk加殼,防止apk二次打包等。對這些android的加密與破解技術看過比較好的文章參考:《Android安全及病毒分析》 ,其中《Android APK加殼技術方案【2】》最為經典。而本篇文章我們主要來說說針對Unity3D的加密。

閒扯就到這裡,我們開始說正事:

Unity3D所有客戶端的程式碼都會以dll檔案形式存下來,當遊戲應用被開啟時c#vm(也就是mono的虛擬機器)會去載入所有dll,從而開始執行真正的程式畫面了。而破解的很大一部分都是通過解壓apk後拿到主邏輯dll,對dll進行反編譯,然後修改後重新編譯,再放入apk重新簽名打包。所以我們需要針對dll進行加密,以防止他們反編譯dll。

加密一個dll檔案非常容易,無論你用什麼演算法都行,但是在哪解密呢?答案是libmono.so。libmono.so是mono的核心程式,它承載了載入解析dll和虛擬機器執行的功能。所以說libmono.so是關鍵,我們需要修改mono核心程式並重新編譯它。

下面將開始mono的編譯過程,別看步驟寫得簡單明瞭,其實我花了起碼一個多星期的思考,嘗試,失敗,再思考,再嘗試,再失敗…..總結其中原因一方面也是自己的愚鈍的資質,另一方面是unity mono和mono並不一樣,unity mono缺少編譯文件並且還混合著原mono的編譯文件,導致誤判了很多:

1.首先不要認為unity mono 與 原生態mono一樣。可以編譯mono就可以同樣步驟編譯unity mono。我在這裡嘗試了很久,使用configure進行編譯,嘗試使用不同的編譯引數,進行編譯,最後發現unity mono使用的是ndk-9下的linux-4.8編譯器,所有引數都是根據這個編譯器所設定的。

2.unity mono 地址:https://github.com/Unity-Technologies/mono 你需要從這裡下載unity mono。

3.mono需要autoconf automake libtool pkg-config這些工具。你最好還是去下載安裝了。你可以用brew安裝。brew install autoconf automake libtool pkg-config。

4.我一開始使用mac x86_64進行編譯,折騰了很久然後建了個linux-x86_64虛擬機器來編譯,然後又折騰了很久,又建了個linux-i386來重新編譯mono,因為我一直認為交叉編譯需要加些不同的編譯引數和變數。在linux-i386首次編譯成功後又開始轉化到mac上,進行交叉編譯也一樣成功,最後發現其實是我沒找對路子。這路子就是unity 的mono-build-tool:https://github.com/Unity-Technologies/monobuildtools 它已經在unity mono的專案裡了,在mono的external/buildscripts下。

5.buildscripts下的build_runtime_android.sh是編譯安卓平臺的關鍵。它是unity製作的一個自動編譯 mono 流程的指令碼。你需要將這個指令碼copy到mono根目錄下再執行。

6.指令碼里寫些內容,如果你懶得看,我幫你稍微解釋下。它會去檢查你當前的ANDROID_NDK_ROOT環境變數是否是指向ndk-9,所以你需要去下ndk-9版本,放到機子上,然後編輯環境變數ANDROID_NDK_ROOT指向它,如果你沒有它會通過perl模組lwp-download去下載ndk-9,但是你必須要要有這個perl模組才行,我勸你還是老老實實自己去下吧。ndk版本下載地址參考這裡:《android-sdk-ndk-studio-下載列表和構建說明》。如果是linux下編譯環境變數設定參考這裡:《linux環境變數簡介》 。然後呢,它會用git去clone一個編譯時用到的庫,這個也是unity自己改編過的一個庫,地址為:https://github.com/Unity-Technologies/krait-signal-handler ,這個庫有個坑說下:perl指令碼build.pl頭部有個命令是#!/usr/bin/env perl -w,這個在部分機子上並不相容,如果你有錯誤停在這裡這個檔案上,你可以將env去除再嘗試手動perl build.pl 執行構建一遍沒問題再重新編譯,原因參考:http://abloz.com/2011/01/13/why-use-usr-bin-env.html 。最後就先make clean && make distclean 清除前面編譯的內容,然後進行預編譯configure,引數都在指令碼里設定好了,你不需要關心了。預編譯後就開始make編譯了。

7.執行build_runtime_android.sh後terminal基本都是刷屏的節奏。刷刷刷的編譯輸出,你根本來不及看清到底做到哪了做了些什麼內容。而config.log這個檔案記錄所有的編譯輸出,包括哪行錯誤了,哪行通過了。除錯基本也考這個log檔案,如果關鍵部位錯誤它會停止,然後你就可以針對性的查了。這裡提醒一點,編譯時它很多地方都是在檢測編譯器是否正常,因為它要確認編譯器對錯誤的編譯內容是否能夠檢測到,所以很多錯誤內容只是測試內容-你需要省略掉。

8.如果編譯成功,那就恭喜你了。windows下我沒有測試過,有可能會增加不少坑,我建議還是用linux或者mac編譯吧,因為我搜集資料的時候不少人對windows下編譯mono都抱怨不少。那麼我們開始邁入下一個坑吧:)

下面介紹加解密DLL部分:

加密演算法自己選我不多說了,但我這裡要引用一篇同樣介紹mono的dll加密的文章,我覺得也寫得滿不錯的,但是文章描述不夠詳盡。我這篇文章彌補了他的不足,將細節補充得更加細緻。你大可以兩篇文章加起來參考。http://www.unitymanual.com/home.php?mod=space&uid=7672&do=blog&id=1440 不知道地址是不是原作者的,如果不是我再更換吧。

1.首先找到dll解密入口。mono下/mono/metadata/image.c裡mono_image_open_from_data_with_name是關鍵方法,引數中的data是dll傳入的資料。你要做的就是將它解密後傳給datac,這個方法程式你必須看下,因為你要了解下解密程式放在哪才合適。

2.大部分dll都會通過mono_image_open_from_data_with_name這個方法進行載入,但不是所有dll,例如mscorlib.dll和System.Core.dll就不會,可能還有其他dll,我並不確定還有哪些。所以你還是得辨別下哪些dll會通過這個方法,這樣你才能確定哪個dll可以加密。如何判斷data屬於哪個dll呢,引數name就是data的路徑名,name列印出來後就像:/data/app/com.xx.xx.apk/assets/bin/Data/Managed/xxx.dll 這樣。

3.列印除錯。你可以使用g_message例如:g_warning(“dll name: %s \n”, name); 其他的列印除錯你可以檢視原始碼中的它寫的程式碼。很容易找到,查關鍵字LOG吧。

4.改完後重新編譯mono,找到libmono.so(find . -name libmono.so),完成編譯後libmono.so的平臺有好幾個,你可以根據自己的平臺來選。有人拷貝這些mono重新編譯過的檔案去覆蓋了unity編輯器的原來mono檔案,這樣也可行。但我選擇在打包android時再從外部複製libmono.so,這樣就可以繞過編譯器重新編譯後無法讀取無加密dll的麻煩,可以少做一層無意義的編輯器狀態下的加解密工作。

5.mono解密部分就到這裡了。其他部分的關鍵就是你的加解密程式了,是否能夠加密和解密都是ok的並且都是不改變size。你需要的引數有data和data_len,mono_image_open_from_data_with_name方法裡面都有。

6.為了安全起見我使用c來編寫加密程式,因為我認為c#和c的編譯器對於變數記憶體的儲存機制不一樣,怕引起不必要的麻煩。

這裡要非常感謝一個人,全程都在提供幫助:熾樂@宗樹 

轉載請註明出處:http://www.luzexi.com

相關文章