书接上文,如果我们没有找到原版的Decrypt.js该如何进行分析
前置要求
按照思路一,分析到游戏读写文件的函数被DecrypterPlayer.js
重写了
思路
之前有提到能用浏览器运行游戏会给我们逆向游戏带来巨大的便利性,那我们该如何使用呢?假设作者没有写注释,我们没有办法根据注释查到最原始版本的加密脚本,我们又该如何分析
首先我们用Edge打开游戏(准备篇有讲如何用Edge打开游戏)
F12打开开发者模式,在资源中找到DecrypterPlayer.js
,并选择优质打印(就是格式化代码)图中红圈
找到我们分析出来的进行解密的地方,设置断点
刷新页面,当断点被触发,选择单步步入
步入两次之后,我们发现代码执行到了YEP_KeyCore.js
也就是我们上一篇通过各种分析得到的秘钥文件
选择优质打印,可以看到,这与我们上篇的分析结果完全符合
使用Debug的方式也可以找到秘钥文件,但我们不关心解密函数具体如何实现,我们只需要在上层调用
我们使用编辑器打开DecrypterPlayer.js
文件,并进行格式化,添加一行控制行输出代码
取消断点,刷新页面,切换到控制台页,可以看到文件确实明文输出了
我们当然可以在解密函数这里插入代码,直接将解密文件输出,不过注意要依赖node.js
那可能会有人有疑问,加密脚本是如何运行的呢?加密脚本要把源文件加密,输出,并且删除源文件。这是因为RPG Maker MV使用了NW.js
搜索之
NW.js由node-webkit项目发展而来 , 通过使用页面相关技术开发桌面应用 , 同时能够使用DOM调用Node.js的所有模块 .
简而言之,如果你使用RPG Maker MV来启动游戏,那么游戏是可以调用读写文件库的
好的,那么至此,一次完整的RPG Maker MV游戏逆向过程就分享完毕了,感谢阅读
一些扩展
- 加密后的判断语句如何理解
if (xhr.status < 400) {
window[name] = JSON.parse(Decrypter.decryptText(xhr.response));
DataManager.onLoad(window[name]);
}
经过加密后
400 > h.status && (window[a] = JSON.parse(Decrypter.decryptText(h.response)), DataManager.onLoad(window[a]))
加密后的语句该如何理解
实际上这是运用了逻辑运算的短路特性
什么是短路呢?
举个例子:
条件1 && 条件2
如果条件1为假,那么条件2无论真假最终结果都是假,所以没有必要判断条件2
再看看上面的语句是不是豁然开朗
- 我们的思路只适用于RPG Maker MV吗
先来回忆一下我们做了什么
我们拿到一款数据文件加密过了的游戏,根据分析,找到了数据的解密函数,并据此写出新的函数,获得所有未加密的文件
那我们的思路是不是仅对RPG Maker MV游戏有效果呢?
诚然,本篇所讲到的手段都是针对RPG Maker MV游戏的,但是仅仅是手段,分析问题的思路是通用的
举个例子:
想必很多人修改游戏入门都是通过修改植物大战僵尸的阳光数,那我就通过这个来讲一讲
首先,给大家补充一点基础知识
我们使用的计算机架构被称为“冯诺依曼结构”
我们所有的程序包括操作系统都是加载在内存上运行的,这也是你为什么能通过CE搜索内存来修改游戏数据
直接改内存值就是操作内存地址对应的值,很好理解
那有些功能比如若种植不减阳光,植物无CD,种植物阳光不减反而增加其背后的原理又是什么?
再给各位开个小灶
计算机只认识字节码,而我们根本记不住字节码,就算有神人记住了所有的字节码,那编程效率也是极低的,那怎么办呢?我们发明了一种最基础的编程语言,叫做汇编。汇编和字节码有一一对应的关系,所以人们只需要记住有意义的单词,转换的工作就交给计算机(也就是编译),在此之上人们进行了更高层的抽象,诞生了高级编程语言,比如C, C++, JAVA等等
但程序最终还是要加载在内存中执行的,我们用CE浏览汇编代码区域的时候就是从内存中把字节码取出来并且翻译成人类可阅读的汇编代码,我们使用“是什么修改了这个内存地址”来找到修改数值的函数,这和我们之前的思路是一样的,要找到目标函数,再在目标函数上进行修改以实现相应的功能。
好了,那我们就知道了,所有的破解和修改器,实现思路无非就是找到目标函数(比如权限验证函数,加减钱函数),然后理清函数是如何实现的,我们在对函数进行修改以达到我们的功能。
- 再再讲一点,什么是HOOK
嗨呀,讲的停不下来了
修改游戏的时候我们我们经常能听到一个名词HOOK,翻译过来是钩子,那他究竟是什么意思呢?
结合上面讲到的,任何程序都是加载在内存里运行的,我们如果在原来的内存断上随意的修改,会造成内存的混乱,进而导致进程崩溃
那我们如何才能把自己的代码附加进去呢?
我们可以申请一片新的内存空间,将原来的函数某一处改为jmp(跳转命令,参数是目标内存地址),我们在新的内存空间写上原来被替换掉的内容再加上我们新的内容,最后在跳转回原来的地址,这样就实现了新增我们自己的处理逻辑
上面所说的这种操作手段就是HOOK了,大部分复杂的修改器功能都是通过HOOK来实现的
- 再再再来一波
那网络游戏为什么不能修改呢?按道理说也运行在本地的内存上
网络游戏对于重要的数据是在服务器上有存储的,你的每一个相关操作都会重新请求一次服务器来更新本地数据,所以你即使修改了本地数据,但是你下一次请求发生之后,服务器返回的数据会覆盖你修改的数据
但所有数据都在服务器处理无疑会给服务器带来巨大的压力,因此有一部分数据在本地修改了是可以生效的,但现在计算机性能越来越强,基本上所有会影响到游戏平衡的数据都在服务器处理了
针对网游,除了内存式修改器,还可以通过模拟发包的方式
什么是发包呢?
这里需要对网络有一定的概念
A和B两台计算机之间如果连上网线就可以互相通信了(假设没有防火墙,且有通信协议支持,实际想让两台计算机通信是很复杂的)
A给B发送:“hi” B收到A发送的消息
这个过程对A来说叫发包,B是收包
现代操作系统都会提供网络通信的函数,因此,我们直接在这些函数上附加断点,分析调试,就能找到游戏发送数据包的函数以及数据包内容加密的函数。之后再伪造请求,比如自动吃药功能,通过内存检测血量不足了,程序模拟一个吃药的包发送给服务器,我们就实现了自动吃药功能。(倘若服务器没有验证剩余药数量那就可以实现即使没药也能加血的功能,当然这种基本在code review阶段就被干死了)
好啦好啦,这下真就结束了,两天三篇文章,真是爆肝
所以呢,无论是写代码的,搞逆向的,其实思路都是一样的,只是一种是正着想,一种是反着想
但我们的基本功是不变的,语言和工具都是细枝末节(我不是说他们不重要),我们更应该深挖的是原理层面的,你如果熟悉网络,熟悉计算机组成,了解编程思路,那么无论是做编程还是做逆向都会事半功倍,DEBUG的时候无疑有更多的思路。
加油,朋友们,共勉