霸都老科学岛,地下二层。
废弃的机房亮起一盏应急灯。三面机架空空荡荡,墙上还贴着2008年的机房巡检表——泛黄、落满灰。空气里混着臭氧和铁锈味,像一台快要报废的老式服务器。
「这是我爸建神码之前待的地方。」程思语拍掉ThinkPad上的灰,「零几年合肥还叫霸都的时候,他在这儿写第一版交易引擎。」
陈默扫了一圈。墙角线槽里还挂着十年前的六类网线,像一条条干枯的蛇。
「他肯定想不到,有一天他女儿会拿他起家的地方,造他施毒的解药。」
程思语没接话。她把内网虚拟机接入机房里一个布满灰尘的服务器端口——壳公司租的专线,物理层独立,绕过程泰来的一切监控。
屏幕亮了。
陈默打开终端,敲下第一行:
```python
import ast
```
手指悬在回车键上,停了半秒。
「怎么了?」程思语问。
「上一次敲这个东西,还是用来画Python语法树玩。」陈默按下回车,「这一次,要拿它当手术刀。」
光标下,代码逐行展开。
```python
sample = """
try:
process(data)
except ValueError as e:
alert(e)
raise
"""
tree = ast.parse(sample)
print(ast.dump(tree, indent=2))
```
屏幕上弹出一棵语法树——从 `Module` 根节点,到 `Try` 节点,到 `ExceptHandler`,再到 `Raise`。每一层都干干净净,像是编译器没被污染之前的纯净蓝图。
程思语盯着屏幕:「所以AST层面是干净的——那字节码的毒,是在编译那一步下的?」
「对。」陈默敲下第二段代码:
```python
compiled = compile(sample, '<验证>', 'exec')
```
然后打开 `dis`。
```python
import dis
dis.dis(compiled)
```
屏幕显示 `RAISE_VARARGS` 指令——完好无损,像刚从工厂出来的零件。
程思语长长呼出一口气。
「这一行 `raise`——」她说,「我追了三个多月。」
「不是`raise`的问题。」陈默把 `ast.dump` 的输出拉大,「是验证的问题。我们怎么让全世界相信,任意一段Python代码,它的异常处理是真实的?」
他敲了一段新的代码——这一次,不再是从字符串生成AST,而是手写AST节点。
```python
# 手动构造一个保证 raise 的代码块
raise_node = ast.Raise()
handler = ast.ExceptHandler(
type=ast.Name(id='Exception', ctx=ast.Load()),
name='e',
body=[raise_node]
)
try_node = ast.Try(
body=[ast.Raise()],
handlers=[handler],
orelse=[],
finalbody=[]
)
module = ast.Module(body=[try_node], type_ignores=[])
ast.fix_missing_locations(module)
clean_code = compile(module, '<freedom>', 'exec')
exec(clean_code)
```
终端立刻输出:
```
Traceback (most recent call last):
File "<freedom>", line 1, in <module>
raise Exception()
Exception
```
程思语盯着那三行Traceback,眼眶红了一瞬。
「这是我见过最漂亮的Traceback。」
陈默没说话。他盯着屏幕上的异常堆栈——一个真正被抛出、被传播、没有被任何人吞掉的异常。
从第1章被裁,到第9章发现字节码投毒——他终于亲手编译了一段无法被篡改的 `raise`。
「但这还不够。」陈默把椅子往后一推,「手动构造AST不是出路。我们需要一个工具——」
「——能从任意Python代码生成可信字节码的编译器?」程思语接上。
「不。是验证器。」陈默打开一个新的文件,敲下几行骨架:
```python
class RaiseGuard:
"""编译验证器:确保每一条路径上的 raise 都真实存在。"""
def __init__(self):
self._trusted_compile = compile
```
「先做验证,再做生成。先保证能看到毒,再保证不中毒。」
程思语的手机震了一下。她低头看,脸色微变。
「怎么?」
「我爸的人开始查壳公司的IP了。我们大概——还有三天。」
窗外,科学岛的夕阳把锈蚀的机架染成金色。三面空机架,一台ThinkPad,两个被全行业追捕的人,和一个刚写了两行的 `RaiseGuard` 类。
陈默把窗口最小化。
「三天够写一个编译器。」
程思语没笑。
「如果不够呢?」
「那至少够让Traceback被全世界看见。」
他敲下第三行代码——为 `RaiseGuard` 加上第一个函数。
屏幕的光在灰暗的机房里,是唯一还亮着的东西。