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平台:依旧能在assetsbinDataManaged 目录下找个这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出来)

原文链接:https://blog.csdn.net/blueeffie/article/details/49072263

如果觉得我的文章对你有用,请随意赞赏