Unity3D-重新編譯Mono加密DLL。安卓應用總是讓人頭疼,游戲遭到破解與反編譯是研發(fā)的人最不愿意看到的。自己的辛苦勞動成果被人隨意竊取與利用,對這些咬牙切齒的痛恨。所以我們需要加強自身的反破解技術力量。不過這世上沒有破解不了的東西,道高一尺魔高一丈,我們做的只是讓破解更加困難而已。讓那些破解的人付出點代價才能得到他們想要的,如果他們覺得代價太高,看不清前面的道路,他們就有可能放棄,然后我們的目的達到了。
游戲本身加密方式有很多,對apk加殼,防止apk二次打包等。對這些android的加密與破解技術看過比較好的文章參考:
《Android安全及病毒分析》 ,其中《Android APK加殼技術方案【2】》 最為經(jīng)典。而本篇文章我們主要來說說針對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 與 原生態(tài)mono一樣??梢跃幾gmono就可以同樣步驟編譯unity mono。我在這里嘗試了很久,使用configure進行編譯,嘗試使用不同的編譯參數(shù),進行編譯,最后發(fā)現(xiàn)unity mono使用的是ndk-9下的linux-4.8編譯器,所有參數(shù)都是根據(jù)這個編譯器所設定的。
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 x8664進行編譯,折騰了很久然后建了個linux-x8664虛擬機來編譯,然后又折騰了很久,又建了個linux-i386來重新編譯mono,因為我一直認為交叉編譯需要加些不同的編譯參數(shù)和變量。在linux-i386首次編譯成功后又開始轉化到mac上,進行交叉編譯也一樣成功,最后發(fā)現(xiàn)其實是我沒找對路子。這路子就是unity 的mono-build-tool:https://github.com/Unity-Technologies/monobuildtools 它已經(jīng)在unity mono的項目里了,在mono的external/buildscripts下。
5.buildscripts下的buildruntimeandroid.sh是編譯安卓平臺的關鍵。它是unity制作的一個自動編譯 mono 流程的腳本。你需要將這個腳本copy到mono根目錄下再執(zhí)行。
6.腳本里寫些內容,如果你懶得看,我?guī)湍闵晕⒔忉屜隆K鼤z查你當前的ANDROIDNDKROOT環(huán)境變量是否是指向ndk-9,所以你需要去下ndk-9版本,放到機子上,然后編輯環(huán)境變量ANDROIDNDKROOT指向它,如果你沒有它會通過perl模塊lwp-download去下載ndk-9,但是你必須要要有這個perl模塊才行,我勸你還是老老實實自己去下吧。ndk版本下載地址參考這里:《android-sdk-ndk-studio-下載列表和構建說明》。如果是linux下編譯環(huán)境變量設定參考這里:《linux環(huán)境變量簡介》。然后呢,它會用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,參數(shù)都在腳本里設置好了,你不需要關心了。預編譯后就開始make編譯了。
7.執(zhí)行buildruntimeandroid.sh后terminal基本都是刷屏的節(jié)奏。刷刷刷的編譯輸出,你根本來不及看清到底做到哪了做了些什么內容。而config.log這個文件記錄所有的編譯輸出,包括哪行錯誤了,哪行通過了。調試基本也考這個log文件,如果關鍵部位錯誤它會停止,然后你就可以針對性的查了。這里提醒一點,編譯時它很多地方都是在檢測編譯器是否正常,因為它要確認編譯器對錯誤的編譯內容是否能夠檢測到,所以很多錯誤內容只是測試內容-你需要省略掉。
8.如果編譯成功,那就恭喜你了。windows下我沒有測試過,有可能會增加不少坑,我建議還是用linux或者mac編譯吧,因為我搜集資料的時候不少人對windows下編譯mono都抱怨不少。那么我們開始邁入下一個坑吧:)
下面介紹加解密DLL部分:
加密算法自己選我不多說了,但我這里要引用一篇同樣介紹mono的dll加密的文章,我覺得也寫得滿不錯的,但是文章描述不夠詳盡。我這篇文章彌補了他的不足,將細節(jié)補充得更加細致。你大可以兩篇文章加起來參考。http://www.unitymanual.com/home.php?mod=space&uid=7672&do=blog&id=1440 不知道地址是不是原作者的,如果不是我再更換吧。
1.首先找到dll解密入口。mono下/mono/metadata/image.c里monoimageopenfromdatawithname是關鍵方法,參數(shù)中的data是dll傳入的數(shù)據(jù)。你要做的就是將它解密后傳給datac,這個方法程序你必須看下,因為你要了解下解密程序放在哪才合適。
2.大部分dll都會通過monoimageopenfromdatawithname這個方法進行加載,但不是所有dll,例如mscorlib.dll和System.Core.dll就不會,可能還有其他dll,我并不確定還有哪些。所以你還是得辨別下哪些dll會通過這個方法,這樣你才能確定哪個dll可以加密。如何判斷data屬于哪個dll呢,參數(shù)name就是data的路徑名,name打印出來后就像:/data/app/com.xx.xx.apk/assets/bin/Data/Managed/xxx.dll 這樣。
3.打印調試。你可以使用gmessage例如:gwarning("dll name: %s \n", name); 其他的打印調試你可以查看源碼中的它寫的代碼。很容易找到,查關鍵字LOG吧。
4.改完后重新編譯mono,找到libmono.so(find . -name libmono.so),完成編譯后libmono.so的平臺有好幾個,你可以根據(jù)自己的平臺來選。有人拷貝這些mono重新編譯過的文件去覆蓋了unity編輯器的原來mono文件,這樣也可行。但我選擇在打包android時再從外部復制libmono.so,這樣就可以繞過編譯器重新編譯后無法讀取無加密dll的麻煩,可以少做一層無意義的編輯器狀態(tài)下的加解密工作。
5.mono解密部分就到這里了。其他部分的關鍵就是你的加解密程序了,是否能夠加密和解密都是ok的并且都是不改變size。你需要的參數(shù)有data和datalen,monoimageopenfromdatawith_name方法里面都有。
6.為了安全起見我使用c來編寫加密程序,因為我認為c#和c的編譯器對于變量內存的存儲機制不一樣,怕引起不必要的麻煩。 這里要非常感謝一個人,全程都在提供幫助:熾樂@宗樹
轉載請注明出處:http://www.luzexi.com