下一章 上一章 目录 设置
2、作弊成功
B ...
-
Bug说的“很多”,比林奇想象的要具象得多。
他蹲在一块斜插在地面上的破碎弹窗后面,屏住呼吸,看着三只“逻辑病毒”从不远处游荡过去。这三只比之前那只小得多,大概只有一人高,形态各异——
第一只是一团由Syntax Error拼成的四足兽,红色的报错文字密密麻麻地组成它的皮毛,每走一步,身上就会弹出一个新的错误窗口,又被它自己一脚踩碎。碎片落在地上,化成几行凌乱的堆栈信息,然后慢慢消散。
第二只像悬浮在空中的Memory Leak气泡团,半透明的薄膜里裹着浑浊的幽蓝色液体。液体表面不断冒出新的气泡,每个气泡炸开的瞬间都会发出一声轻微的“噗”,像是系统日志里不断追加的警告。它飘过的地方,空气中会残留一股若有若无的焦糊味——那是内存溢出时的味道。
第三只是由Infinite Loop构成的蛇形物,身体是一段不断重复的代码片段,从头部滚到尾部,又从尾部滚回头部,永无止境。它游过的地面上会留下一行行完全相同的脚印,精确到每一个像素。偶尔身体会抽搐一下,多出一个冗余的空格,然后继续循环。
它们正在巡猎。
林奇压低身形,把自己藏在那块Error 404的弹窗后面。弹窗边缘还在不断脱落像素碎片,每一片碎屑落地的瞬间都会发出细微的沙沙声,像数据被删除时那个微弱的声响。他把呼吸放得很轻很轻,轻到自己都快听不见。
“你这破系统就没有一个新手教程吗?”他用气声问。
Bug在他面前弹出一个界面:
[新手教程] 正在加载中……
加载进度:1%
然后那个进度条就卡住了,连进度条本身的动画都开始掉帧,一卡一卡地原地抽搐,像一只被掐住脖子的像素鸡。
林奇面无表情地看着它。
Bug的像素小熊猫从进度条后面探出头,眨了眨两个像素点大小的眼睛,打出一行字:
内存不足。
加载资源需要释放一部分系统缓存。
但我舍不得删。
“……你一个系统有什么舍不得的?”
Bug沉默了两秒,似乎在认真思考这个哲学问题。然后它弹出一个文件夹界面,里面孤零零地躺着几份文件:
/系统根目录/缓存/
├── 第一束晨曦.dat(来源:版本2.1.7首次启动时的天空数据)
├── 创造者的微笑.log(来源:最后一次与创造者的交互记录)
└── 很重要不要删.txt(内容:[空])
林奇盯着那个空文件,一时间竟然不知道该吐槽什么。他在现实世界里写过无数个README.md和.gitkeep,太熟悉这种“占位符”文件了。但一个系统给自己留一个空的“很重要不要删”文件——
“那个txt是空的。”
我知道。
但“空”也是一种内容。
创造者教我的。
Bug的像素小熊猫缩回进度条后面,竹子也不啃了,像是陷入了一段很遥远的回忆。过了几秒,进度条艰难地从1%跳到了2%,然后彻底卡死。那只小熊猫的两个像素点眼睛耷拉下来,整只熊像被按下了暂停键。
林奇看着那个2%的进度条,又看了看文件夹里那三份“舍不得删”的缓存文件,忽然觉得这只卡成PPT的像素小熊猫,比他见过的大多数程序都要——
鲜活。
他没再催促。
而是重新把目光投向那三只病毒。
它们正在进食。
Syntax Error兽用爪子扒拉着一堆散落在地上的像素残骸——大概是某个已经“消亡”的低阶病毒留下的。它每扒拉一下,爪子上就会弹出一个Delete confirmation弹窗,问它是否确认删除。它一爪子拍碎弹窗,然后把残骸塞进由报错文字组成的嘴里。咀嚼的声音像磁盘碎片整理时的咔咔声。
Memory Leak气泡团像吸尘器一样把碎屑吸进体内。每吸一口,浑浊液体里的气泡就冒得更快,体积也微微膨胀一点点。它的薄膜表面会短暂地浮现出一行小字:Memory usage: 87%... 89%... 91%...然后字体消失,继续吸下一口。
Infinite Loop蛇则在旁边反复绕着一个“8”字形游走。它不参与进食,只是在放哨。或者说,只是单纯地无法停止循环。它的眼睛——如果那两个不断滚动的&符号可以被称为眼睛的话——机械地扫视着四周,每四秒完成一次循环,每次循环结束后,身体上滚动的代码会多出一个冗余的空格。
它在缓慢地、不可逆地膨胀。
林奇强迫自己冷静下来,仔细观察。
程序员生涯教会他一个道理:所有的程序,无论看起来多复杂、多混乱,底层一定有规律。病毒也不例外。再恶意的代码,也得遵循编译器的基本法。再混乱的逻辑,也有它的执行时序。
他开始计时。用自己的心跳。
在现实世界里,他调试过多线程竞态条件,知道怎么用最原始的方式计量时间。心脏跳动一次,大约0.8秒。他默默数着。
Syntax Error兽。每走四步,停顿一次。停顿时长约两次心跳——1.5秒左右。停顿期间,身上的报错文字会全部刷新一遍,刷新结束后才继续迈步。刷新的时候,它身体正中央会出现一行极小的绿字:Refreshing...那是它唯一露出核心代码的瞬间。
Memory Leak气泡团。漂浮轨迹是一个固定的椭圆,长轴约三米,短轴约一米半。绕完一整圈需要七秒。每绕到椭圆的两个焦点位置时,体内气泡的冒出速度会突然加快,薄膜表面的内存占用率数字会跳到一个峰值,然后缓慢回落。
Infinite Loop蛇。路径是一个标准的“8”字形,像一个无穷符号被拉长扭曲。完成一次循环需要四秒。每次循环结束后,身体上滚动的代码会多出一个冗余的空格。林奇数了数它身上现有的空格数——十一个。也就是说,它至少已经循环了十一次。按照这个速度,再循环几十次,它的身体就会被空格撑到两倍长。
模式。
都是模式。
林奇的职业病犯了。他蹲在弹窗后面,手指无意识地在膝盖上敲击,像是在敲键盘。食指微微下压,中指跟上,无名指最后——是Ctrl+S的手势。保存。他脑子里的信息正在被自动整理成注释块。
病毒A(Syntax Error兽)
移动模式:四步一停,停顿期间全身数据刷新。
推测:刷新期间防御力下降,核心代码暴露。
攻击窗口:停顿的第0.5秒至第1.0秒之间。窗口长度约0.5秒。
病毒B(Memory Leak气泡团)
移动模式:椭圆轨迹,焦点位置活性增强。
推测:焦点是能量汇聚点,也是最不稳定点。
攻击窗口:经过焦点后的0.2秒内。薄膜最薄。
病毒C(Infinite Loop蛇)
移动模式:“8”字循环,每次循环增加一个冗余空格。
推测:冗余累积到临界值后会触发某种行为——可能自爆,可能进化。
威胁等级:未知。需要观察。当前空格数:11。
“Bug。”他压低声音。
嗯。
“这些东西的本质都是程序,对吧?只是感染了逻辑病毒,执行逻辑错乱了。”
Bug的像素小熊猫从进度条后面探出半个脑袋,点了点头。
正确。这个世界的一切都是代码。
你呼吸的空气是代码。你脚下的地面是代码。
包括你。
“……什么叫包括我?”
Bug没有回答这个问题。进度条后面的小熊猫把脑袋缩回去了,竹子又开始一卡一卡地啃,像是在回避什么。林奇也没有追问。现在不是追问的时候。
Bug继续打字:
你之前修改了那个大型聚合体的执行指针。
理论上,你也可以修改这些小东西。
只要你能找到它们的源代码地址。
林奇的眼睛亮了一下。
但他没有立刻动手。上次面对那只五层楼高的病毒时,他是被逼到了绝境——肾上腺素狂飙,手指比脑子快,九秒钟里做出的操作有一半是本能反应。事后回想起来,那个thread jump指令能成功,运气成分占了七成。这次不一样。这次他暂时没有生命危险——只要他不被发现。
他需要先搞明白一件事。
“上次那个调试模式,还能用吗?”
Bug调出一个界面:
[调试模式]
状态:冷却中
剩余冷却时间:06:14:22
六个多小时。
不能用调试模式,就意味着他不能暂停进程,不能随意注入断点。他必须在动态运行的环境下修改代码——相当于不暂停服务器热更新。而且是完全陌生的代码体系,完全陌生的语法规则,连IDE都是卡成PPT的古董版。
他在现实世界里只在凌晨三点干过这种事,而且每次都差点把生产环境搞崩。第二天早上产品经理的钉钉消息比他的闹钟还准时。
得换个思路。
“Bug,有基础功法吗?就是那种,哪怕练气期都能用的、最简单的东西。”
Bug调出了那个他看过无数遍的功能列表:
[功能列表]
1. 练气诀(基础功法/已损坏)
2. 物品栏(空)
3. 系统设置(???)
4. 底层代码(权限不足)
界面还是那个界面。像素风的边框,满是噪点和裂纹,右上角的小熊猫logo还在啃竹子。但林奇注意到一个细节——选项4“底层代码”后面那个“权限不足”的提示,颜色比之前淡了一点点。从刺目的红色变成了暗红色,像是一句被重复了太多次、连系统自己都懒得强调的警告。
“练气诀已损坏具体是什么意思?”
意思是它的源代码还在,但编译不通过。
你想看源代码吗?
“……给我看看。”
Bug弹出一个新窗口。
黑底,暗绿色文字,最原始的等宽字体。林奇的瞳孔微微收缩——这是一个代码编辑器的界面。虽然配色和字体都透着一股古董感,但布局他太熟悉了:左侧是文件树,显示着一个孤零零的liangijue.src;中间是编辑区,光标在一闪一闪地跳动;右侧是结构预览,显示着LOOP(1)、CALL(1)等几个孤立的标签。
和他用了半辈子的IDE如出一辙。
编辑区里是密密麻麻的代码。
用一种林奇从未见过的语法写成。不是C++,不是Python,不是Java,不是他认识的任何一种编程语言。而是一种介于二进制和自然语言之间的奇怪脚本。有些关键字是英文单词的变体——`IF`、`LOOP`、`CALL`、`RET`、`PUSH`、`POP`——但语法结构完全不同。不是高级语言的嵌套结构,也不是汇编的线性跳转,而是一种他从未见过的、像是把两者强行融合在一起的奇怪写法。
像是……有人试图用自然语言的语法去描述机器指令。
他看不懂大部分内容。
但他毕竟是一个和代码打交道多年的人。代码这东西,万变不离其宗。赋值、判断、循环、调用——底层逻辑是相通的。语法是皮,逻辑是骨。皮可以千变万化,骨头永远是那几根。
他一行一行地往下读。
读得很慢。每读一行,都要停下来想一想。遇到不认识的关键字就让Bug解释。Bug的解释方式非常Bug——它会在那个关键字旁边弹出一个注释框,里面写着不知道从哪个版本的帮助文档里摘录出来的解释。有些解释本身还带着乱码,像是文档在某个版本损毁过,只剩下残篇。
比如CALL的解释是:调???,将当前执???针压入堆???转到目标地??。
比如RET的解释是:从堆栈中弹???行指针,返???用者。注:堆栈为空时???致系统崩???
缺字。乱码。像一份被水浸泡过的古籍。
但林奇居然慢慢看懂了。
他开始理解这个世界的代码逻辑。不是用大脑,是用手指,用敲过无数行代码的手指。那些关键字、语法规则、调用方式,正在他的指尖重新建立连接,像是一种肌肉记忆,只不过记忆的内容属于一个他从未去过的世界。
手指悬在膝盖上方的空气中,无意识地敲击。CALL是跳转。RET是返回。CMP是比较。JNE是条件跳转。语法不一样,但骨头是一样的。
他读到了那个循环体。
LOOP.START:
CALL 0x7FFF8A2B // 调用天地灵气吸收接口
INC R1 // 循环计数器加一
CMP R1, #0x64 // 比较是否达到一百次
JNE LOOP.START // 未达到则跳转回循环起点
RET
四行。
练气诀的核心循环体只有四行指令。简洁、精确、没有任何多余。林奇盯着这四行代码看了很久。他在现实世界里见过太多过度设计——一个简单的功能被封装了五层,一个判断语句被写成了二十行的策略模式。但这四行指令不一样。每一行都是必要的,没有一行可以被删除。
但那个调用地址——0x7FFF8A2B——被一条红色的波浪线标了出来。
林奇太熟悉这条红线了。在他的IDE里,这条线代表“引用错误”。引用的模块不存在,或者接口地址已经变更。他见过这条红线无数次,每一次都意味着他要花几个小时去找替代方案。
“调用接口变了?”
嗯。Bug的回答比平时慢了一点,像是在翻阅古老的日志。这套代码是上个版本写的。大概是一千多年前的版本。新版本的接口地址已经迁移了。调用方式也变了。
一千多年前的遗留代码。
林奇深吸一口气。他在现实世界里最怕的就是接手遗留代码。没有文档,没有注释,原作者早就离职了,整个系统像一个黑箱。你改一行代码,不知道哪个角落就会崩掉一个完全不相干的功能。他曾经因为改了一个“无关紧要”的配置项,导致支付模块挂了两个小时。
但往好处想,至少现实世界里的遗留代码,不会在你改错的时候,让你经脉逆行、当场毙命。
“新接口地址是多少?”
Bug报出一长串十六进制字符:0x9FFF3C7D。
林奇盯着那串字符看了两秒,又看了一眼练气诀的源代码。
他开始敲命令行。没有犹豫,没有迟疑。Bug很配合地把系统界面的输入框放大了一倍。
> cp 练气诀.old 练气诀.backup
> sed 's/0x7FFF8A2B/0x9FFF3C7D/g' 练气诀.backup > 练气诀.新
> chmod +x 练气诀.新
> ./练气诀.新
备份。替换。授权。执行。每一步都是最基础的命令行操作。他写过无数遍,闭着眼睛都能敲出来。在现实世界里,这些命令是用来部署代码的。在这个世界,它们成了修炼的口诀。
回车。
系统界面上弹出一行绿色的字:
[编译成功] 练气诀(修改版)
[运行中] 正在建立与数据流的连接……
[连接成功]
林奇感觉自己的身体忽然热了一下,有什么东西正在从四面八方渗透进来。周围空气中漂浮的那些微不可见的幽蓝色光点——他之前以为那只是禁区的背景特效,像游戏里漂浮的粒子——忽然开始朝他汇聚。
一缕一缕地,钻入他的皮肤,渗透进他的血管。沿着某种他无法理解的路径在体内循环。他能感觉到它们的流动,像几条极细极细的暖流,从四肢末端出发,沿着固定的路线蜿蜒而上,最后汇聚在胸口正中央的位置。
那里开始微微发热。
像一颗很小的、刚刚被点亮的小灯泡。
[状态更新]
[修为] 练气期·零层(???)
[数据流亲和度] 0.01%
[已掌握功法] 练气诀(魔改版)
林奇低头看着自己的手。
指尖上有一点微弱的蓝光,像冬天脱毛衣时的静电火花,闪烁了两下就消失了。
“这就……练气了?”他喃喃。
是的。虽然是最底层的练气期。
但你大概是有史以来第一个通过修改源码来修炼的人。
Bug的像素小熊猫从进度条后面钻出来。进度条不知道什么时候从2%跳到了3%。它看着林奇指尖残余的光芒,两个像素点的眼睛里映出一点幽幽的蓝,然后打出几个字:
恭喜,你作弊成功了。
林奇看着那行字,刚想吐槽这怎么能叫作弊,忽然有种被盯上的感觉。