Project1

标题: 防止内存修改 [打印本页]

作者: fjm    时间: 2017-5-31 18:53
标题: 防止内存修改
有人建议搞个防内存修改的东西,防止有些人修改游戏,要是知道方法的告诉下,谢谢
作者: fux2    时间: 2017-5-31 20:38
建议先去学习如何修改,这样你就知道设计程序的时候如何保存变量才不容易被找到。
作者: 七重    时间: 2017-5-31 21:59
自己写一套数值换算的方法,这是出自某个小黄油的做法。大概可以防御到数值检索和直接改存档两种修改手段。

一般玩家直接改存档应该是主流吧,其实仅是改用变量存几个关键的属性,已经可以防御到相当一部分傻瓜修改。

会拆工程的人就别防了。
作者: guoxiaomi    时间: 2017-6-1 00:28
本帖最后由 guoxiaomi 于 2017-6-1 22:40 编辑

对于防止修改存档,可以手动对一些数据进行验证。比如要检查玩家是否修改了物品等,就验证 $game_party 这个量,或者细致一点,验证 $game_party.items,不过默认 @items 是不可访问的,要稍微修改一下脚本。

这样做:

1. 准备一个并没有什么用的密钥字符串(盐),比如说 r = '1234567890'

2. 存档前,把校验码写入变量 10
  1. v = $game_variables[10]
  2. r = '1234567890'
  3. v = Zlib::crc32(r + $game_party.items.inspect)
复制代码

3. 读档后,进行验证。
  1. r = '1234567890'
  2. v = Zlib::crc32(r + $game_party.items.inspect)
  3. if v != $game_variables[10]
  4.   p "存档验证失败,即将退出游戏!"
  5.   exit
  6. end
复制代码


多个数据各自调用自己的 Object#inspect 方法,然后把字符串拼接到一起验证也是可以的。crc32 只是很简单的验证手段,不过足以应付大多数不懂程序的人了,如果觉得不放心可以用 SHA-1 等更优秀的哈希算法进行验证。但是计算速度就不保证了= =

其实直接调用 Object#hash 方法也能得到对应的验证,而且在我的电脑上它的速度比生成字符串再做 crc32 快了大概3000倍,我把这个也写进去供参考~
但是直接调用 $game_party.hash 是不推荐的,如果实在要考虑性能,那最好带上盐多做几层,就像注释里面的一样。
原因是 crc32 算法是个公开的算法,而且所有语言、版本都得到一致的结果,而 Object#hash 可能不同的 ruby 版本得到不一致的结果。



刚刚不放心测试了一下,果然是有问题的,只有String (字符串) 的 hash 值是固定不变的,其他的对象的 hash 值每次游戏会发生变化。而且每次 inspect 方法得到的数据也不完全一样,所以注释的部分还是别用了(我已经删除)。Hash,Array,String,Fixnum 和 True/False/Nil 的 inspect 方法会返回相同的字符串,但是其他的 class XXX 的实例就不保证了,所我也把原来 $game_party.inspect 改为了 其 @items 对象的 inspect 方法。

如果要防止修改内存,也可以考虑用校验的方式,比如 @items 的每一次修改都是在 Game_Party#gain_item 方法里,在这个方法的结尾计算@items 的 hash 的值保存在一个全局变量里,然后在你需要的时候检验 hash 值和对应的对象转化的字符串是否相符合。
还是拿 @items 举例~
RUBY 代码复制
  1. class Game_Party
  2.   alias _check_item_initialize initialize
  3.   def initialize
  4.     _check_item_initialize
  5.     @salt = rand.inspect
  6.     @item_hash = item_hash
  7.   end
  8.  
  9.   alias _check_item_gain_item gain_item
  10.   def gain_item(*args)
  11.     cheat = check_item
  12.     _check_item_gain_item(*args)
  13.     @item_hash = item_hash if !cheat
  14.   end
  15.  
  16.   def item_hash   
  17.     sprintf("%08x%08x", (@salt + @items.inspect).hash, (@items.inspect + 'rpg.blue').hash)
  18.   end
  19.   private :item_hash
  20.  
  21.   def check_item
  22.     # 无作弊返回 false,否则返回 true
  23.     @item_hash != item_hash
  24.   end
  25.  
  26. end

(RMXP)这段代码插入main前,可以在每次调用 gain_item 方法后判断是否为作弊,如果没有作弊则重置 hash。如果有作弊,@item_hash 的值不会更新,从而保证 check_item 一直返回 false,除非 @items 回归到作弊前的情况,或者玩家同时修改了正确的 @item_hash。

为了避免玩家猜出来正确的 @item_hash,盐字符串 @salt 的生成是随机的~所以每一个新的游戏或者存档,都有不同的 @salt,从而也不能通过其他人的存档得到对应的 @item_hash。从而获取盐字符串的方法只能穷举或者解包游戏~相比下来,解包说不定更快捷,那样就没有办法了。建议修改一下 item_hash 的具体计算方法,弄复杂一点点,比如像上面这样返回一个表示64位整数的字符串。

然后在你需要检查 items 作弊情况的时候判断 $game_party.check_item 即可。比如每次存、读档,进入菜单页面时。

如果游戏里存在不使用 gain_item 修改 @items 的情形,会被判定为作弊(当然)。如果无法避免这种情况,在每次修改了 $game_party.items 的后面都对 @item_hash 重新赋值。

我的想法就是这样了……
作者: fux2    时间: 2017-6-1 01:07
其实有一个简单的方法,你的重要变量,写成一个类,每次赋值,都新建一个类,类格式如下。
  1. class Var
  2. attr_accessor:value
  3. end
复制代码

这样每次赋值后实际值在内存里的地址都会变化,别说防新手,只要不深入研究基本就无法修改(超多级指针
作者: fjm    时间: 2017-6-1 22:20
guoxiaomi 发表于 2017-6-1 00:28
对于防止修改存档,可以手动对一些数据进行验证。比如要检查玩家是否修改了物品等,就验证 $game_party 这 ...

多谢,试了下会报错

QQ截图7899.jpg (10.66 KB, 下载次数: 28)

QQ截图7899.jpg

作者: sutao80216    时间: 2017-6-3 20:11
如果不能保证游戏不被解包,弄这些还是做无用功而已。




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1