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库)
先下载Unity-Mono源码:https://github.com/Unity-Technologies/mono 直接下Zip包(注意Tag版本与开发用的Unity版本要相同)
找到/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解密的算法
关于Unity-Mono的编译参考这篇文章吧 Unity3D-重新编译Mono加密DLL
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出来)