C64《Mercenary》(1985):开局就能触发的一个新漏洞
文章摘要
这是 Games Explained 网站为 Commodore 64 经典游戏《Mercenary》(1985 年,Novagen Software 发行,Paul Woakes 制作)做的一个”迷你站”。页面称它为”开放世界的鼻祖级杰作”:你的飞船坠毁在行星 Targ 上,可以自由探索一座有 233 栋建筑的线框 3D 城市,以及城外和高空的各种去处。整页内容是贡献者 air 逐条反汇编原版游戏代码后写出的技术解读,共 23 节,附带大量可交互的移植演示:飞行、地下房间、物体目录、电脑 Benson 的说话声音、数学例程,都是把 6502 汇编逐个例程移植到网页上,并在 6502 模拟器中与原代码逐字节对照验证过。
标题里的漏洞:开局草地上的隐形电梯。 你开局坠落在城市方格 08-08,巨大的殖民飞船(Colony Craft)就悬停在你正上方 64997 的高度,地面上这里本来没有电梯。但从降落点出发、朝西北走大约 14 秒(约 100 步),有一块边长 1024 单位、肉眼看不出任何标记的草地,在这里按 E 键,游戏会把它当成殖民飞船甲板上的电梯:你”下行”进入殖民飞船的机库(8 号房间),再按一次 E,就上到了甲板,高度显示 64997。原因很简单:殖民飞船悬在 08-08 上空,所以甲板和下方草地共用同一组 X、Y 坐标,只有高度不同;而 E 键的判定只检查所在方格和 X、Y 的中间字节,从不读取高度。于是甲板电梯正下方的那块草地,表现得和电梯一模一样。作者说这个漏洞对游戏的破坏没有想象中大:8 号机库的三扇门都锁着,开局时你最远只能到机库和甲板,基本上唯一的出路就是坐”特快电梯”,也就是直接从殖民飞船顶上走下去,一路翻滚摔回城市。页面给出了复现步骤:等开场动画播完、Benson 打出”CRASH LANDED ON TARG”后,右转八分之三圈面向西北,走 14 秒左右按 E;最稳妥的办法是在 VICE 模拟器的监视器里直接改坐标。
其他发现,挑几个有意思的。
- 城市的渲染方式:为了控制渲染开销,游戏内存里一次只保留一栋建筑,你所在的方格决定加载哪一栋,所以建筑只有在进入其方格时才会”蹦出来”。从高处往下看能看到道路,到了极高处则换成一幅固定的 13 条道路图。整个世界是 256×256 个方格,城市只占中间 16×16;城外的荒野里只放了四样东西:两部通往机库的电梯、一座金字塔和一根桅杆,桅杆没有名字,代码里也没有任何地方引用它,用途不明。
- 永远绕城飞行的飞船:Palyar 指挥官小舅子的新飞船从游戏开始就沿城市西缘以 ALT 500 的高度一圈圈往南飞,永不停歇。没人能驾驶它,但它有自己的运动参数记录,而且是游戏里最快的,极速 11475,比玩家能开的最快载具还快。击落它会触发专门的脚本和 Palyar 的报复。
- 隔壁方格射击无人追究:导弹击中建筑后,游戏会启动 16 个循环的倒计时,期间才去计数、报出建筑名并派对应阵营的飞船来追你,但只有你站在那个方格里时这段逻辑才会执行。从相邻方格开火摧毁建筑,倒计时结束后谁也不会注意到。
- “广告战”:城里有五块 Commodore 广告牌和五块 Atari 广告牌。打掉 Commodore 的会得到一句”TRAITOR!”(叛徒),打掉 Atari 的则是”GOOD SHOW!”(干得漂亮)。
- 性能与飞行模型:画面是 160×136 的多色位图,双缓冲交替绘制,俯瞰全城时一帧要画 13 个电视帧,大约每秒 4 到 5 帧。飞行模型里正弦表采样偏了半格,导致”水平”姿态的正弦值是 −0.003 而不是 0,平飞会缓慢下沉,倒车反而会爬升。移植版测得的各载具极速与 1986 年 5 月 Zzap!64 杂志刊登的表格逐位吻合。
- Benson 的声音:飞船电脑 Benson 每帧打出一个字符,每个字符对应一个音高,这就是那种滴滴答答的”说话声”;字母从 A 的 737 Hz 到 Z 的 1112 Hz,跨度约 7 个半音,空格则静音。游戏没有音乐,只有 13 个四字节的音效,引擎声的音高随速度变化。
- 数据与数学:地下有 174 个房间,每个都是一个带门轮廓的线框盒子;全游戏 64 个物体,从载具、钥匙到沙发都在一张表里;消息用词典编码存储;钱用 BCD 码存储,脚本不支持减法,买东西是加上价格的十进制补码;乘法通过对数表相加实现,误差在 ±0.44% 以内;画线用八个例程分别处理八个象限。
- 防拷与杂项:原版磁盘伪装成卡带,按复位键时会跳进游戏自己的一个死循环,本来想逐页清空内存,但分支跳错了一条指令,结果永远只写同一个地址。游戏在美国的 NTSC 机器上会崩溃。数据里还夹带了一段 Atari DOS 2 菜单屏幕的残片。如果你打掉了城里作者为前作《Encounter》做的广告牌,飞船会拒绝起飞:”作者不让你走,除非你把他的广告修好。”通关后脚本系统的忙碌标志永远不会清除,”GAME OVER”每隔 18 秒左右就会再出现一次。另外,由于起始资金 9000、所有价格都是 5000 的倍数,你的余额尾数永远是 4000 或 9000。
HN 评论精华
这个帖子只有 63 分、11 条评论,讨论很少。之后还有人以「C64 Mercenary: a novel exploit bug in a 41-year-old game」为题重复提交了两次,都只有 dang 留下的指向本帖的链接,没有额外讨论。
漏洞是怎么发现的。 提交者 a1r(也就是页面的贡献者 air)总结说:在起点西北方向约 100 步处有一块隐形草地,作者忘了做高度检查,按 E 就能传送到头顶上方的殖民飞船,”这个 bug 41 年来一直没被人注意到”。paulgerhardt 问他是怎么找到的,能不能用于速通跳关。a1r 回答说,他做了一个用于自动化逆向工程、专门寻找有趣 bug 的技能(skill),是 Opus 5.5 发现了这一个,相关源码已放在 GitHub 上。至于速通的问题,systemnate 提醒文章里已经写了:机库的门都锁着,到了甲板也只能跳下来。
怀旧。 aenis 说这款游戏对他影响巨大,他 9 岁时在 Commodore Plus/4 上玩:”那种自由探索的感觉!城外远处的隐藏电梯,可以摧毁的建筑,还有那颗卫星!”amiga386 悼念了 2017 年 7 月 15 日去世的作者 Paul Woakes,并用游戏里的梗致意:”愿他那至关重要的 12939 永不断供。”uzidil 分享了自己大约十年前做的一个网页版致敬作品,aenis 说他今年本来想自己移植,现在不用了。TacticalCoder 说自己当年有 C64、各种游戏、工具和一个可以冻结程序改内存数值的”Power Cartridge”,C64 是他多年的全部生活,可他竟然没听说过《Mercenary》,惊叹于在那么少的内存和资源下能做到这些;他的结尾是:”告辞,我要去为下一个网站加载的 JavaScript 体积哀叹了。”salvarito 称赞网站的讲解非常细致,Razengan 则说他总有一天要把《M.U.L.E.》也研究透。