Unity 3d指令碼加密方案
1.1 加密方案
Unity 3D專案遊戲邏輯採用C#指令碼,我們知道C#編譯生成的DLL或EXE是IL程式集。IL程式集中有一個MetaData,記錄了程式集中的一切資訊,所以容易被反編譯。傳統的防破解方式是是對IL程式集進行混淆或者加殼。但是這種混淆基本上只是做一些名稱混淆或流程混淆或者加一些打花指令。這種混淆或加殼的結果基本上還是保留了IL程式集的原貌,還是很容易被破解的。
因為有這些缺點,我們實現了一套自己的加密方案:直接對IL程式集進行加密。改變程式集形態,這樣子,它就不再是IL程式集格式了。
Unity 3D專案的遊戲邏輯指令碼編譯後會生成Assembly-CSharp.dll。只要對它進行加密,但是這帶來一個問題是,Unity不認加密後的DLL,因為它不再是dll格式了。那我們就需要修改Unity底層程式碼,好在Unity是基於Mono的,而Mono是開源的。只要找到Mono載入dll的程式碼,對它進行逆向即可。
Mono載入dll的程式碼位於/mono/metadata/image.c檔案中的mono_image_open_from_data_with_name,程式碼如下:
MonoImage *
mono_image_open_from_data_with_name (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, const char *name)
{
MonoCLIImageInfo *iinfo;
MonoImage *image;
char *datac;
if (!data || !data_len) {
if (status)
*status = MONO_IMAGE_IMAGE_INVALID;
return NULL;
}
//紅色部分是我們新增的,對加密過的dll進行解密
if (strstr(name, “Assembly-CSharp.dll”) != NULL)
{
//這裡是解密過程,我們採用的是xxtea加解密演算法。
}
…
return register_image (image);
}
1.2 Unity mono編譯
有了加密方案,我們要解決的是mono編譯問題。
1.2.1 原始碼下載
https://github.com/Unity-Technologies/mono/tree/unity-4.5
1.2.2 編譯Windows平臺mono.dll
1、開啟Visual Studio Command Prompt(2010)
2、進入mono-unity-4.5\msvc目錄
3、執行msbuild.exe mono.sln /p:Configuration=Release_eglib
注意:直接開啟mono.sln解決方案,在Visual Studio底下是編譯不了的。
1.2.3 編譯android平臺libmono.so
編譯android下的libmono.so需要下載NDK工具鏈交叉編譯。
準備NDK r9以上版本。32位或者64位均可。
Linux 32位環境下編譯採用32位NDK版本。
Linux 64位環境下編譯採用64位NDK版本。
Cygwin32位環境下編譯採用32位NDK版本。
Cygwin64位環境下編譯採用64位NDK版本。
建議使用32位linux與32位NDK來編譯
1、安裝gcc、make、automake等編譯工具
$ sudo apt-get install build-essential
Cygwin環境下安裝devel裡面的包,基本編譯工具就都有了。
2、下載NDK,解壓
/home/jjl/android-ndk-r9
設定環境變數export ANDROID_NDK_ROOT=/home/jjl/android-ndk-r9
3、下載mono原始碼,解壓
/home/jjl/mono-unity-4.5
4、安裝依賴包
bison
gettext
libffi-dev
zlib(注意ubuntu底下包名為zlib1g-dev)
libtool
安裝以上包用 sudo apt-get install xxxx
5、進入mono-unity-4.5目錄
編輯external/buildscripts/build_runtime_android.sh檔案,這個檔案是編譯指令碼
找到第14行,填上對應的ndk版本號。
perl ${BUILDSCRIPTSDIR}/PrepareAndroidSDK.pl -ndk=r9 -env=envsetup.sh && source envsetup.sh
修改envsetup.sh路徑
perl ${BUILDSCRIPTSDIR}/PrepareAndroidSDK.pl -ndk=r9 -env=envsetup.sh && source /root/mono-unity-4.6/envsetup.sh
註釋掉145、146行
#clean_build “$CCFLAGS_ARMv5_CPU” “$LDFLAGS_ARMv5″ “$OUTDIR/armv5″
#clean_build “$CCFLAGS_ARMv6_VFP” “$LDFLAGS_ARMv5″ “$OUTDIR/armv6_vfp”
在mono-unity-4.5目錄下執行
./external/buildscripts/build_runtime_android.sh
注意,一定要切換到mono-unity-4.5目錄底下執行,而不要進入external/buildscripts/目錄執行
執行過程中,會出現以下錯誤:
configure: error: C compiler cannot create executables
編輯external/android_krait_signal_handler/build.pl這個檔案
將#!/usr/bin/env perl –w改為#!/usr/bin/perl –w
其它的錯誤根源都因為這一行程式碼。
再次進入mono-unity-4.5目錄,執行
./external/buildscripts/build_runtime_android.sh
注意,一定要切換到mono-unity-4.5目錄底下執行,而不要進入external/buildscripts/目錄執行
直到出現以下字樣,說明我們已經編譯成功了。
Build SUCCESS!
Build failed? Android STATIC/SHARED library cannot be found… Found 4 libs under builds/embedruntimes/android
注意:出Build failed?字樣,並不是我們編譯失敗了,而是我們註釋掉了build_runtime_android.sh 第145、146行,有些庫就沒有編譯。只編譯我們要的庫,加快編譯速度。
編譯出來的庫位於builds/embedruntimes/android/armv7a資料夾中。
最後要編譯release版,請將build_runtime_android.sh 第66行的-g選項去掉。
這裡有個很噁心的事情,unity的mono版本並不是按小版本分的,比如我想找unity4.6.1 對應的mono那麼它就沒有,unity只提供4.3x 或者 4.6x 或者5.1x 這種大版本的mono .從提交時間上來看更新的很隨意啊。我感覺要想找到對應的unity版本,可以根據unity這個版本釋出的時候,然後在github上找對應時間的mono版本。。
如下圖所示,開啟網頁後,找到對應的branches版本, 這裡選擇unity-4.6 或者 unity-5.1 這兩個版本我已經測試通過。別的版本希望大家都能來參與測試
sh external/buildscripts/build_runtime_android.sh