在阅读这篇文章之前,我在处理mono加密问题时,也是参考了雨凇的文章,所以建议先看一下雨凇写的关于加密Dll的文章:

1.Unity3D研究院之Android加密DLL与破解DLL .SO

2.Unity3D研究院之Android二次加密.so二次加密DLL

假装读者已经看过上面的两篇文章了,下面我会记录一下我做的整个加密流程。

一.选取加密Dll的算法

我们主要目的是对程序集:Assembly-CSharp.dll 进行加密,然后修改mono源码,在mono加载Dll的时候进行解密。显然我们需要一种可逆、对称的加密算法,其实这类算法很多,如DES、TEA、XXTEA等,一般这类对称秘钥算法的安全性都是基于秘钥的(Key),所以如何在mono解密是保护自己的秘钥就十分重要了。我目前使用的是XXTEA,实现的话不清楚,但是github上有开源实现,所以直接拿来用了:xxtea-c

1.先用Unity导出一个android Google工程,在工程路径 {$Project}\assets\bin\Data\Managed\Assembly-CSharp.dll ,这个文件就是需要我们替换的程序集啦

2.编写加密Dll工具,大家可以把上面开源xxtea项目中的源码:xxtea.h、xxtea.c 和下面的encryptDll.c代码放在同一目录,用MinGW下的gcc编译就可以了:gcc xxtea.c encryptDll.c –o EncryptDll

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "xxtea.h"#define SIZE 1024*1024*10
void main() //命令行参数
{FILE *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);//加密DLLsize_t len;char* key = "123456";char *encrypt_data = xxtea_encrypt(buffer,total_len, key, &len);printf("Encrypt Dll Successfully and len : %d\n" , len);//写DllFILE* 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);
}

在用生成的EncryptDll.exe Dll_Path 就可以直接加密改Dll了

二.mono中解密

我们需要修改{$mono_root}/mono/metadata/image.c ,它有一个mono_image_open_from_data_with_name 函数,该方法是加载Dll的入口函数,在这里实现解密。

MonoImage *
mono_image_open_from_data_with_name (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, const char *name)
{if(strstr(name ,"Assembly-CSharp.dll")){g_message("mono: === Start Decrypt Dll ==========\n");char key = "123456";size_t len;char* decryptData = decrypt(data , key);//换成对应的解密函数int i = 0;for ( i = 0; i < len; ++i){data[i] = decryptData[i];}g_free(decryptData);g_message("mono: === End Decrypt Dll ========== \n");}........return register_image (image);
}

到此解密和加密过程就结束了,

1.我们可以重新编译修改后的mono,然后用{$mono_root}/embedruntimes/android/*下对应平台libmono.so覆盖掉{$Unity_Root}/Editor/Data/PlaybackEngines/androidplayer/(development | release)/libs/* , 然后就可以重新导出android工程了。

2.导出android工程后,用生面生成的EncryptDll.exe 加密Assembly-CSharp.dll

3.用eclipse 或者 android studio 导出apk,运行 success !

三.mono种key保护

如果顺利完成(二)中的过程,那么就可以防住很大一部分小白破解者了,但是就像雨凇文章中说的,只要是稍微厉害点的玩家还是可以破解的,用IDA神器,很快就能反编译libmono.so 并找到key,然后解密Dll,然后就又可以堂而皇之地修改Dll啦……sadly,那么我们如果防止这种情况呢,下面有几种方案可供选择,但是在阅读后面的内容时强烈建议先了解一下ELF文件格式,推荐两个链接:http://www.cnblogs.com/xmphoenix/archive/2011/10/23/2221879.html ,http://blog.chinaunix.net/uid-21273878-id-1828736.html , 了解一些ELF文件头信息,会很有帮助的,因为肯定会踩一些坑的……

1.加密指定的section

这个方式雨凇已经在文章中给了足够详细的说明和源码,这里就不瞎补充了,但是,这个方案有个致命的缺陷,就是无法兼容x86架构的cpu,骤然一听不兼容x86似乎是一个非常严重的问题,其实有所了解x86的就会明白其实并没有什么大问题,因为x86的机器真的很少,除了华硕和联想有几款小众机型外,其他品牌几乎没有x86的机型,甚至在weTest上也找不到x86的机型 ,这估计也是雨凇没有测出来的原因……,当然在我初步遇到这个问题也是用了一两天时间去尝试修改代码使它兼容x86 cpu,下面是我做的尝试方案:

a.修改保存信息ELF位置

这个方案的代码有个前提是,ehdr.e_entry , 和 ehdr.e_shoff 或者其他ELF头其他位置可读写,并不会影响android对动态库so的加载执行,然而在x86架构下,它不容许修改入口地址,即ehdr.e_entry位置,如so入口地址ehdr.e_entry ,否则就拒绝加载,直接崩掉……于是,我尝试修改保存信息位置

1).我把源码中base 和 length信息放在了ehdr.ident后8个字节中,测试还是会拒绝加载,然后使用 ehdr.e_shoff = base;

2). e_shnum 和 e_shstrndx 保存lenght(因为length是四个字节,而e_shnum 和 e_shstrndx均是两个字节,所以需要同时占用e_shnum 和 e_shstrndx),测试时发现,虽然可以加载了,但是算出的section地址不对,造成加密的sectiong函数寻址错误,还是崩掉,最后证明这个修复方案行不通

b.直接写死偏移(base)和length信息

既然无法正常保存偏移地址,那么我就尝试手动写死对应的参数,然后测试,结果发现还是会崩掉,和 a.2 中的情况一致,于是判断这个方案行不通

c.解密动态算出偏移和length信息

根据加密过程动态算出找到加密的section地址,然后解密(可惜当时的代码已经删除了),最终的测试发现,在匹配字符串表时无法找到指定的节信息,很有可能x86在加载时改变了ELF位置信息,所以最终也是失败啦

至此我就放弃了修复的想法,寻找其他方案,当然如果公司可以容忍不兼容那少数的几台x86机器就可以采用这个方案,我咨询过几个朋友,他们采用这个方案的项目已经上线了……

2.对指定的函数进行加密

这个其实我也并没有看明白,但是我尝试可几次都没成功,这里附上链接,有心的哥们可以参考一下:http://www.cnblogs.com/lanrenxinxin/p/4962470.html

3.折中方案

我们如果无法容忍不兼容x86,有无法搞定2中方案,那只能自己想办法了。直接写明文key在mono中肯定不行,那么是不是可以把key变通一下存放在ehdr.e_shoff 或者其他位置呢,这样的话除非破解者找到对应的赋值函数,否则也不大容易获得key,具体思路:

1)假设key = fun(c);

2)把c存放到ehdr.e_shoff;

3)在mono加载之前找到 ehdr.e_shoff,并计算出根据fun(c)计算出key

4)缓存key,就可以继续解密Dll了

那么这个方案是否完备,答案肯定是no,以上没有完备的方案,只要破解者找到你的解密处的函数就可以反向获得key,重新破解Dll,但是相对写明文来说可能是一个折中的方案,下面贴出参考代码:

加密libmono.so的代码

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>/* 32-bit ELF base types. */
typedef unsigned int Elf32_Addr;
typedef unsigned short Elf32_Half;
typedef unsigned int Elf32_Off;
typedef signed int Elf32_Sword;
typedef unsigned int Elf32_Word;#define EI_NIDENT 16/** ELF header.*/typedef struct {unsigned char  e_ident[EI_NIDENT];  /* File identification. */Elf32_Half  e_type;    /* File type. */Elf32_Half  e_machine;  /* Machine architecture. */Elf32_Word  e_version;  /* ELF format version. */Elf32_Addr  e_entry;  /* Entry point. 4 byte int  */Elf32_Off  e_phoff;  /* Program header file offset. */Elf32_Off  e_shoff;  /* Section header file offset. 4 byte int */Elf32_Word  e_flags;  /* Architecture-specific flags. */Elf32_Half  e_ehsize;  /* Size of ELF header in bytes. */Elf32_Half  e_phentsize;  /* Size of program header entry. */Elf32_Half  e_phnum;  /* Number of program header entries. */Elf32_Half  e_shentsize;  /* Size of section header entry. */Elf32_Half  e_shnum;  /* Number of section header entries. */Elf32_Half  e_shstrndx;  /* Section name strings section. */
} Elf32_Ehdr;/** Section header.*/typedef struct {Elf32_Word  sh_name;  /* Section name (index into thesection header string table). */Elf32_Word  sh_type;  /* Section type. */Elf32_Word  sh_flags; /* Section flags. */Elf32_Addr  sh_addr;  /* Address in memory image. */Elf32_Off sh_offset;  /* Offset in file. */Elf32_Word  sh_size;  /* Size in bytes. */Elf32_Word  sh_link;  /* Index of a related section. */Elf32_Word  sh_info;  /* Depends on section type. */Elf32_Word  sh_addralign; /* Alignment in bytes. */Elf32_Word  sh_entsize; /* Size of each entry in section. */
} Elf32_Shdr;int main(int argc, char** argv){Elf32_Ehdr ehdr;Elf32_Ehdr _ehdr;unsigned int key = xxxx;//决定key的因子int i;int fd;if(argc < 2){puts("Input .so file");return -1;}fd = open(argv[1], O_RDWR);if(fd < 0){printf("open %s failed\n", argv[1]);goto _error;}//读取ELF文件头(mono.so 52个字节)if(read(fd, &ehdr, sizeof(Elf32_Ehdr)) != sizeof(Elf32_Ehdr)){puts("Read ELF header error");goto _error;}ehdr.e_shoff = key;//覆盖新ELF文件头lseek(fd, 0, SEEK_SET);if(write(fd, &ehdr, sizeof(Elf32_Ehdr)) != sizeof(Elf32_Ehdr)){puts("Write ELFhead to .so failed");goto _error;}lseek(fd, 0, SEEK_SET);read(fd, &_ehdr, sizeof(Elf32_Ehdr));printf("Write Key : %d \n", _ehdr.e_shoff);puts("Completed");
_error:close(fd);return 0;
}

mono中解密代码:

//SO---------------加密----------------------#include <sys/types.h>
#include <elf.h>
#include <sys/mman.h>unsigned int encrypt_key = 456987;void mono_trace_free_tree() __attribute__((constructor));
unsigned long getLibAddr();int getKey();int getKey(){return luta_encrypt_key;
}void mono_trace_free_tree(){g_message("mono:============= print Elf Start =============\n");unsigned long base;Elf32_Ehdr *ehdr;base = getLibAddr(); ehdr = (Elf32_Ehdr *)base;unsigned int temp_key = ehdr->e_shoff;encrypt_key = fun(temp_key);g_message("mono: Find luta_encrypt_key = %d\n",encrypt_key);g_message("mono: ============= print Elf End =============\n");}unsigned long getLibAddr(){unsigned long ret = 0;char name[] = "libmono.so";char buf[4096], *temp;int pid;FILE *fp;pid = getpid();sprintf(buf, "/proc/%d/maps", pid);fp = fopen(buf, "r");if(fp == NULL){g_message("mono: open failed");goto _error;}while(fgets(buf, sizeof(buf), fp)){if(strstr(buf, name)){temp = strtok(buf, "-");ret = strtoul(temp, NULL, 16);break;}}
_error:fclose(fp);return ret;
}
//SO---------------加密----------------------

至此方案3的加密方案接结束,如果不多ELF文件有一定了解,恐怕很难完成这个内容……

4.其他方案

1)其实一些做加密的服务很多都对加密so有支持,然而都是付费的,sadly……,如果公司有钱可以考虑类似“爱加密”等加密服务

2)我们可以把获得key和加密函数抽离出来,单独做成decrypt.so,对其进行加密,然后在libmono.so加载前在android层解密并加载decrypt.so,还可以对android层代码混淆等,相当于多做几层防护,加大破解难度。

最后

加密Dll这件事其实还是无法做到绝对完备,只能加大破解难度,如果有问题请留言

Unity防破解 —— 加密Dll与Key保护相关推荐

  1. IOS防破解 加密

    IOS内购防破解 我们使用的AnySDK的支付来做的,比较简单有效 http://docs.anysdk.com/integration/server/payment-notice/ CPP 接口说明 ...

  2. 简单Unity 3D游戏加密dll文件提取

    大多数游戏都是使用Unity 3D做的,我们逆向分析主要分析\assets\bin\Data\Managed中的Assembly-CSharp.dll文件,当我们用.NET Reflector 或者d ...

  3. 【Unity】防反编译之windows平台加密dll

    功能取决于需求,在实现这功能之前,却有一个小小的插曲,有同学认为,并不需要去实现游戏加密,再怎样也会被破解,何必浪费精力.虽然这样说,但是我们所做的加密至少也会增加一点破解成本,不会让我们辛苦写的代码 ...

  4. 网易云加密--Android安卓移动应用程序加密加壳保护方案防破解防逆向分析防游戏外挂

    网易云加密--Android安卓移动应用程序加密加壳保护方案防破解防逆向分析防游戏外挂 一.服务简介 网易云加密为网易公司旗下产品,致力于为移动应用程序提供专业保护方案,让加固变得更简单,一键搞定.目 ...

  5. 如何保护.net中的dll文件(防破解、反编译)

    .net是一种建立在虚拟机上执行的语言,它直接生成 MSIL 的中间语言,再由.net编译器 JIT 解释映象为本机代码并交付CPU执行.中间语言很容易被反编译,所以研究下如何有效的保护dll文件. ...

  6. 文华财经指标公式大全,通达信指标加密破解DLL加密防破解技术方法

    STICKLINE(做多 AND C<O,C,O,0.3,0),COLORFF00FF;  STICKLINE(做多 AND C>=O,C,O,3,1),COLOR0000FF;  {ST ...

  7. unity防反编译 windows平台加密dll

    原文链接:http://gad.qq.com/college/articledetail/7194480 本文首发腾讯GAD开发者平台,未经允许,不得转载 功能取决于需求,在实现这功能之前,却有一个小 ...

  8. Unity3D之Android加密DLL与破解DLL

    转自:http://blog.csdn.net/yupu56/article/details/53216705 1.加密方案 Unity 3D项目游戏逻辑采用C#脚本,我们知道C#编译生成的DLL或E ...

  9. 软件安全防破解之字符串加密,提高破解难度让你的软件不再轻易被山寨

    原文:http://bbs.84zcb.com/showtopic-3430.aspx 大家知道提示框和字符串是新手破解的关系,如果我们能将敏感的字符串进行加密传输,那么就可以杜绝90%以上的破解新手 ...

最新文章

  1. matlab sisotool工具箱实例,MATLAB工具箱Sisotool工具箱在控制系统补偿器中的应用
  2. H3C secpath nat的tcp会话数的限制设置
  3. Amazon SNS和Amazon SQS有什么区别?
  4. 地图漫游功能的具体体现_骏谷科技|数据中心三维可视化管理系统功能亮点
  5. C# WinForm中 获得当前鼠标所在控件 或 将窗体中鼠标所在控件名显示在窗体标题上...
  6. 汉字转拼音php代码函数,php中将汉字转换成拼音的函数代码
  7. linux 查看磁盘空间_Linux下删点日志也能搞死人
  8. Effective Java之在公有类中使用访问方法而非公有域(十四)
  9. Android ContentProvider
  10. 明天一定要记得放一个硬币在口袋
  11. vyos User Guide
  12. 测试丢包_入行游戏测试之弱网测试工具
  13. 同步工具之CountDownLatch闭锁
  14. LeetCode-94. 二叉树的中序遍历
  15. quadprog函数的介绍和应用,二次规划函数
  16. python xmlrpc_Python xmlrpc用法
  17. 2.5 不匹配数据划分的偏差和方差
  18. mysql中不重复_mysql中distinct的用法(不重复记录)
  19. 计算机打印中 进纸盘2,纸盘纸张设置
  20. 这可能是史上最全的常用学术网站

热门文章

  1. Java作业:异常处理实验
  2. 互联网(软件)公司项目管理软件调研报告
  3. 睿企管家成功入驻航天云网 助力160万云网企业用户数字化转型
  4. ChatGPT国内怎么用?官网实在太麻烦了,ChatGPT可以直接国内使用吗?
  5. 今天和大家分享期待已久的剪映专业版Windows
  6. 城市停车难,如何破局?
  7. 关于Win10。。。我是来吃螃蟹的。。。
  8. 可以过滤网页中广告的插件Adblock Plus
  9. CSS高度自适应问题
  10. 阿里App支付服务端接口开发(含后台异步回调,退款)