☰
如何用skitter-creek-bath-salts的dram_dump批量读取受保护内存:含别名映射与往返校准的完整教程
2026/10/8 15:42:04 网站建设 项目流程

如何用skitter-creek-bath-salts的dram_dump批量读取受保护内存:含别名映射与往返校准的完整教程

【免费下载链接】skitter-creek-bath-saltsUnlocking _everything_ on the CPU with DRAM scrambling项目地址: https://gitcode.com/gh_mirrors/sk/skitter-creek-bath-salts

skitter-creek-bath-salts是一个基于DRAM 地址扰序(DRAM scrambling)的研究型开源工具集:它改写 AMD 内存控制器(MCT/DCT)里的一两个配置位,把物理地址"搅成意大利面",让受保护内存区域(PSP、SMRAM、C6 上下文等)通过别名地址(alias)变得可读。本文聚焦其中的批量读取工具 userspace/dram_dump.c:从别名映射的原理、.map文件的生成,到内置的固件指纹校验 + 往返校准双重安全机制,一步步带你完成对受保护 DRAM 区间的批量转储。


一、动手前的三个前提

⚠️ 先说清楚:这是一个研究向工具,会临时改写活体内存控制器状态,可能造成系统不稳定。请务必在自有的备用机器上实验,并准备好断电重启手段。

前提说明
目标平台AMD Family 16h(数据手册公开了内存控制器转换寄存器,且不可锁定)。可用 userspace/platform_check.c 提前自检,不支持的平台会直接退出
root 权限所有用户态工具均需 root 运行,内核模块 kernel/spaghettify.c 提供/dev/spaghettify设备节点
匹配内存的别名映射仓库 data/maps/ 内置了多种 DIMM 配置的现成.map文件(如2x4gb_fw-s1b1_at-s0b0.map),文件名中的fw-sWbS / at-sWbS表示固件默认状态与扰序访问状态;也可用自己硬件生成(见第四节)

用sudo dmidecode -t memory查看你的内存条型号,对照 data/maps/ 前缀即可选到正确的映射。


二、别名映射:为什么"别的地址"能读到受保护内存 🔑

理解dram_dump的关键,是理解别名(alias):

  1. 正常视图下,物理地址A经内存控制器换算成 DRAM 坐标,落在受保护单元里——普通读访问会被围栏(fence)挡下,返回全0xff;
  2. 控制器被临时翻转某个 swizzle 位后,另一个普通地址B经换算落在同一个 DRAM 单元;
  3. 于是"在扰序视图中读B"就等于"在正常视图中读A",而B是一条完全不受围栏保护的普通通路。读完后立刻还原控制器位,平台恢复原状,不留痕迹。

这个"地址搅动"过程的直观演示(&x != &x的瞬间):

数学上,地址转换是 GF(2) 线性映射,所以只要采集一批(target, alias)数据对,就能用 Z3 求解出完整的扰序矩阵——这就是.map文件。


三、dram_dump 的内置双保险:固件指纹 + 往返校准

批量读受保护内存最怕的不是读不到,而是读错位置。userspace/dram_dump.c 在每次真正读取之前强制执行两道检查:

1️⃣ 固件指纹(fw-fingerprint)校验工具先查询控制器实时寄存器(swizzle / bankswap / bank-addr-map),与.map文件头部的fw_*字段比对。BIOS 更新、换内存条、重训导致任何一位不一致,运行立即中止(--ignore-fw-mismatch可强制跳过,仅建议研究使用)。

2️⃣ 往返校准(round-trip calibration)

  • 内核模块在加载时保留一块 16 KB 的安全区(DRAM_SCRATCH_INFO),其中没有别的内核代码会碰;
  • 工具从安全区自动挑一个满足约束的普通地址,短暂写入一个随机魔数(随后恢复),再按映射预测的别名在扰序视图下读回;
  • 读回值不等于魔数 → 映射对当前硬件失效 → 中止。

巧妙的地方:校准用的翻转访问是读而不是写——映射算错只会让你看到垃圾值并安全中止,绝不会向未知 DRAM 单元误写。同一开机会话内确认无误后,可用--dangerously-skip-calibration跳过以加快速度。


四、完整工作流:五步转储受保护内存

# 1. 编译(生成 kernel/spaghettify.ko 与全部用户态工具) make # 2. 平台自检,不在支持范围直接退出 ./userspace/platform_check || exit 1 # 3. 查看当前固件默认状态(fw_* 指纹来源) sudo ./userspace/dram_state # 4. 定位受保护区域,自动导出 PSP_BASE / PSP_SIZE eval "$(sudo ./userspace/dram_carveouts --region psp)" # 5. 批量读取:原始字节输出到 stdout,重定向保存 sudo ./userspace/dram_dump --protected-pa $PSP_BASE --length $PSP_SIZE \ $(printf -- '--map %s ' data/maps/2x4gb_*.map) > psp.bin

💡 注意第 5 步一次性传入了三张映射(at-s0b0/at-s0b1/at-s1b0)。每张映射由不同的扰序状态求解而来,各有一些秩亏导致的"死区"(某些字无别名可达);dram_dump按顺序对每个 4 字节字采用第一张能到达它的映射,三者取并集即可覆盖单张映射读不到的地址。所有映射必须描述同一块硬件(位宽、MMIO 空洞、fw_*一致),否则拒绝启动。

没有现成映射?三步自己生成

仓库提供了现成的分析流水线(analysis/):

# 采集别名数据对(JSONL 存入 data/aliases/,可用 --at-swizzle/--at-bankswap 选扰序态) sudo python3 analysis/gather_aliases.py --at-swizzle 0 --at-bankswap 0 \ --log data/aliases/2x4gb_fw-s1b1_at-s0b0.jsonl # 求解并保存映射(--progress 可实时观察矩阵收敛) python3 analysis/unspaghettify.py \ --save-map data/maps/2x4gb_fw-s1b1_at-s0b0.map \ < data/aliases/2x4gb_fw-s1b1_at-s0b0.jsonl
  • analysis/gather_aliases.py:自动探测 DIMM 容量与搜索掩码,随机采样收集别名;不稳定的机器可改用 analysis/gather_aliases_remote.py 经 SSH 远程驱动;
  • analysis/unspaghettify.py:Z3 逐位求解扰序矩阵;加--target-pa 0x...还能"只查不读",打印任意受保护地址的别名——dram_dump读取路径上不需要 Z3,昂贵的求解只发生在离线这一步。

采集到的数据与求解出的映射在仓库中各有一份样例,格式可对照:

  • 样例数据:data/aliases/2x4gb_fw-s1b1_at-s0b0.jsonl
  • 样例映射:data/maps/2x4gb_fw-s1b1_at-s0b0.map

五、dram_dump 常用选项速查

选项作用
-s, --protected-pa <hex>受保护区间起始物理地址(正常视图)
-l, --length <n>读取字节数,须为 4 的倍数
--map <file>别名映射,可重复传入以并集覆盖
--dry-run只执行双保险检查后退出,不读任何 DRAM——验证映射是否可用的首选
--calibrate-pa <hex>指定校准用地址(缺省时自动从模块安全区挑选)
--dangerously-skip-calibration跳过往返校准(名字故意起得刺眼,慎点)
--ignore-fw-mismatch指纹不一致时仍强制加载映射
--fenced-range <lo>,<hi>把更大的受保护预留区标为围栏窗口,防止"别名落回窗口内"的假读
--allow-fenced-alias对落回围栏的别名也照样读(通常只会拿到围栏填充值)
--plain不加载映射、不改控制器,直接按地址读——用于对比"未扰序"的基线视图

一个实用技巧:先用--plain读同一区间,看到整片0xff(围栏填充值),再切换到--map模式读同一区间——前后对比能直观感受扰序前后的差别。


六、读不到怎么办?处理 0xff 空洞

dram_dump对每个读不到的 4 字节字会填充0xff、计数并在 stderr 告警,保证输出字节流与请求区间严格对齐。出现空洞时的排查顺序:

  1. 多传几张--map:不同(at_swizzle, at_bankswap)组合的死区互不相同,并集能救回大量地址;
  2. 换扰序态重新采集:--at-swizzle/--at-bankswap与固件态至少差一位才能产生有效扰序,换一组重新跑 analysis/gather_aliases.py 再求解;
  3. 注意围栏别名:某些映射的数学别名会落回受保护窗口内部,翻转读取只会撞回围栏拿到填充值。dram_dump默认把这类目标按空洞处理而不是静默报告假数据,可用--fenced-range显式标出整个预留区来捕捉这种情况;
  4. 接受现实:若求解出的矩阵在前向方向秩亏、目标地址的位模式恰好在常量位上冲突,则任何开关与 DIMM 组合都不可达——这是控制器状态与内存布局的数学属性,不是工具能"再努力一下"的。

七、安全提示 ⚠️

  • 工具会临时改写活体内存控制器,README 原话:"Expect system instability; have a power-cycle path ready"(预期系统不稳定,请准备好断电重启路径);
  • 只读方向(dram_dump)误操作的代价是读出垃圾;而它的写侧兄弟 userspace/dram_poke.c 误写会损坏未知 DRAM 单元,所以后者强制校准、逐字回读验证、跳过无法到达的字——批量实验请始终从读开始;
  • 本教程仅面向合法的安全研究场景,请在自己的设备上、为自己的安全评估目的使用。

八、相关源码索引 📁

文件说明
userspace/dram_dump.c本文主角:批量读取受保护 DRAM 区间
userspace/alias_map.h映射解析、GF(2) 伪逆、校准与多映射调度的共享核心
userspace/dram_state.c只读快照:当前 fw_* 指纹
userspace/dram_carveouts.c定位 PSP / SMRAM / C6 等受保护预留区
analysis/unspaghettify.pyZ3 求解别名映射并保存.map
kernel/spaghettify.h内核 ioctl 接口定义(DRAM_RW/DRAM_STATE等)
USAGE.md完整工具参考与端到端示例

按这套流程走完,你已经掌握了dram_dump的全部精髓:离线求解一次映射,在线读取零 Z3;指纹 + 校准双保险兜底;多映射并集补齐死区。接下来,试着把psp.bin丢进反汇编器看看 PSP 里的 ARM 代码吧——那是另一个故事了 🕵️

【免费下载链接】skitter-creek-bath-saltsUnlocking _everything_ on the CPU with DRAM scrambling项目地址: https://gitcode.com/gh_mirrors/sk/skitter-creek-bath-salts

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询