Unity3D放破解反编译。DLL加密,mono解密。全程详解。

Unity3D加密教程


    引言 :

         为了防止别人通过反编译来破解修改自己的游戏项目。可以通过两种比较成熟的方案来预防。一种是混淆,另一种就是加密(加壳)。由于加壳后的文件反编译比混淆后的难度更大,所以我们这里采取加密的方法对PC平台和安卓平台的应用进行加密。

         那么我们去加密什么呢?    Unity通过Mono来达到跨平台的效果。在Build编译时会将你编写的code转为符合CLI的CIL(Common Intermediate Language,中文:中间语言),并且主要的Code会编译在Assembly-CSharp.dll里面,然后再有mono来加载,解析,执行。Mono加载Assembly-CSharp.dll的时候就是读取文件到内存中,和读取一个游戏资源文件没多大区别。所以我们要做的就是把这个Assembly-CSharp.dll文件加密,简单点的可以修改文件的一个字节或者位移一下,破坏其原有的结构。就无法使之通过例如Reflector9VSPro去反编译,因为此时的Assembly-CSharp.dll已经不是一个正确的dll文件了。可尴尬的是,别人没法反编译出文件内容了,Mono自己也不认识了。这将导致,游戏无法运行。所以,我们在对其加密的同时也要加入相应的解密算法。

         image.c脚本在游戏运行时会去主动加载Assembly-CSharp.dll文件。那么我们在image.c相应的方法里面加入解密算法,然后从新编译Mono,生成相应的程序集,来替换待解密项目中的相应文件。就可以达到游戏运行正常又安全的效果。

        So ,Follow me to see.

Window篇

加密Assembly-CSharp.dll :

需要工具:

  1. Reflector9VSPro:反编译工具:用于成果测试。

  2. xxtea:此教程所用的第三方加密解密算法:用于加密解密。

  3. MinGw:使用gcc编译:用于制作加密工具。

  1. 首先下载需要工具的工具3。进行安装配置。可在cmd窗口中用“gcc -v”指令来查看安装结果。
  2. 下载工具2。文件夹中有xxtea.h、xxtea.c两个文件。这两个就是加密解密的代码。
    • 创建新文件夹“encrypt”,把这两个文件放在“encrypt”文件夹中。
    • 新建C语言脚本“EncryptManage.c”,同样放在“encrypt”中。脚本内容:
  #include <stdio.h>  
  #include <string.h>  
  #include <stdlib.h>  
  #include "xxtea.h"  
  #define SIZE 1024*1024*10  
  void main()  
  {  
      FILEFILE *infp = 0;  
      if((infp=fopen("Assembly-CSharp.dll","rb"))==NULL)  
      {  
          printf("Assembly-CSharp.dll Read Error\n");//打开操作不成功  
          return;//结束程序的执行  
      }  
    
      //char buffer[SIZE];  
      char* buffer = (char*)malloc(sizeof(char)*SIZE);  
      memset(buffer,0,sizeof(char)*SIZE);  
    
      int rc = 0;  
      int total_len = 0;  
    
      total_len = fread(buffer , sizeof(unsigned char) , SIZE , infp);  
      printf("Read Assembly-CSharp Successfully and total_len : %d \n" , total_len);  
    
      //加密DLL  
      size_t len;  
      char* key = "123456";  //此处位密钥。可自由更改
      charchar *encrypt_data = xxtea_encrypt(buffer,total_len, key, &len);  
    
      printf("Encrypt Dll Successfully and len : %d\n" , len);  
    
      //写Dll  
      FILE* outfp = 0;  
      if((outfp=fopen("Assembly-CSharp_encrypt.dll","wb+"))==NULL)  
      {  
          printf("Assembly-CSharp_encrypt.dll Read Error\n");//打开操作不成功  
          return;//结束程序的执行  
      }  
    
      int rstCount = fwrite(encrypt_data , sizeof(unsigned char) , len , outfp);  
        
      fflush(outfp);  
    
      printf("Write len : %d\n", rstCount);  
    
      fclose(infp);  
      fclose(outfp);  
    
      free(buffer);  
      free(encrypt_data);  
  } 
  • 打开cmd窗口。cd到encrypt文件夹中。然后使用gcc编译EncryptManage.c文件。生成可自动化加密的exe文件"EncryptManage.exe"。
    代码为gcc xxtea.c EncryptManage.c –o EncryptManage
  1. Build PC平台的游戏项目。在生成的文件夹中找到我们需要加密的xxxxx\xxxx_Data\Managed\Assembly-CSharp.dll文件。把这个文件同样放在第二步中创建的encrypt文件夹中。
  2. 直接点击第二步创建的EcryptManage.exe文件。执行加密Assembly-CSharp.dll文件的操作。生成Assembly-CSharp_encrypt.dll文件。这个就是加密后的文件。把他从新名为原Assembly-CSharp.dll名称。放回他所在源目录。
  3. 使用Reflector9VSPro工具进行反编译Assembly-CSharp.dll。如果发现反编译失败。证明加密成功。此时可相应的运行游戏文件。会发现游戏也无法正常运行。

重编译可解密的mono.dll文件 :

需要工具:
注意:如果你希望编译顺利进行,不把时间浪费在一些坑上。就需要这些工具。
  1. Visual Studio 2010:去编译和修改mono项目:由于mono-unity官方项目使用VS2010创建的,所用使用vs2010会避免很多的冲突,在用VS2015的时候就会碰到配置,引用,平台工具集型号…等等问题,再加上其他各种未知问题,最后选择了VS2010版本,才成功。

  2. mono-unity-5.6:我在此用的时5.6的包,观者可根据需求下载相应版本的包:我们通过对它的修改添加,从编译。生成相应的mono.dll文件,用于解密。经过测试,可编译成功的版本有4.6,5.1,5.5,5.6

  1. 使用上文中的加密文件xxtea.c和xxtea.h复制到下载的mono的工程目录里,具体位置在mono-unity-5.6\mono\metadata文件夹下。

  2. 然后用vs2010打开工程文件mono-unity-5.6/msvc/mono.sln,打开之后,通过“解决方案资源管理器”找到libmono项,再将复制在mono-unity-5.6\mono\metadata文件夹中两个xxtea文件添加到libmono项中,并找到libmono下的image.c,打开,开始添加解密代码。

  3. 由上述引言可知,image.c就是我们要添加解密代码的文件。首先,添加引用头文件#include"xxtea.h"和#include <stddef.h>

  4. 在脚本中找到方法 “mono_image_open_from_data_with_name”。这个就是加载Assembly-CSharp.dll的入口。那么我们就在此方法中添加我们的解密代码。
    代码如下:

MonoImage *  
mono_image_open_from_data_with_name (charchar *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status,    gboolean refonly, const charchar *name)  
  {  
      MonoCLIImageInfo *iinfo;  
      MonoImage *image;  
      charchar *datac;  
    
    //第一个参数data指向运行时Assembly-CSharp.dll的内存地址
      if (!data || !data_len) {  
          if (status)  
              *status = MONO_IMAGE_IMAGE_INVALID;  
          return NULL;  
      }  
    
     //这是添加的代码,开始   你也可以换成自己想要的解密方法
     if(name != NULL)
     {
      if (strstr(name, "Assembly-CSharp.dll")) {  
          char* key = "123456";     //此处密钥需要对应加密时候所创建的密钥
          size_t len;  
          char* decryptData = (charchar *)xxtea_decrypt(data, data_len,key, &len);  
          int i = 0;  
          for (i = 0; i < len; ++i)  
          {  
              data[i] = decryptData[i];  
          }  
          g_free(decryptData);  
    
          data_len = len;  
      } 
    }
     //这是添加的代码,结束
      datac = data;  

5. 最后,我们开始编译工程。
* 打开 Visual Studio Command Prompt(2010)
* cd 到mono-unity-5.6\msvc目录中
* 执行msbuild.exe mono.sln /p:Configuration=Release_eglib命令

注意:直接打开mono.sln解决方案,通过Visual Studio直接生成是编译不了的,这是个坑。

6.我在这里用了54秒就编译成功了。生成的dll位置在mono\builds\embedruntimes\win32\mono.dll。(63位的mono.dll文件要用Visual Studio x64 Win64 命令提示(2010)去编译)

7. 最后把你编译出来的mono.dll文件复制到你的项目包中,替换Mono文件夹中的源mono.dll文件。
好了,OK。运行游戏,完美运行。再用反编译工具去反编译Assembly-CSharp.dll。发现无法反编译。


Android篇

注意:由于windows和ubuntu平台中对Assembly-CSharp.dll的加密操作是一样的,所用不做多余叙述,Assembly-CSharp.dll的加密可以直接使用windows平台加密后的Assembly-CSharp.dll文件。 在window篇中我们要使用的是编译后的mono.dll文件。但是在Android项目中,我们要使用的是libmono.so文件。其他需要操作的对象文件都一样。所以我们只需要使用Ubuntu32为系统去重编译可解密的libmono.so文件即可。

Ubuntu 32位虚拟系统搭建:

需要工具:

  1. Ubuntu32位:ubuntu系统:由于windows上面比较麻烦,而且错误特别多。而ubuntu64位不选择的原因是坑很多。用32位系统至少可以让坑少一半。

  2. VMware Workstation :桌面虚拟计算机软件:用于搭载Ubuntu系统。

  3. android-ndk-r10e-linux 32位:基于linux系统的NDK:NDK的版本是根据项目需求下载的,此处操作的mono-unity-5.6的mono项目包,此项目要求使用“r10e”版本的NDK。

  1. 通过VMware Workstation 搭建Ubuntu虚拟环境。
  2. 安装编译所需相关工具:
    • autoconf
    • automake
    • bison
    • gcc
    • gettext
    • git    //编译时,项目会需要git命令去下载依赖文件。
    • glib >=2.0    //编译所需库文件。
    • libtool    //如果你是64为的NDK。由于交叉编译工具是32为的。则需要安装的libtool也是32为的。所以,这个工具在安装的时候要使用 sudo apt-get install libtool*命令安装,注意要加“  *  ”。这样可以安装整个libtool-bin库。非则的话就算安装了libtool也会一直提示没有安装libtool。在32位系统下不存在这个问题。
    • make    //编译工具。
    • perl    //用于运行pl脚本。

命令为sudo apt-get install autoconf automake bison build-essential gettext git libglib2.0 libtool* perl
3. 配置NDK的环境,配置步骤:
a. 在终端输入sudo gedit ~/.bashrc。打开环境变量配置文件。
b. 在文件末端直接加入环境变量:

 NDK_ROOT=/home/xxxx/xxxx/android-ndk-r10e   
 NDK=$NDK_ROOT
 ANDROID_NDK_ROOT=$NDK_ROOT
 export NDK_ROOT NDK ANDROID_NDK_ROOT

c. 保存并且使环境生效:` source ~/.bashrc ``

编译前的文件修改 :

需要工具:

  1. mono-unity-5.6:观者可根据项目需求下载相应的包。

  1. 下载mono-unity-5.6文件之后。这个文件夹的上层级最少要有两层。如果不够,则新建空文件夹用于存放此文件夹。
    原因:在build_runtime_android.sh文件中,大概56行有 :KRAIT_PATCH_PATH="${CWD}/../../android_krait_signal_handler/build" 。这个表示,在编译的时候会下载依赖文件“android_krait_signal_handler”。如果无法保证上层级在两层以上,也可以更改这行代码。
  2. 在下载的mono-unity-5.6文件夹中。找到build_runtime_android.sh文件。具体位置在\mono-unity-5.6\external\buildscripts文件夹中。把他放在\mono-unity-5.6\根目录中。
  3. 打开 build_runtime_android.sh文件。在15行perl ${BUILDSCRIPTSDIR}/PrepareAndroidSDK.pl -ndk=r10e -env=envsetup.sh && source envsetup.sh中。 -ndk=r10e描述的是所需ndk版本。
  4. 修改build_runtime_android.sh文件内容:
    • 在文件第6行的export ANDROID_PLATFORM=android-9下面添加exportANDROID_NDK_ROOT=/home/xxxx/xxxx/android-ndk-r10e。为防止不必要的错误,手动指定ndk目录。
    • 如果出现无法找到 envsetuo.sh文件的错误。则需要手动指定envsetuo.sh文件所在目录。第15行perl ${BUILDSCRIPTSDIR}/PrepareAndroidSDK.pl -ndk=r10e -env=envsetup.sh && source envsetup.sh末端的``source中直接指定文件目录source xxxx\xxxx\mono-unity-5.6\envsetup.sh`。
    • 在文件第74行:-fpic -g -funwind-tables \中。把-g改为-O2(O0,O1,O2,O3分为好几个压缩档次)。通过更改这个可以编译出release版本。会比debug版本体积更小。
    • 找到第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文件。所以注释掉可以节省编译时间,
  1. 修改build_runtime_android_x86.sh文件内容:

    • \mono-unity-5.6\external\buildscripts文件夹中找到:build_runtime_android_x86.sh文件。打开准备修改。
    • 同build_runtime_android.sh的修改一样。在第6行下面添加NDK目录:exportANDROID_NDK_ROOT=/home/xxxx/xxxx/android-ndk-r10e
    • 修改第71行:-fpic -g\。去掉-g改为-fpic \。为了防止x86下的手机进入游戏卡顿的情况。
  2. 注意: 如果你下载的是32为的NDK可以忽略此步骤。修改PrepareAndroidSDK.pm文件内容。文件在/mono-unity-5.6/external/buildscripts/下面。

    • 直接翻到最后的第435行中,找到PrepareNDK方法。
    sub PrepareNDK
       {
     	my ($ndk) = @_;
    	my $ndk_root = $ENV{$NDK_ROOT_ENV};
     	$ndk_root = $1 if($ndk_root=~/(.*)\/$/);
    
         # 读取NDK目录下的RELEASE.TXT文件以查看NDK版本号
    	if (-e $ndk_root and open RELEASE, "<", catfile("$ndk_root", "RELEASE.TXT"))
    		{
    			my @content = <RELEASE>;
    			close RELEASE;
    			chomp(@content);
    			my $current = $content[0];
    			print "\tCurrently installed = " . $current . "\n";
    	
    	         
    			# remove the possible '(64-bit)' from the end
    			#如果你下载的NDK是Linux 64-bit NDK,它的版本号是”  r10e-rc4(64-bit) “。那么将会在次数出错。程序将一直找不到你的NDK。
    			#所以需要修改下面代码为:`my @curr_arr = split(/\-|\s/, $current)`
    			#或者直接修改NDK目录下RELEASE.TXT文件内容为:` r10e (64-bit) `或者 ` r10e` 
    			my @curr_arr = split(' ', $current);
    			$current = $curr_arr[0];
    			
    			if ($ndk eq $current)
    			{
    				print "\tNDK '$ndk' is already installed\n";
    				return;
    			}
    	   .....
    }
    
  1. 检查编译所需环境是否合格。

    • 方法一:打开终端。cd 到mono-unity-5.6目录中,使用管理员权限执行autogen.sh文件。命令为:sudo ./autogen.sh。这是个批处理文件,帮我们检查编译mono-unity所需要的环境。如果出现缺失库的错误,那么根据错误进行相应修改和安装。这个文件会帮你执行configure,make,make clean,make distclean等命令。
    • 方法二:在终端中cd到mono-unity-5.6目录中。使用管理员权限执行sudo ./configure --prefix=/usr/bin命令。也是检查编译环境是否合格,如何没有合格,会报错。如果合格,则会他提示你执行make指令。到了这步,说明你的环境大致安装完成了。
  2. 开始第一次编译。管理员身份执行复制在mono-unity-5.6根目录下的build_runtime_android.sh文件,命令为:sudo ./ build_runtime_android.sh不要使用"sudo sh build_runtime_android.sh "去执行。第一次编译通常情况下都会碰到/usr/bin/env: perl -w: No such file or directory的错误。没关系。这次编译只是为了下载krait-signal-handler依赖文件。

  3. 如果你出现上面8所述的/usr/bin/env: perl -w: No such file or directory错误。那么打开刚才下载的krait-signal-handler文件夹。找到里面的build.pl文件。修改第一行#!/usr/bin/env perl -w#!/usr/bin/perl -w

  4. 注意: 如果你下载的是32为的NDK可以忽略此步骤。在修改完上述9中所述的错误之后。还需要用/mono-unity-5.6/external/buildscripts/目录下的PrepareAndroidSDK.pm替换/krait-signal-handler/目录下的PrepareAndroidSDK.pm

编译libmono.so文件及其之后的操作 :

  1. 开始第二次编译。 运行命令为:sudo ./ build_runtime_android.sh

    • 如果出错:查看/mono-unity-5.6/根目录下面的config.log文件。编译的错误会全部输出在此文件中。
    • 如果不出错,那么恭喜你。在/mono-unity-5.6/builds目录下就是编译出来的armv7a和x86的libmono.so文件。测试完成,那么加入解密代码。然后进行最终编译。
  2. 添加解密代码。还是找到image.c文件。在 “mono_image_open_from_data_with_name”方法中添加解密代码。此处和windows平台不同的是需要把xxtea.cxxtea.h的代码直接合并到image.c以及image.h中。

  3. 再次编译。成功生成libmono.so文件。然后复制到windows平台下。替换源游戏apk文件中相应的文件。包括。两个libmono.so以及Assembly-CSharp.dll文件。

从新打包APK :

需要工具:

  1. Apktool:反编译apk和从新打包apk工具。工具相关教程:Android apk反编译及重新打包流程



这里写图片描述

Hello ,I am 李本心明


首先谢谢大家的支持,其次如果你碰到什么其他问题的话,欢迎来 我自己的一个 讨论群559666429来(扫扫下面二维码或者点击群链接 Unity3D[ 交流&副业]CLUB ),大家一起找答案,共同进步 同时欢迎各大需求商入住,发布自己的需求,给群内伙伴提供副职,赚取外快。对了,资源的话,要在群公告里面找。

由于工作生活太忙了,对于大家的帮助时间已经没有之前那么充裕了。如果有志同道合的朋友,可以接受无偿的帮助别人,可以单独联系我,一块经营一下。
如果你有更好的经营方式也来练习我,加我QQ

在这里插入图片描述


发布了65 篇原创文章 · 获赞 103 · 访问量 37万+

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览