Unity3D 遊戲加密解密那些事
0x01 加密
加密主要是為了防止美術資源被竊取(換皮)和程式碼被惡意修改(外掛輔助,廣告,二次打包)
1.資源的加密
先說說Unity載入資源的方式,第一種Resources.Load,第二種AssetBundle。
第一種是Unity預設的資源格式,如果對這個資源進行加密,Unity將會無法識別
第二種是先通過BuildPipeline.BuildAssetBundle把資源打成AssetBundle,在對AssetBundle進行加密。載入資源時,用WWW把加密後AssetBundle下載下來,進行解密,解密後在通過AssetBundle.CreateFromMemory動態創建出AssetBundle進行解密。
2.程式碼的加密
Unity是一個基於Mono框架的跨平臺遊戲開發引擎,Unity所使用的Mono屬於Mono開源專案的分支
在Unity中,我們寫的程式碼會編譯到Assembly-CSharp.dll和Assembly-CSharp-firstpass.dll(這個檔案不一定有,Plugins或Standard Assets中的程式碼會編譯到這個檔案裡)
iOS平臺:目前新版Unity用L2CPP方式編譯的遊戲已經看不到這2個檔案了,程式碼被編譯到ios navite程式碼中了,所以在iOS平臺下就不考慮程式碼加密了。
Android平臺:依舊能在\assets\bin\Data\Managed\ 目錄下找個這2個檔案
第一種是對程式碼進行混淆,雖然有幾個混淆軟體CodeGuard、CryptoObfuscator、de4dot...可以用用,但是有很多限制,保護程式碼的作用不是太大
第二種是對Assembly-CSharp.dll和Assembly-CSharp-firstpass.dll進行加密(有一定難度,需要自己從新編譯修改Mono庫)
找到/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; } datac = data; if (need_copy) { datac = g_try_malloc (data_len); if (!datac) { if (status) *status = MONO_IMAGE_ERROR_ERRNO; return NULL; } memcpy (datac, data, data_len); } image = g_new0 (MonoImage, 1); image->raw_data = datac; image->raw_data_len = data_len; image->raw_data_allocated = need_copy; image->name = (name == NULL) ? g_strdup_printf ("data-%p", datac) : g_strdup(name); iinfo = g_new0 (MonoCLIImageInfo, 1); image->image_info = iinfo; image->ref_only = refonly; image->ref_count = 1; image = do_mono_image_load (image, status, TRUE, TRUE); if (image == NULL) return NULL; return register_image (image); }
第一個引數char *data這個指標指向執行時Assembly-CSharp.dll和Assembly-CSharp-firstpass.dll的記憶體地址, 可在該方法內新增或呼叫對data解密的演算法
0x02 解密
1.AssetBundle加密了那麼加密演算法就在Assembly-CSharp.dll或Assembly-CSharp-firstpass.dll中,用ILSpy或Reflector C#反編譯工具就能看到原始碼,找到WWW下載AssetBundle的地方就能找到加密演算法。
2.dll加密用IDA類似的反彙編工具載入libmono.so這個檔案,搜尋到mono_image_open_from_data_with_name這個方法,簡單的分析一下就可以找到加密演算法。也可以用IDA動態除錯,忽略加密演算法,dump出解密後的dll檔案(動態dump比靜態分析要容易簡便,但靜態分析可以看看別人用的加密演算法,同時也能提高自己的分析能力)
3.現在客戶端主流的加密也就這樣了,但是Android平臺下用IDA動態反編譯除錯一下就破解了。。針對Android平臺繼續提高破解門坎的話就是隱藏Mono.so中ELF頭的section資料,同時新增Anti-Dump(反dump,因為在動態除錯下破解者可以跳過你的加密過程,直接把解密後的內容從記憶體中dump出來)