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

  • 5
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值