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