(如果这里问错了,我很抱歉。我认为这肯定与编程有关,不过如果这属于其他网站,请告诉我)
我从小就在玩“魔怪红蓝”游戏,这些游戏非常有趣,但由于有很多可利用的小故障而声名狼藉(例如,请看这场比赛的疯狂快攻,它利用内存损坏将项目屏幕变成了一个十六进制编辑器)。
最近,我发现了一个有趣的游戏快速运行,它使用一个名为"ZZAZZ小故障“的故障来破坏重要的内存位置,并允许玩家几乎立即赢得比赛。据作者对速滑的描述称,ZZAZZ的故障工作如下:
要开始一场教练大战,游戏需要加载大量的数据,比如.如果输了他会认输的钱。当它满载钱时,事情就会变得很糟糕。出于我无法理解的原因,货币以完全不同的方式存储,游戏使用三个字节的数据结构,而不是将值转换为二进制,而是将其存储在“人工”表示中。例如,$123456将存储为0x123456,而不是0x01E240,这是正确的转换。 培训师表中的一些无效条目指向具有无效货币数据的位置。当游戏试图对上述结构中的这些数据执行算术时,它就会发狂,并开始覆盖大量RAM。更具体地说,对于每三个字节块,其中两个将包含0x9999 (培训师可以给出的最大金额)。这种模式通过RAM重复多次。为了更好地看到这一点,我建议在ZZAZZ培训器面对后暂停在模拟器上的视频,并将VBA的内存查看器设置为0xD070。
这种分析是有意义的,但作为我的程序员,我不禁想知道,程序员到底是如何编写的代码,使之成为可能。如果输入不是一个有效的十六进制编码的十进制数字,那么编写一个将十六进制编码的十进制数字转换为十进制的函数将永远不会开始用0x9999填充随机内存块。
我的问题是,如果没有专门设计这种失败的算法,是否可以直接实现从十六进制编码的十进制到十进制的转换,当输入无效的值?时,会导致这种内存损坏。
再说一次,如果这不是话题,我很抱歉。我的想法是,这个网站上的其他程序员可能也是在玩这个游戏长大的,这听起来像是逆向工程中的一个有趣的练习,试图找出这样的故障是如何可能发生的。
发布于 2014-06-06 19:27:07
谜团解开了!看起来像用户TheZZAZZGlitch找出了造成这种情况的原因。。
当游戏试图计算一个非常大的整数时,就会触发故障。在内部,游戏有一个例程,反复添加数值来模拟乘法。它似乎是一边写字节,一边在输出写位置上移动。该代码旨在切断任何超过0x009999的值,这样玩家从训练师的战斗中获得的收入不会超过9999美元(这些值存储在十六进制编码的十进制中)。但是,当出现这种情况时,游戏会忘记重置输出指针,因此,如果生成一个非常大的数字,游戏将通过将写入指针移到内存中并将0x99写入到每三个字节中的两个字节,从而在RAM中反复写入模式0x009999。
希望这能有所帮助!
发布于 2011-07-29 23:53:42
老实说,我的猜测是,这只是一个愚蠢的,肮脏的小故障,有人写了他们的第一个目标游戏之一。红/蓝口袋妖怪是该系列的第一个,还有很多其他的小故障,任天堂通常会退出抽签测试,我想知道它是如何通过的。滚动屏幕移位的问题是一个让我。不管怎样,谁知道他们在想什么。也许这个区域是通过脚本编写的,因此存储东西的方式就不同了。也许位模式0x0101被用来表示内存被释放了,而代码在奇怪的地方意外地发疯了。我可以倒在Z80代码上,在那个平台上重新体验我自己的游戏开发时间,但是。太多的工作试图解密他们所想的火焰。
它确实赚了很多钱.
编辑1:
好吧,你悬赏了它。我花了更多的时间倾注在我的记忆中,并为你找到了一点线索。GBC/DMG有一个名为DAA的操作码。小数调整累加器(A)这样做是将累加器中的值转换为BCD格式。您正在看到的内存中的区域已经采用BCD格式:小数。
现在我可以告诉你们,在我为游戏手工编写Z80汇编程序的4年左右的时间里,我从来没有需要过这个操作码,只看到它在棒球比赛中使用过一次,我们用它来显示一些分数。虽然它是一个1周期算术指令,但我从来没有真正找到一个好的用途,在正常的编码。嗯。实际上,我还有任天堂的DMG技术文档。不管怎么说,它也没有什么令人兴奋的地方,只是它以古怪的方式搅乱了许多旗子。
我猜这张表被假定为BCD格式。将其更改为超出该格式的内容会导致内部数学变得极其混乱--在不应该设置的情况下设置为零标志。这会导致从一列到另一列的溢出,导致计算非常大的数字。如果不直接查看读取此区域的操作码,我无法确定,但我猜这里有一个catch所有检查,即如果在完成BCD数学时仍然设置了进位,则设置一个最大值,而不是存储一个负值或超出范围的值。当接收到垃圾数据时,该指令或DAA指令将返回返回值的0x99,但我对此不太确定。
希望这能帮上忙。
发布于 2011-08-01 04:31:49
我可以想到一种算法(尽管我为写它的人感到遗憾):
如果您的值末尾没有0x00 (即32位“十进制”整数大于0x999999),您可以看到如何得到故障。
当然,这是一种比较模糊的计算值的方法,但我认为很有可能有人对此执行了时间/do-while循环,而不是有界的for循环。
编辑1:
起初,我认为这样做有一个“好处”,就是允许将字符串直接显示给用户(因为它将以空结尾),但这当然不适用于小endian。他们可以对大端点做类似的事情,但这需要一个反向循环才能溢出,我认为这是一个不太可能有人犯的错误。
编辑2:
可能是由于未定义的行为(程序员不知道的,比如无效的指针转换或混叠问题)而导致的编译器优化?
https://stackoverflow.com/questions/6876642
复制相似问题