设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 4543|回复: 5
打印 上一主题 下一主题

[讨论] 【RMXP】游戏防作弊集锦

[复制链接]

Lv1.梦旅人

梦石
0
星屑
60
在线时间
705 小时
注册时间
2007-12-23
帖子
874
跳转到指定楼层
1
发表于 2013-8-13 11:43:32 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
今天去研究了一下RMXP的反作弊

除了破解RGSSAD以外,主要还有这两种作弊方式:
1.修改运行时内存数据(比如金山游侠、Cheat Engine等)
2.修改存档(网上应该已经有这类存档修改器了吧)

对于修改内存,RMXP也有保护机制,不过只是将数字乘以2再加1而已,地球人都知道。对于修改存档,则并没有什么好的防范手段。
针对这两种破解方式,在下在网上搜索了一早上,终于找到了几种反作弊方法。
1.内存防改(by @土撥撥 )
http://rpg.blue/thread-218920-1-1.html
6R上已经有现成的了,但也只能防止金钱和变量被修改,如果战斗的时候改改血量之类的就防不住了。希望大神可以研究、改进。
2.存档防改(by @FantasyDR
这在6R上很难找到,而且已经可以暴力破解了。不过也只是暴力破解,毕竟还是需要一定时间的。可以防范大多数的傻瓜式作弊玩家。
它已经被应用于魔塔样板,但原版代码却很难找到,今天终于在某个角落里发现了它。
RUBY 代码复制
  1. #==============================================================================
  2. # ☆★☆ Custom Save☆★☆
  3. #------------------------------------------------------------------------------
  4. # - FantasyDR
  5. #------------------------------------------------------------------------------
  6. # MSN: [email][email protected][/email]
  7. #------------------------------------------------------------------------------
  8. # - 2006.7.18
  9. #------------------------------------------------------------------------------
  10. # 自定义存档数据排列顺序并校验存档
  11. #==============================================================================
  12.  
  13. #==============================================================================
  14. # ■ Scene_File
  15. #------------------------------------------------------------------------------
  16. #  存档画面及读档画面的超级类。
  17. #==============================================================================
  18.  
  19. class Scene_File
  20.   #--------------------------------------------------------------------------
  21.   # ● 存档密钥,请定义成自己的密钥
  22.   #   密钥不同的存档将无法被读取
  23.   #--------------------------------------------------------------------------
  24.   SAVE_KEY = 1234567
  25.   #--------------------------------------------------------------------------
  26.   # ● 存档号码最大值
  27.   #   默认0~3共4个存档,如果需要大于4个存档,需要改动窗口类。
  28.   #   这里不提供大于4个存档的窗口排布,如果有需要请自己定义。
  29.   #--------------------------------------------------------------------------
  30.   SAVE_MAX = 3
  31.   #--------------------------------------------------------------------------
  32.   # ● 初始化对像
  33.   #   help_text : 帮助窗口显示的字符串
  34.   #--------------------------------------------------------------------------
  35.   def initialize(help_text,type = "档案",save_max = SAVE_MAX)
  36.     @help_text = help_text
  37.     @save_max = save_max
  38.     @type = type
  39.   end
  40.   #--------------------------------------------------------------------------
  41.   # ● 主处理
  42.   #--------------------------------------------------------------------------
  43.   def main
  44.  
  45.     # 生成存档选择
  46.     save_list = []
  47.     for i in 1..@save_max+1
  48.       save_list.push(@type+i.to_s)
  49.     end   
  50.     # 生成帮助窗口
  51.     @help_window = Window_Help.new
  52.     @help_window.set_text(@help_text)  
  53.     # 生成存档文件查
  54.     @savefile_windows = []
  55.     for i in 0..@save_max
  56.       @savefile_windows.push(Window_SaveFile.new(i, make_filename(i)))
  57.     end
  58.     # 选择最后操作的文件
  59.     @file_index = $game_temp.last_file_index
  60.     @savefile_windows[@file_index].selected = true
  61.     # 执行过渡
  62.     Graphics.transition
  63.     # 主循环
  64.     loop do
  65.       # 刷新游戏画面
  66.       Graphics.update
  67.       # 刷新输入信息
  68.       Input.update
  69.       # 刷新画面
  70.       update
  71.       # 如果画面被切换的话就中断循环
  72.       if $scene != self
  73.         break
  74.       end
  75.     end
  76.     # 准备过渡
  77.     Graphics.freeze
  78.     # 释放窗口
  79.     @help_window.dispose
  80.     for i in @savefile_windows
  81.       i.dispose
  82.     end
  83.   end
  84.   #--------------------------------------------------------------------------
  85.   # ● 刷新画面
  86.   #--------------------------------------------------------------------------
  87.   def update
  88.     # 刷新窗口
  89.     @help_window.update
  90.     for i in @savefile_windows
  91.       i.update
  92.     end
  93.     # 按下 C 键的情况下
  94.     if Input.trigger?(Input::C)
  95.       # 调用过程 on_decision (定义继承目标)
  96.       on_decision(make_filename(@file_index))
  97.       $game_temp.last_file_index = @file_index
  98.       return
  99.     end
  100.     # 按下 B 键的情况下
  101.     if Input.trigger?(Input::B)
  102.       # 调用过程 on_cancel (定义继承目标)
  103.       on_cancel
  104.       return
  105.     end
  106.     # 按下方向键下的情况下
  107.     if Input.repeat?(Input::DOWN)
  108.       # 方向键下的按下状态不是重复的情况下、
  109.       # 并且光标的位置在 3 以前的情况下
  110.       if Input.trigger?(Input::DOWN) or @file_index < 3
  111.         # 演奏光标 SE
  112.         $game_system.se_play($data_system.cursor_se)
  113.         # 光标向下移动
  114.         @savefile_windows[@file_index].selected = false
  115.         @file_index = (@file_index + 1) % 4
  116.         @savefile_windows[@file_index].selected = true
  117.         return
  118.       end
  119.     end
  120.     # 按下方向键上的情况下
  121.     if Input.repeat?(Input::UP)
  122.       # 方向键上的按下状态不是重复的情况下、
  123.       # 并且光标的位置在 0 以后的情况下
  124.       if Input.trigger?(Input::UP) or @file_index > 0
  125.         # 演奏光标 SE
  126.         $game_system.se_play($data_system.cursor_se)
  127.         # 光标向上移动
  128.         @savefile_windows[@file_index].selected = false
  129.         @file_index = (@file_index + 3) % 4
  130.         @savefile_windows[@file_index].selected = true
  131.         return
  132.       end
  133.     end
  134.   end
  135.   #--------------------------------------------------------------------------
  136.   # ● 生成文件名
  137.   #   file_index : 文件名的索引 (0~3)
  138.   #--------------------------------------------------------------------------
  139.   def make_filename(file_index)
  140.     return "Save#{file_index + 1}.rxdata"
  141.   end
  142. end
  143.  
  144. #==============================================================================
  145. # ■ Scene_Load
  146. #------------------------------------------------------------------------------
  147. #  处理读档画面的类。
  148. #==============================================================================
  149.  
  150. class Scene_Load < Scene_File
  151.   #--------------------------------------------------------------------------
  152.   # ● 初始化对像
  153.   #--------------------------------------------------------------------------
  154.   def initialize(save_max = SAVE_MAX)
  155.     # 再生成临时对像
  156.     $game_temp = Game_Temp.new
  157.     # 选择存档时间最新的文件
  158.     $game_temp.last_file_index = 0
  159.     latest_time = Time.at(0)
  160.     for i in 0..save_max
  161.       filename = make_filename(i)
  162.       if FileTest.exist?(filename)
  163.         begin
  164.           file = Zlib::GzipReader.open(filename)
  165.         rescue
  166.           next
  167.         end
  168.         if file.mtime > latest_time
  169.           latest_time = file.mtime
  170.           $game_temp.last_file_index = i
  171.         end
  172.         file.close
  173.       end
  174.     end
  175.     super("要载入哪个文件?","读取",save_max)
  176.   end
  177.   #--------------------------------------------------------------------------
  178.   # ● 确定时的处理
  179.   #--------------------------------------------------------------------------
  180.   def on_decision(filename)
  181.     # 文件不存在的情况下
  182.     unless FileTest.exist?(filename)
  183.       # 演奏冻结 SE
  184.       $game_system.se_play($data_system.buzzer_se)
  185.       return
  186.     end
  187.     # 演奏读档 SE
  188.     $game_system.se_play($data_system.load_se)
  189.     # 写入存档数据
  190.     begin
  191.       file = Zlib::GzipReader.open(filename)
  192.       read_save_data(file)
  193.     rescue
  194.       # 演奏冻结 SE
  195.       $game_system.se_play($data_system.buzzer_se)
  196.       return
  197.     end
  198.     file.close
  199.     # 还原 BGM、BGS
  200.     $game_system.bgm_play($game_system.playing_bgm)
  201.     $game_system.bgs_play($game_system.playing_bgs)
  202.     # 刷新地图 (执行并行事件)
  203.     $game_map.update
  204.     # 切换到地图画面
  205.     $scene = Scene_Map.new
  206.   end
  207.   #--------------------------------------------------------------------------
  208.   # ● 取消时的处理
  209.   #--------------------------------------------------------------------------
  210.   def on_cancel
  211.     # 演奏取消 SE
  212.     $game_system.se_play($data_system.cancel_se)
  213.     # 切换到标题画面
  214.     $scene = Scene_Title.new
  215.   end
  216.   #--------------------------------------------------------------------------
  217.   # ● 读取存档数据
  218.   #   file : 读取用文件对像 (已经打开)
  219.   #--------------------------------------------------------------------------
  220.   def read_save_data(file)
  221.     # 读取描绘存档文件用的角色数据
  222.     characters = Marshal.load(file)
  223.     # 读取测量游戏时间用画面计数
  224.     Graphics.frame_count = Marshal.load(file)
  225.     # 读取校验
  226.     crcs = Marshal.load(file)
  227.     # 读取文档字串
  228.     strings = Marshal.load(file)
  229.     # 校验检测
  230.     key = SAVE_KEY
  231.     strings.each_index do |i|
  232.       key = Zlib.crc32(strings[i],key)
  233.       unless crcs[i] == key
  234.         file.close
  235.         raise "file check error"
  236.         return
  237.       end
  238.     end
  239.     # 读取各种游戏对像
  240.     $game_system = Marshal.load(Zlib::Inflate.inflate(strings[0]))
  241.     $game_variables = Marshal.load(Zlib::Inflate.inflate(strings[1]))
  242.     $game_self_switches = Marshal.load(Zlib::Inflate.inflate(strings[2]))
  243.     $game_switches = Marshal.load(Zlib::Inflate.inflate(strings[3]))
  244.     $game_troop = Marshal.load(Zlib::Inflate.inflate(strings[4]))
  245.     $game_map = Marshal.load(Zlib::Inflate.inflate(strings[5]))
  246.     $game_player = Marshal.load(Zlib::Inflate.inflate(strings[6]))
  247.     $game_screen = Marshal.load(Zlib::Inflate.inflate(strings[7]))
  248.     $game_actors = Marshal.load(Zlib::Inflate.inflate(strings[8]))
  249.     $game_party = Marshal.load(Zlib::Inflate.inflate(strings[9]))
  250.  
  251.     # 魔法编号与保存时有差异的情况下
  252.     # (加入编辑器的编辑过的数据)
  253.     if $game_system.magic_number != $data_system.magic_number
  254.       # 重新装载地图
  255.       $game_map.setup($game_map.map_id)
  256.       $game_player.center($game_player.x, $game_player.y)
  257.     end
  258.     # 刷新同伴成员
  259.     $game_party.refresh
  260.   end
  261. end
  262.  
  263. #==============================================================================
  264. # 自定义存档菜单
  265. #==============================================================================
  266.  
  267. #==============================================================================
  268. # ■ Scene_Save
  269. #------------------------------------------------------------------------------
  270. #  处理存档画面的类。
  271. #==============================================================================
  272.  
  273. class Scene_Save < Scene_File
  274.   #--------------------------------------------------------------------------
  275.   # ● 初始化对像
  276.   #--------------------------------------------------------------------------
  277.   def initialize(save_max = SAVE_MAX)
  278.     super("要保存到这个文件吗?","存入",save_max)
  279.   end
  280.   #--------------------------------------------------------------------------
  281.   # ● 确定时的处理
  282.   #--------------------------------------------------------------------------
  283.   def on_decision(filename)
  284.     # 演奏存档 SE
  285.     $game_system.se_play($data_system.save_se)
  286.     # 写入存档数据
  287.     file = Zlib::GzipWriter.open(filename,9)
  288.     write_save_data(file)
  289.     file.close
  290.     # 如果被事件调用
  291.     if $game_temp.save_calling
  292.       # 清除存档调用标志
  293.       $game_temp.save_calling = false
  294.       # 切换到地图画面
  295.       $scene = Scene_Map.new
  296.       return
  297.     end
  298.     # 切换到菜单画面
  299.     $scene = Scene_Menu.new(5)
  300.   end
  301.   #--------------------------------------------------------------------------
  302.   # ● 取消时的处理
  303.   #--------------------------------------------------------------------------
  304.   def on_cancel
  305.     # 演奏取消 SE
  306.     $game_system.se_play($data_system.cancel_se)
  307.     # 如果被事件调用
  308.     if $game_temp.save_calling
  309.       # 清除存档调用标志
  310.       $game_temp.save_calling = false
  311.       # 切换到地图画面
  312.       $scene = Scene_Map.new
  313.       return
  314.     end
  315.     # 切换到菜单画面
  316.     $scene = Scene_Menu.new(5)
  317.   end
  318.   #--------------------------------------------------------------------------
  319.   # ● 写入存档数据
  320.   #   file : 写入用文件对像 (已经打开)
  321.   #--------------------------------------------------------------------------
  322.   def write_save_data(file)   
  323.     # 生成描绘存档文件用的角色图形
  324.     characters = []
  325.     for i in 0...$game_party.actors.size
  326.       actor = $game_party.actors[i]
  327.       characters.push([actor.character_name, actor.character_hue,actor.id])
  328.     end
  329.  
  330.     strings = []
  331.  
  332.     # 写入描绘存档文件用的角色数据
  333.     Marshal.dump(characters,file)
  334.     # 写入测量游戏时间用画面计数
  335.     Marshal.dump(Graphics.frame_count,file)
  336.     # 增加 1 次存档次数
  337.     $game_system.save_count += 1
  338.     # 保存魔法编号
  339.     # (将编辑器保存的值以随机值替换)
  340.     $game_system.magic_number = $data_system.magic_number
  341.  
  342.     # 写入各种游戏对像
  343.     strings.push(Zlib::Deflate.deflate(Marshal.dump($game_system)))
  344.     strings.push(Zlib::Deflate.deflate(Marshal.dump($game_variables)))
  345.     strings.push(Zlib::Deflate.deflate(Marshal.dump($game_self_switches)))
  346.     strings.push(Zlib::Deflate.deflate(Marshal.dump($game_switches)))
  347.     strings.push(Zlib::Deflate.deflate(Marshal.dump($game_troop)))
  348.     strings.push(Zlib::Deflate.deflate(Marshal.dump($game_map)))
  349.     strings.push(Zlib::Deflate.deflate(Marshal.dump($game_player)))
  350.     strings.push(Zlib::Deflate.deflate(Marshal.dump($game_screen)))
  351.     strings.push(Zlib::Deflate.deflate(Marshal.dump($game_actors)))
  352.     strings.push(Zlib::Deflate.deflate(Marshal.dump($game_party)))
  353.  
  354.     # 计算校验值
  355.     crcs = []
  356.     key = SAVE_KEY
  357.     strings.each do |i|
  358.       key = Zlib.crc32(i,key)
  359.       crcs.push(key)
  360.     end
  361.     Marshal.dump(crcs,file)
  362.     Marshal.dump(strings,file)
  363.   end
  364. end
  365.  
  366. #==============================================================================
  367. # ■ Window_SaveFile
  368. #------------------------------------------------------------------------------
  369. #  显示存档以及读档画面、保存文件的窗口。
  370. #==============================================================================
  371.  
  372. class Window_SaveFile < Window_Base
  373.   #--------------------------------------------------------------------------
  374.   # ● 初始化对像
  375.   #   file_index : 存档文件的索引 (0~n)
  376.   #   filename   : 文件名
  377.   #--------------------------------------------------------------------------
  378.   def initialize(file_index, filename,viewport=nil)
  379.     super(0, 64 + file_index % 4 * 104, 640, 104)
  380.     self.contents = Bitmap.new(width - 32, height - 32)
  381.  
  382.     @file_index = file_index
  383.     @filename = "Save#{@file_index + 1}.rxdata"
  384.     @time_stamp = Time.at(0)
  385.     @file_exist = FileTest.exist?(@filename)
  386.  
  387.     if @file_exist
  388.       begin
  389.         file = Zlib::GzipReader.open(filename)
  390.         @time_stamp = file.mtime
  391.         @characters = Marshal.load(file)
  392.         @frame_count = Marshal.load(file)
  393.         @total_sec = @frame_count / Graphics.frame_rate
  394.         file.close
  395.       rescue
  396.         @file_exist = false
  397.       end
  398.     end
  399.  
  400.     self.refresh
  401.     @selected = false
  402.   end
  403. end

使用时只需修改SAVE_KEY即可,一般人是无法轻易破解的


唉,6R上的朋友似乎对素材保护比较感兴趣,对作弊倒没什么防范。要是一个游戏可以作弊,那么正常通关的玩家还会有同样的成就感吗?其实反作弊也是在为玩家着想。
但是这两种反作弊还得配合加密技术才能发挥作用,而加解密居然是禁止讨论的,那些技术贴都加了阅读权限限制。而且这些都只是很简陋的加密,对高手来说不堪一击。它的目的并不是绝对保护,而是浪费一下那些无节操作弊玩家的时间和精力,让他们失去破解的信心。
买了正版RMMV的同学进来看一下,谢谢~
https://rpg.blue/thread-393237-1-1.html

Lv3.寻梦者

梦石
0
星屑
3841
在线时间
1966 小时
注册时间
2013-1-3
帖子
9536
2
发表于 2013-8-13 13:03:58 | 只看该作者
其实那个存档密钥用起来不方便
因为需要这样加密的游戏一般比较高端,而自己重定义存档又太费劲…
《宿愿·寻剑篇》正式版已经发布!快去看看!点击进入论坛发布贴
回复 支持 反对

使用道具 举报

Lv2.观梦者

梦石
0
星屑
681
在线时间
791 小时
注册时间
2011-10-20
帖子
2394

开拓者

3
发表于 2013-8-13 13:57:10 | 只看该作者
云rm
欢迎您

点评

纯IE支持差评(好想给你减分  发表于 2013-8-14 12:54
回复 支持 反对

使用道具 举报

Lv4.逐梦者

梦石
0
星屑
9275
在线时间
2504 小时
注册时间
2011-5-20
帖子
15389

开拓者

4
发表于 2013-8-14 12:06:10 | 只看该作者
百度的云平台告诉你修改是浮云
[img]http://service.t.sina.com.cn/widget/qmd/5339802982/c02e16bd/7.png
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
65
在线时间
391 小时
注册时间
2012-5-5
帖子
208
5
发表于 2013-8-14 16:47:47 | 只看该作者
本帖最后由 my.0lantulantu0 于 2013-8-14 17:12 编辑

其实Marshal的入口点还是蛮好找的比如04 08……
Zlib可以通过重写module Zlib实现XX……
加密算法时间:破解算法时间 大约在1:10~1:200左右吧
估计解下边这个也不太轻松:
加密算法时间:破解算法时间 大约在1:5~1:500左右吧
RUBY 代码复制
  1. Marshal.dump(_res).each_byte{|w|f=f+([w].pack(_packtype))}
  2. file.write([f].pack(_packtype2))
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
4476
在线时间
380 小时
注册时间
2012-11-8
帖子
272
6
发表于 2013-8-14 21:04:10 | 只看该作者
内存防修改可以hook api或者用api监控进程,前者容易报毒,后者改个进程名就好了。。。貌似没什么好的方法。。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-4-28 07:25

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表