unity 加密、防止反编译、mono编译

最近在弄unity的打包安全的问题,下面就记录下自己搞定整个过程踩过来的坑吧,一方面留个记录,另一方面给新手一个指引。

为什么要加密呢

这个问题怎么说呢?打个比方吧,就好比人为什么要穿漂亮衣服打扮下自己一样,无非是不让别人看到不改看的地方。。。此处省略一万字。。。

哪些东西要加密呢

其实我看大牛们的博客,直接了当,直接讨论加密方法,原理,新手一开始就搞的云里雾里,这里主要照顾到新手,老鸟直接自行略过即可。

unity在打包的时候会吧我们写的C#代码直接打包到一个dll中去,具体位置如下:

           windows:

             Data目录\Managed\Assembly-CSharp.dll

           Android:

             assets\bin\Data\Managed\Assembly-CSharp.dll 

             这里需要注意下,如果打包的是AndroidStudio工程路径不太一样,但差别不大;
             src\main\assets\bin\Data\Managed\Assembly-CSharp.dll

           IOS:
             这个暂时不讨论,ISO目前不考虑加密。

如果unity工程中没有脚本文件那么就不会有这个Assembly-CSharp.dll,但是这个dll是赤裸裸的暴露的啊,随便拿个反编译工具ILSpy、Reflector、de4dot等等,直接就能看到源码,是不是很无力,很无解。那么我们要干的工作第一步就是加密这个文件。

加密Assembly-CSharp.dll

思路:打包完后,通过加密算法加密Assembly-CSharp.dll

这里加密用到xxtea加密库.C#版本 https://github.com/xxtea/xxtea-dotnet,把XXTEA.cs添加到自己的项目中去。

关键代码:以windows为例

    [MenuItem("Build/x86")]
    private static void BuildWin_x86()
    {
        BuildWindows(BuildTarget.StandaloneWindows);
    }
     [MenuItem("Build/x64")]
    private static void BuildWin_x64()
    {
        BuildWindows(BuildTarget.StandaloneWindows64);
    }
    private static void BuildWindows(BuildTarget target)
    {
        //打包
        string path = EditorUtility.SaveFilePanel(target.ToString(), EditorPrefs.GetString("BuildPath"), PlayerSettings.productName, "exe");
        if (string.IsNullOrEmpty(path))
            return;
        BuildPlayerOptions _buildOption = new BuildPlayerOptions();
        _buildOption.locationPathName = path;
        _buildOption.scenes = EditorBuildSettingsScene.GetActiveSceneList(EditorBuildSettings.scenes);
        _buildOption.target = target;
        BuildPipeline.BuildPlayer(_buildOption);

        //加密
        EncryptyWindowsAssemblyCSharp(path);
    }

    /// <summary>
    /// windows 平台加密
    /// </summary>
    /// <param name="path"></param>
    private static void EncryptyWindowsAssemblyCSharp(string path)
    {
        string acsPath = path.Replace(".exe", "_Data") + "/Managed/Assembly-CSharp.dll";
        byte[] readByte = File.ReadAllBytes(acsPath);
        string key = "这里是你的加密密钥";
        byte[] encrypt_data = Xxtea.XXTEA.Encrypt(readByte, key); //用xxtea加密库加密  
        File.WriteAllBytes(acsPath, encrypt_data);
    }

至此Assembly-CSharp.dll加密的任务完成了,然后你打包,运行,发现

是不是很气愤,什么鬼,mono还报错,这就引出下面的问题在mono中解密Assembly-CSharp.dll。

在mono中解密Assembly-CSharp.dll

首先要知道Assembly-CSharp.dll是在mono上运行的,mono是什么东西,额,你就把理解为跨平台的虚拟环境吧,对,没错扩平台。那么mono在哪里呢,路径如下:

       windows:

               Mono\mono.dll

       Android:

               libs\armeabi-v7a\libmono.so
               或 libs\x86\libmono.so   两种不同的架构

              当然AndroidStudio路径不太一样:

               src\main\jniLibs\armeabi-v7a\libmono.so
               或src\main\jniLibs\x86\libmono.so
       IOS:

               这个不讨论了。

讨论完路径,再来看如何解密。

思路:修改mono源代码,增加解密算法,加载Assembly-CSharp.dll时解密即可。

下面看如何实现解密:

1.下载mono源码

mono源码:https://github.com/Unity-Technologies/mono

输入上图关键字,选择自己unity对应的版本,我用的unity是5.6.1版本,所以选择了unity-5.6分支,然后点击右上角的按钮直接zip下载就行,用git也行。解压到本地,备用。

2.下载xxtea加密库

这里是c语言版本的xxtea加密库:
https://github.com/xxtea/xxtea-c

解压到本地备用。

3.把xxtea库添加到mono库

1.在下载好xxtea后,复制xxtea.c和xxtea.h两个文件到开始下载的mono的源码里。
具体位置在mono/mono/metadata文件夹下。

2.然后再用vs2010打开mono/msvc/mono.sln,将上面的xxtea的两个文件添加到libmono里
(项目右键→添加项→现有项→选择xxtea.h和xxtea.c文件即可),记得保存一些解决方案,
或者退出vs,会提示你保存解决方案,要不然这两个文件没有引入哦。

4.mono中添加解密算法

<1>找到libmono下的image.c文件,添加头文件  #include "xxtea.h"
<2>在image.c文件中找到mono_image_open_from_data_with_name函数,这个函数就是mono加载Assembly-CSharp.dll的入口,我们在这里添加解密函数。
<3>增加代码
    //Decrypt
    if (strstr(name, "Assembly-CSharp.dll") != NULL)
    {
        const char *key = "这是上面unity加密Assembly-CSharp.dll的那个密钥,这里用来解密"; //记得替换密钥
        data = xxtea_decrypt(data, data_len, key, &data_len);
    }

这是添加头文件:

这是解密的函数:

到此添加完了加密算法,下面开始编译mono了。

注意:Andorid端因为要在linux或mac下编译,没法像vs那样方便的添加文件引用了,所以加密文件xxtea.h需要合并到image.h 中去,xxtea.c需要合并到image.c中去,这样编译的时候就不会提示找不到解密方法了。

编译mono源码

windows平台:
一定要用vs2010编译啊,用vs2013、vs2015各种死翘翘,。。。心好累哦,没有vs2010的话,去装一个,编译环境直选C++的就行,其他的啥都别装,要不了太大的空间。

      <1>打开vs 的命令行工具, Visual Studio x64 Win64 命令提示(2010)
      <2>进入mono-unity-5.6\msvc目录
      <3>执行 msbuild.exe mono.sln /p:Configuration=Release_eglib

      生成的dll在mono-unity-5.6\build\embedruntimes\win64,同时你还要编译32位的
      打开vs x86 的命令行工具  Visual Studio 命令行提示(2010) ,同样执行上面的操作
      注意:vs2010默认就是x86平台的,同时兼容x64平台

注意:如果LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏类似的问题,这是因为按照高版本的vs之后再去安装的vs2010,cvtres.exe版本错误导致的结果,所以凡是能使VS链接器找到正确的cvtres.exe版本的方法都可以解决该问题。
解决办法: 当前系统中存在两个cvtres.exe文件,版本不同。让VS2010使用.NET 4.5的cvtres.exe程序。重命名或删除:(vs2010安装的位置)C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cvtres.exe。这样C:\Windows\Microsoft.NET\Framework\v4.0.30319 (.NET 4.5)中的cvtres.exe文件就可以被VS2010使用。

Android平台:
楼主用mac编译的,当然也可以用linux编译。编译环境网上有好多教程,我就不细说了,遇到的坑自己度娘就行。

注意:

  1. 执行build_runtime_android.sh时需要在mono-unity-5.6这个目录下

  2. 在文件第74行:-fpic -g -funwind-tables \中。把-g改为-O2(O0,O1,O2,O3分为好几个压缩档次)。通过更改这个可以编译出release版本。会比debug版本体积更小。

  3. 找到第154到156行:

    #clean_build "$CCFLAGS_ARMv5_CPU" "$LDFLAGS_ARMv5" "$OUTDIR/armv5" 
    #clean_build "$CCFLAGS_ARMv6_VFP" "$LDFLAGS_ARMv5" "$OUTDIR/armv6_vfp" 
    clean_build "$CCFLAGS_ARMv7_VFP" "$LDFLAGS_ARMv7" "$OUTDIR/armv7a" 
    

    注释掉前两行。我们只需要armeabi-v7a和x86类型的libmono.so文件。

  4. 修改build_runtime_android_x86.sh文件内容:

    修改第71行:-fpic -g\。去掉-g改为-fpic \。为了防止x86下的手机进入游戏卡顿的情况。
    

如果顺利的话就会提示如下:

到此mono库是编译完成了,但是还没有玩,android平台的libmono.so是可以用ida pro神器查看的,所以需要对libmono.so二次加密,具体参见MOMO大大的教程http://www.xuanyusong.com/archives/3571

替换带解密功能的mono库

对,最后一步就是自动化,替换新编译好的mono库,接着在加密方法那个类里写:
核心代码如下:

    private static void BuildWindows(BuildTarget target)
    {
        //打包
        string path = EditorUtility.SaveFilePanel(target.ToString(), EditorPrefs.GetString("BuildPath"), PlayerSettings.productName, "exe");
        if (string.IsNullOrEmpty(path))
            return;
        BuildPlayerOptions _buildOption = new BuildPlayerOptions();
        _buildOption.locationPathName = path;
        _buildOption.scenes = EditorBuildSettingsScene.GetActiveSceneList(EditorBuildSettings.scenes);
        _buildOption.target = target;
        BuildPipeline.BuildPlayer(_buildOption);

        //加密
        EncryptyWindowsAssemblyCSharp(path);

        //替换mono,用于解密使用
        ReplaceMonoDll(path, target);

        Debug.Log(target.ToString()+"这是加密的哦,你解不开解不开解不开!");
    }
 /// <summary>
 /// 替换mono  
 /// </summary>
 /// <param name="path"></param>
 /// <param name="target"></param>
private static void ReplaceMonoDll(string path,BuildTarget target)
{
    switch (target)
    {
        case BuildTarget.StandaloneWindows:
        case BuildTarget.StandaloneWindows64:
            { 
                string monoPath = path.Replace(".exe", "_Data") + "/Mono/mono.dll";
                string encryptMonoPath = Application.dataPath + "/" + AppConst.AppName + "/Editor/mono/" + target.ToString() + "/mono.txt"; //用txt保存,这个无所谓,我们只关心里面的内容
                byte[] readByte = File.ReadAllBytes(encryptMonoPath);
                File.WriteAllBytes(monoPath, readByte);  //当然你可以选择直接复制替换
            }
            break;
        case BuildTarget.Android:
            {
               //Android的代码自己写哦,需要注意打包出来的是Eclipse还是Android Studio工程
            }
            break;
        default: 
            break;
    }
}

总结

加密只是为了保护自己的劳动成果,如果你选择开源,那就无所谓了。当然unity5.4版本之后Andorid可以选择IL2CPP这个黑科技了,不采用mono了,这样打包之后就是c++代码,我们也就不用操心加密这回事了。如果仍然选择mono就需要本文这种类似的处理了。整个过程是痛苦的,但是成功完成所有步骤那叫一个酣畅淋漓啊。祝君顺利,如有不明白的地方欢迎留言交流。

附录

给出的参考链接:

http://www.xuanyusong.com/archives/3553

http://blog.csdn.net/kitok/article/details/72472142

http://lib.csdn.net/article/dotnet/55414

http://blog.csdn.net/yupu56/article/details/53216705

http://blog.csdn.net/swj524152416/article/details/69946259

This asset obfuscates your code to make it harder for bad guys to reverse engineer your projects. Specifically designed for Unity, it seamlessly links in with its build process. The top priority of this package is to work straight out of the box with no extra steps required. While other obfuscators can stop a game from working, Beebyte's obfuscator looks for specific Unity related code that must be protected. The contents of your source files are unchanged, the obfuscation targets the compiled assembly. Features: - Supports IL2CPP - Supports Assembly Definition Files (Unity 2017.3+) - Removes Namespaces without any conflicts - Recognises Unity related code that must not be changed - Renames Classes (including MonoBehaviours) - Renames Methods - Renames Parameters - Renames Fields - Renames Properties - Renames Events - String literal obfuscation - Adds fake methods - Easy and extensive customisation using the Unity inspector window - Consistent name translations are possible across multiple builds and developers - Semantically secure cryptographic naming convention for renamed members The asset works for both Unity Free and Unity Pro version 4.2.0 onwards (including Unity 5 & 2017 & 2018). Build targets include Standalone, Android, iOS, WebGL, UWP. Other platforms are not guaranteed or supported but may become supported at a future date. IL2CPP builds are much harder to reverse engineer but strings and member information (class, method names etc) are visible in the global-metadata.dat file. Obfuscation will apply to this file adding further security. Why not complement your security with the Anti-Cheat Toolkit - a great third party asset. For more information about the Obfuscator, please see the FAQ
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值