采用unity开发的游戏(pc客户端)并且未加密的话,恢复成 ...
反编译unity文件的工具我用过,可以将资源文件导出;借助reflector等工具反编译mono源码也很容易。但是恢复到工程状态还需要怎么做? 最近我做的Unity版《马戏团》得到大家的好评,于是做了一个速通竞速版《马戏团》,让更多人体验挑战速通的乐趣。而为了防伪速通成绩截图,我做了一个MD5校验码的功能。本来是一个很小的功能,但又引申出关于代码加密的问题。所以我把这些天的研究成果总结成文章,以飨读者。
一、引子——游戏速通成绩验证
在制作速通版马戏团时,因为玩家的成绩最终是以界面方式展现的,大家在交流成绩时截图即可,像这样:
可以看到,界面下方是一个校验码。它是做什么用的呢?
设计校验码,是因为截图成绩太容易PS篡改,如果没有某种简单的防伪机制,伪造成绩就很影响速通交流的兴趣。
这个码实际就是个MD5码,用C#生成非常简单,只需要用内置的MD5 API即可:
using System.Security.Cryptography;// 加密算法的命名空间
// 函数输入:任意字符串
// 函数输出:以16进制表示的MD5码字符串,例如 FF-EE-AA-BB-80-81-82... 这样的
string CalcMD5(string source)
{
MD5 md5 = MD5.Create();
byte[] bytes_src = Encoding.UTF8.GetBytes(source);
byte[] bytes_md5 = md5.ComputeHash(bytes_src);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes_md5.Length; i++)
{
sb.Append(bytes_md5.ToString(&#34;X2&#34;));
if (i != bytes_md5.Length - 1)
{
sb.Append(&#34;-&#34;);
}
}
return sb.ToString();
}
那我用哪些东西作为原始数据进行加密呢?我把用户昵称、开始时间、结束时间、总时间,外加一些特殊数值作为加密的原始字符串,就可以获得一个对每个人、每次挑战都不一样的MD5数值。
在验证时,我写了一个小工具,只要输入以上信息,就能算出MD5值,只要MD5值相同,就是真实的成绩了。
二、破解方法——C#反编译
仔细想想,其实只要知道我是把哪些原始数据、以什么格式、什么顺序拼在一起的,就能计算出同样的MD5码。具体比如:
原始字符串 = 开始时间 日/月/年 + 结束时间 年/月/日 + 总时间 时:分:秒.毫秒 + 昵称
如果我像上面这样,直接告诉你加密的格式,你只要把任意信息按上面的格式拼成字符串,再送到MD5函数中计算,自然就能得到和我一样的MD5码了。
所以,唯一要保密的就是原始数据的格式,一旦被别人知道了格式和顺序,就没有任何秘密可言了。
本来,作为一个小小的防作弊手段,能防止玩家PS截图已经足够了。但是如果较真一些,高手会尝试采用C#反编译的方法获取源码。
在Unity2020之前的版本中,默认脚本后端采用Mono编译器,将C#代码编译为CIL代码。编译好的CIL看起来是一个dll文件,而把CIL的dll还原为C#有很多办法。
比如开源小工具dnSpy,github地址:https://github.com/dnSpy/dnSpy/releases
只要把脚本的dll拖进去,就可以看到反编译结果。
例如马戏团Unity工程,开发时的dll位于:工程目录/Library/ScriptAssemblies/Assembly-CSharp.dll
将它拖到dnSpy窗口里:
把代码的底裤扒光
可以看到,不管什么MonoBehaviour、Start、Update、Awake……通通给你把底裤扒掉。
同理,小小的加密函数,根本逃不出dnSpy的魔爪……反编译之后,一眼就看到格式和顺序了,毫无秘密可言。
针对初级的黑客技术手段,咱们也要考虑反击手段,有两种:
[*]对C#代码进行混淆。
[*]使用IL2CPP,将CIL代码转为C++再编译。
其中,第二种IL2CPP为推荐做法。
三、代码混淆与IL2CPP浅析
代码混淆的百度解释:
代码混淆(Obfuscated code)亦称花指令,是将计算机程序的代码,转换成一种功能上等价,但是难于阅读和理解的形式的行为。如果现在大家搜索“Unity代码混淆”,会看到很多5年前的老回答、老博客。因为在IL2CPP普及以后,其实Unity做代码混淆的意义已经不大了。
在IL2CPP成熟之前,有一些常用的Unity混淆插件,例如开源小插件Unity-Obfuscator。https://github.com/DrFlower/Unity-Obfuscator
它的工作原理为:其实C#代码混淆在其它领域需求更多,有一个专门的库叫做Mono.Cecil,用于修改和混淆CIL格式的dll文件。而Unity-Obfuscator就是借用了Cecil库进行代码混淆的。
经我测试,Unity-Obfuscator插件最多只能用在Unity 2017版本中,到2018版本后就需要升级了。
虽然在Unity混淆方面用处不大,但从纯技术角度看:如果能够方便地读取和修改CIL代码,就能实现更多黑客技术,例如代码注入、插入检测指令等功能。Cecil自有它的用武之地。我也研究了两天Cecil,就当是给自己科普了:)
接下来是IL2CPP。
Project Settings -&gt; Player -&gt; Other Settings -&gt; Configuration,在这里修改脚本引擎
解释一下IL2CPP——IL就是CIL(通用中间语言),2就是to,CPP就是C++。
IL2CPP,就是把初步编译的CIL代码,转成C++源码,再用C++编译器转成dll。虽然看起来也是dll,但标准已经是天差地别。
经IL2CPP转换后的代码运行效率更高,而且……加密效果超级好,至少比Unity-Obfuscator好很多。具体说起来我也不是专家,看看其它人的研究成果:
渔夫佬:关于unity的il2cpp打包后反编译探索的个人学习成果四、编译后的代码所在的路径
我们在使用Unity时,其实所有的脚本都要编译为DLL才能运行。这里简单介绍一下各个DLL的存储路径。
首先,如果用Mono脚本后端,咱们的所有脚本都统一编译到Assembly-CSharp.dll之中。
在编辑器做开发时,它位于:
工程目录\Library\ScriptAssemblies\Assembly-CSharp.dll (含pdb)
打成windows运行的发布包后,它位于:
发布包\Circus_Data\Managed\Assembly-CSharp.dll(不含pdb)
如果要动态修改dll,还需要找到pdb。pdb位于:
Temp\ManagedSymbols\Assembly-CSharp.pdb
而如果用IL2CPP打包后,很多相关的代码会统一打包到GameAssembly.dll中,它就位于打包目录的第一级目录下。如图:
这里容易搞错的是,有一个目录叫XXX_BackUpThisFolder_ButDontShipItWithYourGame,意思就是不要把这个目录发布出去【捂脸】,因为里面存放的是很多CIL文件,如果让人拿到,就没有加密可言了。话说Unity为什么把这么重要的东西放在这里【捂脸】
总结——矛与盾
加密与破解是一对永恒的矛盾。
不存在无坚不摧的矛,也不存在坚不可摧的盾。它们永远在激烈地交锋,而交锋的焦点只有一个——成本与收益。
盾的使命是在尽量少增加加密成本的前提下,不断增加矛的成本;
而矛的使命是用更低的成本去攻破盾,从而得到正向的收益。
二者此消彼长,循环往复,推动网络与软件安全的进步。
例如,Unity-Obfuscator的混淆效果不足,更容易被攻破;而IL2CPP相对更难被攻破。但难攻破不代表攻不破。
在互联网时代,破解技术也在飞速普及,随便一搜,就有一个精彩的破解案例:U3D游戏包il2cpp逆向解包,apk加密资源解密_爱码使的博客-CSDN博客_il2cppinspector
总之,软件安全是一个庞大的话题,远非我等游戏开发者所能掌握。
我们能做的,就是尽量提高自己的安全意识,了解一些基本的知识。最起码的,做到不要给安全专家拖后腿 【笑】
<hr/>欢迎加入游戏开发群欢乐搅基:1082025059
对学习游戏开发、游戏制作感兴趣的童鞋,欢迎访问咱们的主页:皮皮关 如果游戏是用mono打包的话,用dnSpy/ILSpy反编译脚本程序集就能获取代码
反编译出的代码还是存在挺多Bug的,需要手动修复下
如果是il2cpp打包的话,就需要通过Il2CppDumper+反汇编获得代码信息了
用uTiny Ripper提取资源文件,并且可以初步还原成Unity Project结构
uTiny Ripper目前也存在许多待添加的Feature,部分Component无法被提取,需要手动在Unity里添加
想要初步获得Component信息的话,可以在游戏里打Log,也可以用AssetsBundleExtractor直接看Asset
另外,以上这些工具都在GitHub有Repo,大部分是开源的 现在已经不可能了,unity5的IL2CPP已经支持各个平台了,包括Android。导出IL2CPP,首先你的C#代码会被翻译成一堆C++,这些C++可读性比起原来写的C#非常差,然后C++再编译成各个平台的机器码。C++目前地球上还没有人可以反编译。如果反汇编的话,而且C++在各个平台的反汇编的代码都不一样的。就算你能反编译回原来的C++代码,那你还要把那一堆C++代码还原成C#。还原成C#,你要还原它的场景文件,还要还原场景文件上各个对象的拖动关系。所以对于现在的unity5来说几乎不可能。 我想,最大的问题可能是你拿不到制作这些资源的配套编辑器 恢复工程应该不太可能吧,prefab之间的关系和gameobject上绑定的脚本这种比较困难吧。资源和代码是可以弄出来,但整个工程之间的组合,有点困难吧
页:
[1]