外观
从 MP2/EasyTool Hook 到 Health Percentage:SM2268XT2 的可观测性与健康度机制
- 数据时间:2026-06-25 Asia/Shanghai
- 研究对象:SMI SM2268XT2 Secondary MPTool、EasyTool、MPISP、Health Percentage 与系统块读写路径
- 报告类型:存储芯片逆向与健康度机制研究博客
- 资料来源:
smi_mp2_patcher、smi_nvme_mpisp_patcher包内源码与文档、IDA 静态分析记录、EasyTool/MPTool hook 实测记录
样本边界:
- 本文讨论的是 SM2268XT2 Secondary MP2 / EasyTool 这条工作线,不自动外推到 SM2262、SM2267XT、SM2269XT 或其它厂商主控。
- MP2 hook 与 EasyTool hook 默认定位是可观测、只读取证和受控实验。写入、擦除、Health 修复都必须以目标盘自身 dump、spare/checksum 复现、写后完整 readback 和断电后验证为前提。
- 文中提到的地址、opcode 和函数名来自当前样本的逆向命名。它们是工程坐标,不是公开 ABI;换固件、换 MPTool 版本或换 NAND 后必须重新验证。
核心结论
这轮工程的重点已经越过“再做一个 Clear SMART 补丁”,转向把 SMI NVMe 设备现场拆成三层可审计链路:
| 层 | 工具形态 | 作用 | 边界 |
|---|---|---|---|
| MP2 host hook | smi_hook.dll / smi_hook_launcher.exe | 观察 Secondary MPTool 的 phase、runmode、vendor command、buffer、MessageBox、进程退出 | 默认 observe-only,不改返回值,不吞失败 |
| MPISP patcher | patched MPISP2268XT2.bin + host trigger | 在 MPISP window 内触发自定义 payload,验证 RAM source 或后续设备侧路径 | host trigger 只负责触发,Health 语义必须在 MPISP/ISP 侧成立 |
| EasyTool EZT hook | ezt_hook.dll + loopback JSON-RPC/MCP | 复用 EasyTool 官方 Flash RW helper,自动化 listSysBlocks / readBlock / command capture | 当前主线只使用只读 dump;写/擦需要完整备份、校验与授权 |
Health Percentage 的研究也已经从“SMART byte 5 是哪个字段”推进到“谁在生成这个百分比”。当前结论很克制:0x1028504C 确实是 SMART rebuild 会读取的参数输入,但它不是当前 Percentage Used = 3% 的充分持久来源。更强的闭环来自 C2/0x53 的 erase count 表、D1 telemetry 和 endurance PE 基准:
text
C2/0x53[0x0000..0x073f] = 全盘 data block 16-bit erase count 表
C2/0x53[0x3000..0x300a] = 主 ISP 按区域现场计算的 6 个 PE/EC summary
avgTLC = 23
avgSLC/MLC = 285
maxTLC = 38
minTLC = 9
maxSLC/MLC = 286
minSLC/MLC = 281
InfoBlk.bin + 0x338 = 10000
ceil(285 * 100 / 10000) = 3
ceil(286 * 100 / 10000) = 3这解释了为什么原生 Clear SMART 返回成功,CrystalDiskInfo 仍显示已用寿命 3%。普通 SMART counter 被清了,但 EC / PE / endurance 来源没有变;下一次 Get Log Page 或 SMART rebuild 仍会把旧的磨损状态带回来。
MP2 Hook:先把黑箱流程变成事件流
Secondary MPTool 的问题在于 UI 上只显示“Update MPInfo”“Back to ISP mode fail”一类结果,真正关键的状态藏在 host wrapper、vendor command 和设备 runmode 之间。MP2 hook 的第一目标就是把这些瞬间落成事件,而不是靠弹窗猜测。
统一 hook runtime 的输出是三类文件:
text
events.jsonl
summary.json
buffers/*.bin事件模型按 host、mpisp、isp、ui、process 分层,记录 phase、call site、opcode、CDW、runmode、返回值和动作。第一版 hook 覆盖 ExitProcess、TerminateProcess、MessageBoxA/W、通用 vendor passthrough sub_481D40 和 runmode/status read sub_4838B0。当 inline 观察打开时,buffer dump 会记录 MPISP download chunk、MPISP debug/status readback、SysBlk flash R/W payload、NVMe SMART / D1 telemetry return buffer。
这件事的价值很直接:过去“程序走一会直接退出”只能截图,现在可以问:
- 最后一条 vendor command 是什么?
- 设备当时在 ISP mode 还是 MPISP mode?
- host 下载的 MPISP chunk hash 是否等于包内当前 MPISP?
- 失败发生在
C1/0x99SSDINFO route、C2/0x88debug/status readback,还是 reset/back-to-ISP? - 进程退出前有没有 MessageBox、异常或 caller RVA?
MP2 hook 的默认目标不是绕过失败。它默认不修改返回值、不跳过 call、不注入命令。override 只能作为单独 profile 下的实验策略;正常工作流先拿证据,再决定应该改 host、MPISP payload、系统块,还是放弃这条假设。
Host Trigger 与 MPISP Patch 的分工
SM2268XT2 Secondary 的原生 Clear SMART 不在 MPISP window 内执行。主流程大致是:
text
Force ROM
Download MPISP
Set MPISP mode
Update MPInfo / InfoBlock / ISPBlock
Reset drive
Back to ISP
Clear SMART & Unformat原生 Clear SMART & Unformat 调用 sub_442EA0,下发的是:
text
opcode = 0xC0
CDW12 = 0x0E
data = none但它发生在 reset/back-to-ISP 之后,设备已经不在 MPISP window。于是 host trigger patch 的插入点选在 Reset drive 之前:先读 runmode,如果 status[4] == 1,说明仍在 MPISP mode,就额外发送一次 C0/0E + CDW13,再恢复原始 reset 流程。
这里要分清职责:
| 部分 | 做什么 | 不做什么 |
|---|---|---|
| Secondary MPTool host trigger | 在正确时机发一次 C0/0E + CDW13 | 不在 host 里伪造 Health 成功,不直接写 SMART log |
| MPISP payload | 接住 CDW13=0/1/2/3,执行 probe 或 RAM source 清零实验 | 不自动证明 NAND/metadata 已持久改变 |
| 主 ISP | 正常返回 NVMe SMART / D1 / C2/0x53 | 当前不是可直接 patch 的目标二进制 |
早期 payload 的 CDW13 分层仍有诊断价值:
CDW13 | 定位 |
|---|---|
0x00 | probe:确认 payload 被触发,检查运行态 base 是否可用 |
0x01 | health-source:清 0x1028504C 并读回 |
0x02 | production 实验:清 0x1028504C 和一组 SLC/endurance RAM 来源 |
0x03 | full-state 实验:再清 state/archive mirror |
但实机结果已经说明:RAM source 清零即使返回成功,也不足以让 Health Percentage 持久归零。这条路线现在被降级为诊断层,不能包装成最终修复方案。
EasyTool Hook:把 SysBlk 读写变成可脚本化接口
MP2 hook 解决的是 Secondary MPTool 的生产流程取证;EasyTool hook 解决的是另一个问题:如何复用官方 EasyTool 已有的 Flash RW 能力,自动化系统块读取和证据打包。
ezt_hook 注入 EasyTool 后,会在本机 loopback 端口启动 JSON-RPC 服务,默认端口是:
text
127.0.0.1:22682控制台窗口标题为 EZT Hook JSON-RPC Trace,会同步打印 hook 事件、rpc/request 和 rpc/response。大块 dataHex、inputHex、outputHex 会折叠,避免一次 16 KiB 或 32 KiB 读包刷爆窗口。
当前直接方法包括:
| 方法 | 用途 | 默认使用边界 |
|---|---|---|
ping / info | 确认 hook 版本、PID、direct methods 是否可用 | 安全 |
ui.tree / ui.click / ui.comboSelect / ui.getText | 复用 EasyTool UI 控件 | 适合验证 UI 链路 |
captures.list / captures.get | 查看 DeviceIoControl / command capture | 只读取证 |
ioctl.raw | 重放或构造底层 IOCTL | 需要严格限制 |
listSysBlocks | 读取系统块索引/可选项 | 只读 |
readBlock | 复用 EasyTool 内部 Flash read helper | 当前主线 |
writeBlock / eraseBlock | 复用 EasyTool 内部 Flash write/erase helper | 只有完整备份、校验和授权后才允许 |
这套接口也明确排除了一个常见误解:EasyTool UI 里的 DRAM RW 不等于能直接读写控制器 SRAM。当前 hook 不暴露 ram.read / ram.write,也不把 ISP 内的 0x1028xxxx 地址当作 EasyTool 进程地址。Health RAM source 验证仍要回到 MPISP payload 或已证明的 vendor RAM opcode;ezt_hook 继续聚焦 SysBlk/NAND block 自动化和 host command 取证。
Health Percentage 不是静态 SMART 字节
NVMe SMART/Health log 的 byte 5 看起来只是一个字节,实际不是 host 写进去的静态字段。设备在 Get Log Page 或 rebuild 时会重新生成它。
ISP 里可以看到多条 rebuild 路径读取:
text
0x1028504C = 0x10284C00 + 0x44C再经过转换 helper,把结果写到 [base]+0x1005,也就是 SMART byte 5 的输出位置。原生 Clear SMART sub_C408C 会清 power cycles、unsafe shutdowns 等普通 backing counter,然后调用 rebuild;但它没有清 0x1028504C,也没有清 EC / SLC / endurance 来源簇。
更重要的是,当前盘的反证已经把 0x1028504C 从“唯一来源”降级为“参数输入之一”:8 份 E1/Info page 0x72 的 data[0x44C] 都是 0x00,但设备 NVMe SMART Percentage Used 是 0x03。这说明当前 3% 不能只由 page72+0x44C 解释。
更强的解释来自这条链:
text
0x10193E3C RAM EC table
-> C2/0x53 输出 0x740 字节 EC table
-> 0x3000..0x300A 现场计算 avg/max/min summary
-> D1 telemetry 中的 erase count / endurance PE 字段
-> Percentage Used ~= ceil(SLC_EC * 100 / MaxSlcEndurancePE)所以现在的最小验证顺序变成:
- 读 D1 telemetry 和
C2/0x53,确认 SLC/TLC avg/max EC 与 endurance PE。 - 读 NVMe SMART LID
0x02offset0x05,确认当前 Percentage Used。 - 观察 selector
0x1A、sub_A6920/sub_6A920与 EC summary 的依赖,而不是只盯0x1028504C。 - 找到 EC table / type=3 metadata backing 或原厂 erase-count edit 入口。
- 写后同时读 D1、
C2/0x53、LID0x02,并在 reset/back-to-ISP 后复测,确认没有回弹。
为什么 C1/0x97、C1/0x99 和 C2/0x56 都不能当主线
这轮研究里几个看起来像入口的点已经被降级。
C1/0x97 曾经被怀疑是 0x2000 MPInfo 写入路径。force-heavy 实验确实抓到了 buffer,但内容偏 SN、Model、FW、NQN、MPINFO 等静态身份信息,没有命中 Health、Wear、SLC endurance 或 Percentage Used = 3 的明显模式。它不是当前 Health 动态来源。
C1/0x99 是当前 Update MPInfo fast route 会走到的 SSDINFO / Clear SMART 相关路径。MP2 日志里它会返回成功,但 ISP 侧复核显示 Clear SMART handler 仍是 C0/0E -> sub_C408C,没有新的 0x99 metadata writer。它证明 MPTool 流程跑通,不证明 Health backing 被改。
C2/0x56 静态上能触发 selector 0x24 -> sub_119690,而 sub_119690 会编辑 RAM EC table 0x10193E3C。但实测设备 probe 的 host-visible 行为和 length 相关:Length=0 长忙,非零 length 返回 UNKNOWN,reconnect 后 C2/0x53 summary 不变。因此它只能保留为 selector 静态证据,不能写成 F8 -> 56 或任何可执行清零 recipe。
MPISP window 里的 C2/0x53 也被证伪为读 EC 主线:当前 MPISP mode 下返回 UNKNOWN,EasyTool Read Telemetry 和 FA Prog/Erase Count 也都是全 0。EC / telemetry builder 依赖主 ISP / FTL runtime,不是在 MPISP window 里随时可读。
落盘逻辑:从 RAM EC 表到 metadata record
OpcodeC2.bin 不是 NAND 上某个系统块文件的 raw backing。它是主 ISP 从 RAM 0x10193E3C 现场导出的 host buffer:前 0x740 字节是全盘 data block EC table,尾部 0x3000 起是现场计算出的 summary。
持久化主线现在更像这样:
text
0x10140000..0x10197FFF metadata arena
-> 0x10193E3C RAM EC table
-> sub_6025C()
copy 0x10193E3C..0x1019457B
to 0x101C9080..0x101C97BF
-> E1 / type=3 metadata record body
-> NAND system block / metadata pages这解释了几个看似矛盾的现象:
C2/0x53能看到完整 EC table,不代表某个LogBlock.bin里会直接出现同样的0x740字节。LogBlock.bin+0x418偶然出现0x011E,它等于maxSLC=286,但这更像 0x20-byte metadata header 里的 EC snapshot,不是完整 EC table。- 直接改某个 header snapshot 不能证明 D1、
C2/0x53和 SMART byte 5 会一起变化。 - 真正要改的是 type=3 metadata body / EC table backing,并处理 checksum、多副本、spare 和写回顺序。
系统块读写入口目前选择复用原厂路径,而不是直接移植主 ISP 内部 descriptor queue。EasyTool 普通 Flash RW 的 Read SysBlk 下拉框会从 device info / parameter table 里取系统块线性地址,再按 NAND 几何拆成 Block、Page、CH、Plane、CE、Option 等字段。Secondary MPTool 的系统块 dump/write 流程同样依赖 SM2268PARA 参数表,不是硬编码物理块。
读写 opcode 层面可以概括为:
| 操作 | 原厂 helper | 底层语义 |
|---|---|---|
| Read sysblock/page | sub_47BA84 / Secondary read wrapper | opcode 0x4E |
| Write sysblock/page | sub_47BC84 / Secondary write wrapper | opcode 0x83 |
| Erase block | erase wrapper | opcode 0x03,常见 option 0x84 |
Secondary 原厂 writer 还会检查 first-page spare ID:Info-like block 期待 0xE1,ISP block 期待 0xE4。但这里也有坑:当前复核显示它的内部 readback compare 可能最多只比较 1 byte,因此写回方案不能依赖工具 UI 上的“compare pass”。必须额外 dump 回同一个 sysblock,由本地工具对 data+spare 做完整 hash / byte compare。
证据打包:manifest 比截图更重要
有了 EasyTool hook 之后,系统块采集不应停留在“我读到了一个 bin”。每个 dump 都需要机器可读 manifest:
text
sysblk label: 1st Info / 2nd Info / 3rd Info / 1st ISP / MPInfo / Log
option: C5 00 / 95 00 / 85 00
address: Block, Page, 2KChunk, CH, CE, Plane, Sec size
raw: raw page data path, size, sha256
spare: spare path or inline spare hex, size, sha256
status: DW0, SCT/SC
notes: 是否 Read SysBlk、是否 Physical Access、是否 Plane Base Readsm2268xt2-sysblk-dump-manifest 的意义就在这里:它把 EasyTool/MPTool UI 地址字段、hook events.jsonl 里的 0x4E/0x83 buffer dump、raw/spare hash、以及 health-block-scan 结果绑到同一个 JSON。扫描器会查三类东西:
OpcodeC2.bin[0x3000..0x300B]的 6 个 summary 是否作为连续序列出现。OpcodeC2.bin[0..0x73F]的 EC table 是否 exact hit 或 fuzzy hit。- 是否存在保守的 type=3 metadata record header 候选。
现有负结论同样重要:LogBlock.bin 没有 EC table hit,没有 summary hit,也没有 type=3 record hit;只出现单个 0x011E 不能升级成 backing 证据。这个 stop sign 防止我们把一个看起来像的数值写坏成“修复方案”。
当前工程路线
现在比较稳的路线是四步。
第一步,MP2 观察。用 observe-only hook 跑 Secondary MPTool,保存 events.jsonl、summary.json 和关键 buffers。确认 host 下载的 MPISP 是否是当前包、C1/0x99 / C2/0x88 / reset/back-to-ISP 是否成功、原生 Clear SMART 是否返回 0。
第二步,只读基线。主 ISP mode 下读三套事实:NVMe SMART LID 0x02、EasyTool D1 telemetry、C2/0x53 PE/EC summary。不要用 MPISP window 的全 0 telemetry 或 UNKNOWN 结果替代主 ISP 基线。
第三步,系统块取证。用 EasyTool hook 的 listSysBlocks / readBlock 或 UI 复用链路 dump Info、ISP、MPInfo、Log 和相邻 page,保留 raw+spare,生成 manifest 和 scan bundle。OpcodeC2.bin 作为 baseline,不作为待写文件。
第四步,谨慎写回。只有当 type=3 body、checksum/spare、多副本和原厂 writer 行为都闭合后,才在目标盘自己的 dump 副本上 patch 最小字段。写回后必须立即 readback 完整比较,再 power cycle / reconnect,重新读取 D1、C2/0x53、LID 0x02。
这条路线慢,但它避免了最危险的误判:把 SMART output buffer 当成来源,把 page72+0x44C 当成当前 3% 的持久字段,把 LogBlock.bin+0x418 当成完整 EC table,或者把一个 host 返回成功当成设备状态已经持久改变。
当前结论
SM2268XT2 的 Health Percentage 研究已经从“哪个按钮能清 SMART”进入到“磨损来源如何被重建、汇总和落盘”的阶段。MP2 hook 让 Secondary MPTool 的生产流程可观测,EasyTool hook 让官方 SysBlk 读写能力可脚本化,MPISP patcher 则保留了设备侧诊断入口。三者合起来,是为了让每一步都有日志、hash、buffer 和可复核边界。
当前最强模型是:Percentage Used = 3% 更接近 EC / PE summary 与 endurance PE 的换算结果,而不是某个静态 SMART 字节。0x1028504C 仍是 SMART rebuild 输入,但不是当前寿命值的充分来源;0x10193E3C RAM EC table、0x101C9080 metadata image 和 E1/type=3 metadata record 才是后续持久化研究的主线。
对健康度机制研究和受控实验来说,这个结论很实用:不要急着写。先用 hook 把现场变成事件流,用 JSON-RPC 把系统块读成可追溯证据,用 D1 / C2/0x53 / SMART 三重读回验证每个假设。能被复现、能被回读、能过断电验证的改动,才算真正靠近了 Health Percentage 的落盘逻辑。