返回博客

2026.10.29

一个固件解析器在 100 份随机文件里「找到」了 98 份内存训练表:一份没附原始数据的逆向笔记,哪些结论还能信?

拿随机字节喂解析器、拿带地址的数据填满显存,两个低成本测试把一份流传很广的显卡固件笔记拆成了三层:可信、过度外推、已被实卡推翻。

gpureverse-engineeringmethodologyhomelab

我给那个解析脚本喂了 100 份纯随机的 1 MiB 文件,它在其中 98 份里「找到了」内存训练表。

随机字节里当然没有训练表。这个脚本出自一份讲矿卡解锁的 gist(GitHub 上的代码片段页),讲的是一张挖矿阉割版计算卡的固件结构。我手上正好有这张卡,买来就是为了解锁显存。笔记里还有一句更要命的话:这张 8GB 版的卡物理上只有 32GB 显存。我的卡稳定用着 64GB。

所以问题很具体:一份写得很专业、带着一堆偏移地址的逆向笔记,作者没附原始 ROM,我也没法重跑他的实验,我怎么判断哪些话能信?

想直接拿方法的看第 2、3 章,各是一条公式;想看我自己在这件事上说过什么过头话,看第 4 章。

1. 这份笔记长什么样

三个文件:27,112 字节的正文、8,805 字节的 shell 脚本、18,873 字节的 Python 解析器。正文对比了七份 ROM,给出一大堆偏移、签名范围、加密模式判断。

七份 ROM 一份都没附。烧录器日志、反汇编、测试用例也都没有。

说白了,读者拿到的是一张地图,画地图用的那些勘测记录全在作者自己硬盘上。这种东西最难办:它看起来很硬,你又没法直接验证。

但它附了解析器。解析器是代码,代码能跑。这就是突破口。

2. 第一个测试:随机字节喂进去,看它报什么

思路很土:一个格式识别器,如果在随机数据上也频繁报「找到了」,它报的东西就没有信息量。

我生成了 100 份 1 MiB 的随机文件,挨个喂给解析器的几个扫描函数:

扫描函数 100 份随机文件里报「找到」的份数
内存训练表扫描 98
固件版本/ID 扫描 63
CFG1 配置表扫描 0

CFG1 那个是干净的,0 份。它对结构的要求够严,随机数据凑不出来。另外两个基本是在随机字节里捞模式。

这个比例还能反推出一件更有用的事:这个匹配器实际上在检查多少位信息。

一个 1 MiB 文件大约有 2²⁰ 个起始位置。假设每个位置随机命中的概率是 p,位置之间近似独立,那么:

一份文件里至少命中一次的概率 r ≈ 1 − e^(−N·p)
反推:p ≈ −ln(1 − r) ÷ N,等效检查的位数 ≈ log₂(1/p)

就是个除法加一个对数。代进去:

  • 训练表扫描,r = 0.98 → 每份文件期望命中约 3.9 次 → p 约 1/268,000 → 相当于只检查了约 18 位
  • ID 扫描,r = 0.63 → 每份期望命中约 1 次 → 约 20 位

对照一下:一个老老实实的 4 字节魔数是 32 位。1 MiB 随机数据里期望命中 2²⁰ ÷ 2³² = 1/4096 次,100 份文件里大概一份都碰不上。

18 位意味着什么?两个多字节的特征。在一个 1MB 的真 ROM 里,它找到的「训练表」可能是真的,也可能是恰好长得像的任意两三个字节。你没法从输出里分出来。

这里要标一句:18 位、20 位是我用上面那个公式反推的估算,前提是位置独立、数据均匀随机。真 ROM 里有大量重复结构和填充字节,误报率可能更高也可能更低。这个测试证明的是「这个匹配器在随机数据上分不清」,不能精确告诉你它在真 ROM 上错多少。

顺手读源码,又翻出几个不需要跑就能看出来的问题:

  • 找 PCI ROM 头的函数在头恰好位于偏移 0 时返回整数 0,调用方用 if result: 判断,于是最常见的合法情况被当成「没找到」
  • 没找到训练表时提前返回的结构里塞了一个 Python set,最后 json.dump 直接抛 TypeError。文档说「每个输入都输出 JSON」,实际上没找到就崩
  • 注释说在找签名 RFRD,代码比较的是它的倒序 DRFR,放一个规范的 RFRD 进去返回空
  • 「逐分区容量档位表」的扫描会把刚找到的 CFG1 表本身再认一遍,我造的测试 ROM 里两者偏移都是 0x031000
  • 文档说会提取 subsystem ID,那个函数的主体是一行 pass,而且根本没人调用它

这几条都不难修。它们说明的是另一件事:这个解析器没被系统地测过。 一个没测过的工具,它报的东西我默认打折。

3. 第二个测试:物理 32GB 这句话,一个除法就能判

笔记说 8GB 版物理只有 32GB(4 颗 8GB 显存堆栈)。如果这是真的,解锁到 64GB 只能是地址镜像:后 32GB 的地址其实映射回前 32GB,写后半段会覆盖前半段。

这种事很多显存测试是测不出来的,这是本文最想给你的那件武器:

全 0、全 1、0101 交替这类固定花样,在镜像内存上照样全过。 你往地址 A 写 0,往它的镜像 A' 也写 0,回读两边都是 0,完美。

要测镜像,写进去的值必须跟地址绑定。我用的测试脚本在每个 8 字节单元写的是:

值[i] = 种子 + i × 0x9E3779B97F4A7C15

每个位置的值都不一样。如果物理容量 P 小于测试容量 A,至少有 A − P 字节会被别的地址覆盖:

被覆盖字节数 ≥ 测试容量 − 物理容量

对账:我在一次单独分配里写满 58 GiB 再全部读回校验,错误数 0。如果物理只有 32GB,至少 26 GiB 会读回错误的值。0 错误,所以至少有 58 GiB 互不重叠的真实存储。驱动和内核侧报的是 65536 MiB、堆大小 0x1000000000(64 GiB);之后我跑本地模型,常驻占用 46.6 到 59.5 GiB,跨了好几次重启都正常。

「物理 32GB」这一句,被一个除法推翻了。

4. 我自己也说过头了

推翻别人的同时,我得交代一件自己的事。

在读这份 gist 之前,我在笔记里很自信地写过「8GB 版物理是 4 × 16GB」。这句话我也没有证据。上面那个测试能证明的只有一件事:至少 58 GiB 互不重叠、能分配、能用的地址空间。 它是几颗堆栈、每颗多高、封装里有没有被熔断屏蔽的分区,这个测试一个字都没告诉我。要知道这些得拍 X 光、开盖看丝印,或者逐分区做探测。

所以准确的说法降级成:「可用 64GB 是事实,物理拓扑未知。」笔记说 32GB 是错的,我说 4 × 16GB 是没证据。两边都犯了同一个毛病:从一个侧面的证据直接跳到了封装结构。

5. 正文里那些没法直接测的结论,怎么分级

解析器能跑、容量能测,剩下的大段正文没有原始数据,只能靠读。我按三层分:

能信的:VBIOS 的结构图、签名覆盖范围、各种表的偏移。这些有具体地址、有跨 ROM 对照,跟现有开源解锁工具的行为也对得上。笔记解释了为什么直接改 ROM 会坏签名、为什么现有方案都走运行时打补丁,这部分逻辑是通的。

过度外推的:

  • 标题说「七个字节就是全部限制」。它最多覆盖了作者在所选 ROM 里归因到「显存 + PCIe 速度策略」的那几个字节,熔断配置、运行时算力限制、PCIe 链路上被拆掉的电容、驱动侧的显存管理,一样都不在里面
  • 笔记说某个字节从 44 改成 66 就是 64GB 解锁。按现有解锁工具的源码,66 档对应 40GB,我的卡实际稳定跑的是 77 档(配置值 0x02779000)
  • 判断加密模式是 AES-ECB,给了三条证据,写成「CBC 绝不可能」「排除了 CTR」。三条证据确实都指向 ECB,但它们来自同一批密文,是同一次观察的三个侧面,算不上三次独立实验。我信它大概率是 ECB,不信「绝不可能」这几个字

自相矛盾或不稳定的:

  • 翻 gist 的提交历史,已知明文从 FF×16 改成过 00×16、密钥索引从 6 改成过 37,后来又改回去。一个改了两轮又回滚的「已知明文」,我按未决处理
  • 同一段里说「16 个 strap 状态」,又说硬件只有 STRAP0 到 STRAP2 三根引脚。3 位只能表示 8 种状态
  • 混用了 8Gb 颗粒和 8GB 堆栈。8Gb 是 1GB,8 层叠起来才是 8GB

这个分级本身就是方法:有地址、有对照、能跟独立来源对上的,信;从局部证据跳到全局结论的,降级;自己历史里来回改过的,挂起。

6. 这套测试的毛病

  • 随机输入测试用的是均匀随机字节,跟真 ROM 的分布不一样。它能证明匹配器分辨力差,给不出真 ROM 上的准确误报率
  • 只跑了 100 份。98/100 和 63/100 这种量级足够下判断,但 18 位、20 位这两个估算的置信区间我没算
  • 容量测试是一次 58 GiB 的单次写读。它排除了镜像,但对偶发位翻转、长时间保持这类问题覆盖有限
  • 正文里那些需要烧录器和反汇编才能验证的结论,我一条都没复现。上面的分级是基于可读性和一致性的判断,不是实验结果

7. 下次拿到一份没附原始数据的逆向笔记

  1. 先找它附带的任何可执行的东西:解析器、脚本、测试。能跑的部分最先验
  2. 拿随机字节喂每个识别/扫描函数。命中率高于几个百分点的,用 −ln(1−r) ÷ N 反推它实际检查了几位,低于 32 位就别把它的「找到了」当证据
  3. 读源码找边界:偏移 0、空结果、找不到时的返回值,这三处最常出事
  4. 涉及容量的结论,用跟地址绑定的数据写满再读回。固定花样的显存测试照样会被镜像骗过
  5. 把正文结论分三层:有地址有对照的、从局部跳到全局的、在提交历史里来回改过的
  6. 推翻别人的时候,回头查一遍自己笔记里同一件事写了什么

我那张卡到现在也没人知道它肚子里有几颗显存,它自己不在乎,照样每天跑模型。