解构神奇宝贝故障?

tem*_*def 32 hex reverse-engineering decimal

(如果这是一个错误的地方,我很抱歉.我认为它肯定与编程相关,但如果这属于其他网站,请告诉我)

我一直在玩神奇宝贝红色和蓝色,这些游戏非常有趣,但因为有大量可利用的故障而臭名昭着(例如,看到这个使用内存损坏将项目屏幕变成十六进制编辑器的游戏的荒谬速度).

最近,我发现了一个有趣的游戏速度,使用一个名为"ZZAZZ故障"的小故障来破坏重要的记忆位置并让玩家几乎立即赢得比赛.根据作者对speedrun 的描述,ZZAZZ故障的工作原理如下:

要开始训练师之战,游戏需要加载大量数据,例如如果被击败他会承认的钱.当它加载钱时,事情会变得非常丑陋.由于超出我的原因,钱以完全不同的方式存储,游戏使用三个字节的数据结构,而不是将值转换为二进制,它将其存储在"人"表示中.例如,$ 123456将被存储为0x123456而不是0x01E240,即正确的转换.

[Trainer表中的某些无效条目]指向包含无效货币数据的位置.当游戏试图在所述结构中对这些数据进行算术运算时,它会疯狂并开始覆盖大部分RAM.更具体地说,对于每个三个字节的块,其中两个将包含0x9999(教练可以给出的最大金额).这种模式通过RAM重复多次.为了更好地看到这一点,我建议在面对ZZAZZ训练器后在模拟器上暂停视频并将VBA的内存查看器设置为0xD070.

这种分析是有道理的,但作为程序员,我不禁想知道程序员究竟是如何编写使这成为可能的代码的.如果输入不是有效的十六进制编码的十进制数,那么编写将十六进制编码的十进制数转换为十进制的函数的任何方法都不会开始用0x9999填充随机内存块.

我的问题是 - 没有专门设计算法以这种方式失败,是否有直接实现从十六进制编码的十进制到十进制的转换,当输入无效值时可能导致这种类型的内存损坏?

再说一次,如果这是偏离主题,我道歉.我的想法是,这个网站上的其他程序员也可能已经长大了玩这个游戏,这听起来像是一个有趣的逆向工程练习,试图弄清楚这样的故障怎么可能.

Mic*_*gan 13

老实说,我的猜测是,这是一个愚蠢的,令人讨厌的故障,有人写了他们的第一场比赛的目标之一.口袋妖怪Red/Blue是该系列中的第一个,并且有许多其他故障,任天堂通常会通过批次测试,我不知道它是如何通过的.滚动屏幕转换问题是让我感到困惑的问题.无论如何,谁知道他们在想什么.也许这个区域是通过脚本编写的,因此存储的东西不同.也许比特模式0x0101用于显示内存被释放,并且该代码意外地在奇怪的地方疯狂.我可以倾注Z80代码并在该平台上重温我自己的游戏开发时间,但是.太多的工作来尝试和解密他们正在思考的火焰.

它确实赚了不少钱......

编辑1:

好的,你了解它.我花了一点时间倾注在我的记忆中,为你找到了一个小窍门.GBC/DMG有一个名为的操作码DAA.十进制调整累加器(A).这样做是将累加器中的值转换为BCD格式.你看到的内存区域已经是BCD格式:http://en.wikipedia.org/wiki/Binary-coded_decimal

现在我可以告诉你,在4年左右的时间里,当我为游戏编写Z80汇编程序时,我从来没有一次需要这个操作码,只看到它在我们用于显示一些分数的棒球游戏中使用过一次.虽然这是一个1周期的算术指令,但在正常编码中我永远无法真正找到它.嗯.我实际上还有任天堂的DMG技术文档.去图;)无论如何,没有什么令人兴奋的,除了它以一些时髦的方式与许多旗帜混淆.

我的猜测是表格被假定为BCD格式.将其更改为该格式之外的内容会导致内部数学变得非常混乱 - 当它们不应该被设置时设置Carry和Zero标志.这会导致从一列溢出到另一列,导致计算非常大的数字.如果没有直接查看有关该区域的操作码,我无法肯定地说,但是我的猜测是这里有一个全部检查表明如果在完成BCD数学运算后仍然设置了进位,则设置最大值而不是存储负数或超出界限值.DAA当接收到垃圾数据时,该指令或指令返回0x99它的返回值,尽管我对此不太确定.

希望这可以帮助...


Meh*_*dad 9

可以想到一个算法(虽然我为任何可能编写它的人感到遗憾):

  • 假设输入是十六进制表示的32位十进制数字,小端(例如0x56 0x34 0x12 0x00).

  • 现在遍历每个字节,而你还没有达到零字节.(这应该永远不会发生,如果确实确保0x999999是最大的......但是,唉,它不是.)

  • 在每个循环中,计算出实际值和数据写回的整数(或者到一些其他的缓冲区,在那里你做一个"循环而",而不是像"对于i = 0〜4").

如果你的值最后没有0x00(即32位"十进制"整数大于0x999999),你可以看到如何得到一个小故障.

当然,这是计算值的一种相当模糊的方法,但我认为很可能有人做了一个while/do-while循环而不是一个有界for循环.

编辑1:

起初,我认为这将有利于让要显示的字符串的"优势" ,直接到用户(因为它是空终止的),但当然不会与小尾数工作.他们可以用big endian做类似的事情,但这需要一个向后循环来溢出,我发现这是一个不太可能犯错的人.

编辑2:

也许这是一个编译器优化,因为未定义的行为(程序员不知道,如无效的指针转换或别名问题)?

  • 对于这个时代的GBC/DMG游戏,没有值得使用的编译器.这是所有手工编码的汇编程序.没有什么能够让足够有效的代码大小适应4K银行业务和极其有限的堆栈空间. (5认同)

tem*_*def 7

谜团已揭开!看起来用户TheZZAZZGlitch想出了导致这种情况的原因.

当游戏试图计算一个非常大的整数时,会触发故障.在内部,游戏有一个例程,重复添加值来模拟乘法.它似乎写入字节,转移到输出写入位置.该代码旨在切断超过0x009999的任何值,以便玩家从训练师的战斗中获得的收益不超过9999美元(这些值以十六进制编码的十进制值存储).但是,当发生这种情况时,游戏会忘记重置输出指针,因此如果生成一个非常大的数字,游戏将通过将写指针移位并将0x99写入每三个字节中的两个来重复在RAM上写入模式0x009999.

希望这可以帮助!