1. 程式人生 > >Unity3D 遊戲加密解密那些事

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出來)