单字节替换历史
卡通卡通避暑胜地
那是2000年的夏天。 我六岁了,刚读完一年级,假期开始了。 这意味着我可以在街上玩很长时间,看动画片,并用Windows 98打开父亲的电脑,在一个崭新的,未知的地区,称为Internet上搜索游戏。 我最喜欢的网站之一是卡通网络网站。 在它上面,我发现了许多基于电视动画片的引人入胜的Flash游戏。 那个夏天,他们发布了一系列名为“卡通卡通避暑胜地”的游戏。
卡通卡通避暑胜地第一集的游戏玩法该游戏是二维RPG /冒险游戏,从侧面俯视,包括四集。 玩家在度假时与其他卡通人物一起控制了一个卡通人物。 在每个情节中,度假村中都会出现一个需要解决的问题。 玩家必须通过与角色互动并找到对象或交换它们来解决它。
追溯到18年
在最近一次怀旧攻击中,我想起了这款游戏,意识到自己会再次玩。 她快两岁了,所以很难找到工作联系。
此外,除了Internet Explorer,没有任何现代浏览器会启动古老而脆弱的Shockwave播放器。 我可能是第一个在2018年找到合理理由使用Internet Explorer的人。
玩了一点之后,我注意到了一些不容忽视的时刻。 例如,单调的背景音乐不断循环播放,并且对碰撞的识别不佳。
相同的错误
一段时间后,我在游戏中发现了一个错误:
在不应该发生任何事情的区域中移动时,有时会出现一个对话框。从动画中可以看出,在游戏中您可以租用一条船在水上移动。 当您尝试租用另一艘船时,会出现消息“今天不再有租用的船!”。 如果您向北航行并沿着岛的右边缘行走,但是会打开同样的文本,该文本应该出现在船码头上。
在这个阶段,任何尊重自己的时间和精力的理智的人都会认为该错误是一个小麻烦,提醒自己这是二十年前创建的面向儿童的中等水平的网络游戏,并且会继续玩。 但不是我。
我已经拥有了我所有的编程知识,并且看着游戏,所以我觉得这个bug非常吸引人。 在我看来,我已经出土了一座古墓,并在墓中发现了一个原本可以消失的,没有被任何人解决的谜题。 对我而言,此错误是学习和发现新事物的机会。 有趣的是,正是这些机会才
使游戏过程给了我童年。 如果您从不同的角度看待某件事,那么无意间会给新挑战带来些许诗意。
解构游戏
要修复该错误,我需要了解游戏的内部工作原理。 检查问题后,我发现该游戏是在
Director应用程序的Shockwave上创建的。 使用Director时,项目将另存为
.dir
(导演)文件。 该文件类似于Photoshop的PSD文件。 就像PSD文件包含有关图层和文本的非破坏性信息一样,
.dir
项目会保存所有资源,原始源代码以及其他有助于开发过程的信息。 导演使用专有的脚本语言
Lingo对场景进行动画处理。
如果将游戏保存在
.dir
文件中,则可以在Director中打开它并轻松了解游戏的工作原理。 但是,游戏发布为
.dcr
文件。
.dcr
文件是Director项目的编译版本。 也就是说,所有源代码都被编译为在Shockwave平台上运行的字节码。 此过程类似于简化PSD文件并变成PNG图像的过程。 PNG图像(在我们的示例中为DCR文件)较小,但不包含有关图层和编辑的信息,仅用于分发。
这意味着我持有一个500 KB的二进制对象而没有其结构的文档。 即使我想出了如何找到低级字节码,似乎也没有人对Lingo字节码进行逆向工程,也没有记载Shockwave平台的原理。 所有这些信息均为专有信息,归Adobe所有,没有理由发布。 了解游戏的机会似乎很惨淡。
减压
因为我很可能无法消除此错误而感到失败,所以我决定确定是否可以从游戏中提取资源。 我认为很可能会找到一段压缩数据或类似内容。 搜索之后,我找到了两个名为
offzip和packzip的程序。 这些工具可以在任意二进制文件中搜索zlib数据,显示偏移量并将其提取到单独的文件中。
我用DCR文件下载了压缩文件,令我惊讶的是,它确实找到了档案! 249件,更精确地说。
$ ./offzip.exe -a 1.dcr
- open input file: 1.dcr
- zip data to check: 32 bytes
- zip windowBits: 15
- seek offset: 0x00000000 (0)
+------------+-----+----------------------------+----------------------+
| hex_offset | ... | zip -> unzip size / offset | spaces before | info |
+------------+-----+----------------------------+----------------------+
0x00000026 . 164 -> 214 / 0x000000ca _ 38 8:7:26:0:1:7b6349f6
0x000000d3 .. 3932 -> 9169 / 0x0000102f _ 9 8:7:26:0:1:c1079d84
...
0x00080490 . 265 -> 472 / 0x00080599 _ 0 8:7:26:0:1:04d6b43f
0x00080599 . 209 -> 366 / 0x0008066a _ 0 8:7:26:0:1:7da3ba08
- 249 valid compressed streams found
- 0x0004040d -> 0x001565c8 bytes covering the 50% of the file
我将所有这些文件提取到一个文件夹中,然后开始研究结果。 有206个
.dat
文件,38个
.fff
文件,4个
.atn
和一个
.ini
文件。
发现
我从INI文件开始,但是没有用。 这是Windows和Mac之间Directory 7.0中的一个简单字体转换表。 然后我转到DAT文件。 它们大多数都是1KB,所以我从一个巨大的144KB开始。 我用十六进制编辑器将其打开并进行了研究。 大多数情况下,这是难以辨认的数据。 但是,随着时间的流逝,我发现其中有些单词似乎是Lingo标识符。
对大型DAT文件的分析为我提供了一些线索,它们保留了一些有趣的信息。 我发现Photoshop 3.0最有可能用于图形。 我还了解到该游戏有一个内部地图编辑工具,称为
Map-O-Matic v1
。 我想看看它的外观和创建方式。
我还找到了开发游戏的公司名称:
Funny Garbage 。 文件中也包含首席开发人员的姓名,但我不会命名。 很高兴发现这个游戏的作者,我在将近20年后顽固地试图修复它,并终于看到了成为这个痛苦的可能原因的人的面孔。 所有这些碎屑的信息固然很有趣,但是并没有太大帮助。
突破
然后,我开始在十六进制编辑器中研究
.fff
文件。 令我惊讶的是,这些文件中的所有数据都是可读的,看起来像地图数据:
我手动提取了其中一些数据,并在文本编辑器中对其进行了清理。 我所做的非常像一个JSON数组:
[
#member: "block.104",
#type: #FLOR,
#location: [16, 9],
#width: 64,
#WSHIFT: 0,
#height: 32,
#HSHIFT: 0,
#data: [
#item: [
#name: "",
#type: #WALL,
#visi: [
#visiObj: "",
#visiAct: "",
#inviObj: "",
#inviAct: ""
],
#COND: [[#hasObj: "", #hasAct: "", #giveObj: "sunscreen", #giveAct: "gotscreen"], #none, #none, #none]
],
#move: [#U: 0, #d: 0, #L: 0, #R: 0, #COND: 1, #TIMEA: 0, #TIMEB: 0],
#message: [
[#text: "You bought the sun screen.", #plrObj: "", #plrAct: ""],
[#text: "No more sunscreen today!", #plrObj: "", #plrAct: "gotscreen"]
]
]
]
这非常重要,因为我设法学到了很多有关游戏运作方式的知识。
- 游戏期望地图,文本和事件数据位于类似JSON的Lingo对象中
- 每个
#member
条目都是一个#member
图块,块或字符。 - 坐标和尺寸
#member
可以编辑。
知道游戏的对话框已保存到这些文件后,我写了一条短线,仅将一个对话框导出到文件中:
grep -a -o '#text: "[^"]*' Uncompressed/*.fff | awk '{print $0,"\r"}' > Dialogue.txt
使用此文件,我可以快速找到错误的文本并查看其中的文件:
错误的文本是在
0004eda0.fff
或
0004f396.fff
。 在我们的例子中,错误文本出现在第一个文件中。 我们知道这一点,因为紧随其后的是与Og交互时收到的消息,Og与带有错误的图块在同一地图上的角色。
错误修复
现在,我可以打开
0004eda0.fff
并在十六进制编辑器中找到有关船的行。 找到它之后,我能够找到与它关联的
#member
对象。 然后更改其属性并保存文件。 然后,我再次对其进行压缩,并使用
packzip
将其修补到DCR游戏源文件中。
$ ./packzip -o 0x0004EDA0 Uncompressed/0004eda0.fff test.dcr
当我将块类型从
block.11
为
block.13
并对游戏进行补丁时,我可以清楚地看到错误
block.13
贴的轮廓:
通过更改图块ID,您可以看到问题区域的边界错误修复本身非常简单。 我要做的就是将此错误
#fessage
#message
的标识符
#message
更改为
#fessage
:
现在,如果我们修补更改并返回此区域,则消息将不再出现!
修复了游戏中的错误,实际上改变了1个字节为什么可以解决该错误?
我只能假设游戏引擎可能会在玩家移动时检查其站立的磁贴。 如果满足某些条件,则它将显示
#message
数组中与此图块相对应的消息。 将
#message
更改为
#fessage
不再找到代码正在寻找的
#message
数组
#fessage
链接。 他认为这是一个空(或未定义?)对象,并且不显示任何内容。
考虑一下JS上的示例:
function foo(bar) { if (bar["message"] !== undefined) {
假设我们不能更改函数
foo()
,但是我们需要更改结果。 我们可以访问传输给她的数据。 您可以重命名所传递对象的
message
属性,该函数将认为它不存在。
该错误如何出现?
我想是因为懒惰。 开发人员使用地图编辑器来创建该区域。 他们很可能将过去制作的卡片复制到新区域,然后进行更改。 这比从头开始创建所有内容容易。 但是由于事件和文本数据混合在一起,因此它们很可能在某些地方被忽略了,却忘记了删除或替换它们。 这看起来是最合乎逻辑的,因为错误的对话通常来自正确使用了该卡片的相邻卡片。
为什么要在微不足道的事情上花费大量的工作?
我不知道 也许是由于怀旧?