본문 바로가기

안드로이드

(저장용) Unity3d 加密 Assembly-CSharp.dll (Android平台) 全记录

 

Unity3d 加密 Assembly-CSharp.dll (Android平台) 全记录

标签: Unity3d代码加密mono
 7724人阅读 评论(10) 收藏 举报
 分类:

目录(?)[+]



0、加密的原理

Unity3D 是基于 Mono的,我们平时写的 C# 脚本都被编译到了 Assembly-CSharp.dll ,然后 再由 Mono 来加载、解析、然后执行。

Mono 加载 Assembly-CSharp.dll 的时候就是读取文件到内存中,和平时读取一个 游戏资源 文件没什么区别。

为了防止别人破解,我们会对游戏资源加密,简单点的 比如修改文件的一个字节 或者 位移一下 。只要简单的修改一下,破坏原来的文件数据结构,别人就不能用通用的读取工具来读取了。

Mono 读取 Assembly-CSharp.dll 也是如此,我们只要简单的 修改 Assembly-CSharp.dll 的一个字节,就能破坏掉 Assembly-CSharp.dll 的数据结构,然后 Assembly-CSharp.dll 就不再是一个 dll 了,就变成了一个普通的文件,一个系统都不认识的未知类型的文件。

转自http://blog.csdn.NET/huutu http://www.thisisgame.com.cn



在 Android 中,由 libmono.so 来加载 Assembly-CSharp.dll 。

libmono.so  这就是 Mono 了 。





既然 Assembly-CSharp.dll 被我们加密了,那 libmono.so 这个通用的读取工具就不能再 读取已经加密的 Assembly-CSharp.dll 了,所以我们也要修改 重新编译 libmono.so ,给它加上解密函数才行。


unity3d 是基于 Mono2.0 的,而 Mono2.0是免费开源的。所以基于各种开源协议 ,Unity 官方也将自己修改过的 Mono 开源出来,我们下载过来然后修改 重新编译出自己的 libmono.so 。

项目托管在 Github 上,项目地址:

[html] view plain copy
  1. https://github.com/Unity-Technologies/mono  


了解到一些原理背景后就可以开始进行操作了。

1、安装ubuntu系统 


在 Windows 上面进行编译比较麻烦……在 Linux 或 Mac 上会比较简单,网上多数教程都是基于 Mac的,我这里选择了最新的 Ubuntu 系统。


Ubuntu 官方提供了 ISO 刻录工具:

[html] view plain copy
  1. http://www.pendrivelinux.com/downloads/Universal-USB-Installer/Universal-USB-Installer-1.9.6.3.exe   


Ubuntu 系统下载:

[html] view plain copy
  1. http://www.ubuntu.org.cn/download/desktop  


[html] view plain copy
  1. http://old-releases.ubuntu.com/releases/14.04.1/ubuntu-14.04.1-desktop-i386.iso?_ga=1.187436840.1241524278.1457318071  

转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

使用上面那个工具安装到U 盘然后安装到 电脑中。


2、下载ANDROID_NDK

安装完 Ubuntu 后,在 Ubuntu 中   ,注意32 和64位区别

64 位下载 :

[html] view plain copy
  1. http://pan.baidu.com/s/1dDAqnK1  
 

32位下载  :

[html] view plain copy
  1. http://pan.baidu.com/s/1sjoneRr   


sudo su 切换到root安装

[html] view plain copy
  1. ./android-ndk-r10e-linux-x86.bin  


安装后在安装目录里面找到 RELEASE.txt ,里面记录着NDK 完整版本号,修改为 r10e

(Mono的编译脚本是读取这个RELEASE.txt中记录的版本号,然后和编译脚本中填写的版本号做匹配的,如果不匹配就会去Google下载)


设置环境变量 ANDROID_NDK_ROOT

[html] view plain copy
  1. sudo gedit /etc/bashrc  

添加一行

[html] view plain copy
  1. export ANDROID_NDK_ROOT=/home/captain/Downloads/android-ndk-r10e;  


让环境变量立即生效
[html] view plain copy
  1. source /etc/bashrc  


测试是否添加成功
[html] view plain copy
  1. echo $ANDROID_NDK_ROOT  


3、编译 Development 版本的 libmono.so

从 Github 下载 unity-mono,我这里下载的4.6版本,到branch里面搜4.6

[html] view plain copy
  1. https://github.com/Unity-Technologies/mono/tree/unity-4.6  

解压,然后拷贝 mono-unity-4.6/external/buildscript/build_runtime_android.sh 到   mono-unity-4.6/   根目录中。

切换到mono-unity-4.6/ 目录中,使用 root 运行 build_runtime_android.sh ,

[html] view plain copy
  1. ./build_runtime_android.sh  
转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

会提示没有安装Git

然后安装git

[html] view plain copy
  1. sudo apt-get install git  

修改 build_runtime_android.sh Line 113 ,改为:

[html] view plain copy
  1. (cd "$KRAIT_PATCH_PATH" && perl ./build.pl)  

然后修改 mono-unity-4.6/external/android_krait_signal_handler/build.pl 

(这是脚本调用git clone过来的 https://github.com/Unity-Technologies/krait-signal-handler/)

删掉第一行 

[html] view plain copy
  1. #!/usr/bin/env perl -w  


继续运行出错,提示 ANDROID_NDK 版本不对,又要下载,下载又失败,修改成我们自己的版本。

用命令  找到build.pl 

[html] view plain copy
  1. Find / -name "build.pl"  

修改BuildAndroid 函数里面的 r9 为 自己下载的版本  r10e 

然后找到 

[html] view plain copy
  1. external/android_krait_signal_handler/jni/Application.mk   


修改里面的

[html] view plain copy
  1. TOOLCHAIN_VERSION :clang3.3  


[html] view plain copy
  1. TOOLCHAIN_VERSION :=4.8  

因为r10e里面已经没有3.3了,只有4.8。

转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

然后继续编译

提示没有 autoreconf

使用下面命令安装autoreconf:

[html] view plain copy
  1. sudo apt-get update  
  2. sudo apt-get install autoconf  


同样的方法,顺便把下面的包都安装一下

[html] view plain copy
  1. * autoconf  
  2. * automake  
  3. * bison  
  4. * gcc  
  5. * gettext  
  6. * glib >= 2.0  
  7. * libtool  
  8. * make  
  9. * perl  



都安装之后,继续执行脚本,开始编译滚代码了

过了十多分钟,编译好啦,用 find / -name "libmono.so" 找到编译出来的 so 文件。


这次编译出来的是 Development 版本的,现在就可以 把这个 编译出来的 libmono.so 拷贝到 自己电脑上,在导出 android 项目之后,替换掉 libs目录里面的 libmono.so 。

然后运行到 手机上测试是否OK,我这里是测试 OK的。


至于怎么样从 Ubuntu 电脑上把 libmono.so 拷贝到 Windows 电脑上,直接用 U 盘就可以。然而我没有 U盘,所以我在 Ubuntu 上开启了 SSH 服务,然后在 Windows 上面连接到了 Ubuntu ,然后下载到 Windows 电脑上,比较麻烦。

转自http://blog.csdn.Net/huutu http://www.thisisgame.com.cn

在Ubuntu上开启ssh-server ,安装完毕之后自动会启动

[html] view plain copy
  1. apt-get install openssh-server  

手动启动服务
[html] view plain copy
  1. service ssh start  
  2. /etc/init.d/ssh start  

查看是否启动成功
[html] view plain copy
  1. ps -s | grep ssh  


在windows上安装 FileZilla 官网 https://filezilla-project.org/
[html] view plain copy
  1. http://jaist.dl.sourceforge.net/project/filezilla/FileZilla_Client/3.16.0/FileZilla_3.16.0_win64-setup_bundled.exe  

输入Ubuntu的 ip 帐号 密码 端口22 进行SFTP连接 。


4、编译 Release 版本的 libmono.so 

修改所有的 build_runtime_android.sh 和 build_runtime_android_x86.sh 

找到 -fpic -g 去掉 -g

再次执行 build_runtime_android.sh 

开始编译滚代码了,

编译成功,用 find / -name "libmono.so" 找到编译出来的 so 文件。然后同样 拷贝到 Windows 电脑上,导出一个 Release 版本的 Android 工程出来,运行到手机上测试。

我这边测试OK。


5、修改 libmono.so ,增加 解密 函数

找到 /metadata/image.c 这个文件

找到 

[html] view plain copy
  1. mono_image_open_from_data_with_name  

这个函数,这个函数就是用来读取 dll 的。修改这个函数,添加解密代码。

[html] view plain copy
  1. mono_image_open_from_data_with_name (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, const char *name)  
  2. {  
  3.     MonoCLIImageInfo *iinfo;  
  4.     MonoImage *image;  
  5.     char *datac;  
  6.       
  7.     /* 加入 Decrypt */  
  8.     if(name != NULL)  
  9.     {  
  10.         if(strstr(name,"Assembly-CSharp.dll")){  
  11.             data[0]-=1; //这里注意对应自己的加密方式进行修改。这里是对第一个字节 -1 ,对应加密算法是 +1;  
  12.         }  
  13.     }  
  14.       
  15.     ………………  
  16.     ………………  
  17. }  

这里注意对应自己的加密方式进行修改。这里是对第一个字节 -1 ,对应加密算法是 +1;
转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn
再次执行 build_runtime_android.sh 

开始编译滚代码了,

编译成功,用 find / -name "libmono.so" 找到编译出来的 so 文件。拷贝到Windows 电脑上。

再次导出一个 Release 版本的 工程,然后运行到 手机上发现,场景正常,但是代码都不执行了!!

这是因为 我们修改了 libmono.so ,增加了解密函数,但是当前的 Assembly-CSharp.dll 是没有加密的!!!,所以 libmono.so 这里是读取 Assembly-CSharp.dll 失败了,所以我们写的代码都没有执行。


现在已经知道了怎么增加解密方法,以及编译 Development 和 Release 版本。

所以现在分别编译 Development 、 Release 的加密了的 libmono.so 。

每次编译 都会自动生成  armv7a 、 x86  这两个 CPU 的 libmono.so 。


我们把 这几个 libmono.so 拷贝到 Unity 项目中,存放到 Editor 文件夹,可以下载我的例子查看。


6、加密 Assembly-CSharp.dll 

在 Unity3d 中导出 Android 工程之后,对 Assembly-CSharp.dll 进行加密,然后替换掉 导出的 Android 工程中的 libmono.so 文件。

Unity3d 提供了 Android 工程导出完毕的回调,我们在这个回调中 替换掉 导出的 Android 工程中的 libmono.so 文件, 如下:

BuildPostprocessor.cs :

[csharp] view plain copy
  1. /** 
  2.  * 文件名:BuildPostprocessor.cs 
  3.  * Des:在导出Eclipse工程之后对assets/bin/Data/Managed/Assembly-CSharp.dll进行加密 
  4.  * Author:Captain 
  5.  * **/  
  6.   
  7.   
  8. using UnityEngine;  
  9. using UnityEditor;  
  10. using UnityEditor.Callbacks;  
  11. using System.IO;  
  12.   
  13. public class BuildPostprocessor  
  14. {  
  15.     [PostProcessBuildAttribute(1)]  
  16.     public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)  
  17.     {  
  18.         if (target == BuildTarget.Android && (!pathToBuiltProject.EndsWith(".apk")))  
  19.         {  
  20.             Debug.Log("target: " + target.ToString());  
  21.             Debug.Log("pathToBuiltProject: " + pathToBuiltProject);  
  22.             Debug.Log("productName: " + PlayerSettings.productName);  
  23.   
  24.             string dllPath = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "assets/bin/Data/Managed/Assembly-CSharp.dll";  
  25.   
  26.             if (File.Exists(dllPath))  
  27.             {  
  28.                 //加密 Assembly-CSharp.dll;  
  29.                 Debug.Log("Encrypt assets/bin/Data/Managed/Assembly-CSharp.dll Start");  
  30.   
  31.                 byte[] bytes = File.ReadAllBytes(dllPath);  
  32.                 bytes[0] += 1;  
  33.                 File.WriteAllBytes(dllPath, bytes);  
  34.   
  35.                 Debug.Log("Encrypt assets/bin/Data/Managed/Assembly-CSharp.dll Success");  
  36.   
  37.                 Debug.Log("Encrypt libmono.so Start !!");  
  38.   
  39.                 Debug.Log("Current is : " + EditorUserBuildSettings.development.ToString());  
  40.   
  41.                 //替换 libmono.so;  
  42.                 if (EditorUserBuildSettings.development)  
  43.                 {  
  44.                     string armv7a_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/armeabi-v7a/libmono.so";  
  45.                     File.Copy(Application.dataPath + "/MonoEncrypt/Editor/libs/development/armeabi-v7a/libmono.so", armv7a_so_path, true);  
  46.   
  47.                     string x86_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/x86/libmono.so";  
  48.                     File.Copy(Application.dataPath + "/MonoEncrypt/Editor/libs/development/x86/libmono.so", x86_so_path, true);  
  49.                 }  
  50.                 else  
  51.                 {  
  52.                     string armv7a_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/armeabi-v7a/libmono.so";  
  53.                     File.Copy(Application.dataPath + "/MonoEncrypt/Editor/libs/release/armeabi-v7a/libmono.so", armv7a_so_path, true);  
  54.   
  55.                     string x86_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/x86/libmono.so";  
  56.                     File.Copy(Application.dataPath + "/MonoEncrypt/Editor/libs/release/x86/libmono.so", x86_so_path, true);  
  57.                 }  
  58.   
  59.                 Debug.Log("Encrypt libmono.so Success !!");  
  60.             }  
  61.             else  
  62.             {  
  63.                 Debug.LogError(dllPath+ "  Not Found!!");  
  64.             }  
  65.         }  
  66.     }  
  67. }  

转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

好了,以上就是所有的 步骤。

再次导出 Android 工程,然后运行到手机上,测试是否OK,我这边是测试 OK的。

然后找到 导出的项目中的 dll 文件,在下面的路径:

[html] view plain copy
  1. assets/bin/Data/Managed/Assembly-CSharp.dll  

用 .NetReflector 查看、或者拖入到 MonoDeveloper 中,发现都无法正常打开。

这说明我们加密成功了!!






可以下载我的测试项目来查看和测试:

[html] view plain copy
  1. http://pan.baidu.com/s/1nuloVcH  

我也把它打包成了 Package:

[html] view plain copy
  1. http://pan.baidu.com/s/1o6ZH02u  

注意这里只是简单加密,线上项目不要搞的这么简单…………


参考了网上各种教程:

[html] view plain copy
  1. http://csftech.logdown.com/posts/452269-android-unity-encryption  

[html] view plain copy
  1. https://github.com/Unity-Technologies/mono  

[html] view plain copy
  1. http://www.luzexi.com/unity3d/%E6%B8%B8%E6%88%8F%E6%9E%B6%E6%9E%84/%E5%89%8D%E7%AB%AF%E6%8A%80%E6%9C%AF/2015/04/11/Unity3D-%E9%87%8D%E6%96%B0%E7%BC%96%E8%AF%91Mono%E5%8A%A0%E5%AF%86DLL.html  

[html] view plain copy
  1. http://www.xuanyusong.com/archives/3553