Skip to content
KitploitKITPLOIT
工具博客
Log in
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
工具/GitHubGitHub/will127534/mk4001mtd-usb-bridge
嵌入式系统安全逆向工程数据恢复硬件黑客硬件安全硬件与物联网安全固件分析
GitHubwill127534/mk4001mtd-usb-bridge

MK4001MTD-USB-Bridge

RP2040固件,将东芝MK4001MTD 0.85英寸SDIO微型硬盘桥接为USB大容量存储设备,从头实现完整的SDIO-ATA协议栈,支持PIO加速的读写及坏道恢复。

查看仓库
4212172个月前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

MK4001MTD USB 桥接器

RP2040 Pico 固件,将东芝 MK4001MTD 0.85英寸 SDIO 微型硬盘桥接为 USB 大容量存储设备。 _DSC1170 _DSC1354

MK4001MTD 是一款 4 GB 微型硬盘,最初用于诺基亚 N91 音乐手机和一些其他设备,如 MP3 播放器或 U 盘,当时闪存仍然相当昂贵。

你可能见过一些介绍声称这款硬盘使用 MMC 协议,但实际上这是不正确的。我对此进行了一段时间的研究:我尝试构建一个 8 位 MMCplus 读卡器,并测试了不同的 SD/MMC 读卡器,但没有成功。最后,我买了一部诺基亚 N91 来捕获逻辑信号,并确认它实际使用的协议。

这是我尝试将其与我的 8 位 MMCPlus 读卡器板一起使用时的照片,结果发现它不是 MMC :( _DSC0484

所以我最终购买了 N91 来收集信号: _DSC1093 _DSC1131

与标准的 ATA/CF 微型硬盘不同,它使用 SDIO 接口,通过 CMD52/CMD53 隧道传输 ATA 命令。没有现有的驱动程序支持此协议,因此此固件从头实现了完整的协议栈。

这让我感到惊讶,因为有一个叫 CE-ATA 的 SDIO 转 ATA 标准。但如果你仔细查看发布时间线,CE-ATA 出现在这款硬盘之后。因此,这款硬盘完全依赖 SDIO 命令,而 CE-ATA 不可用。CE-ATA 有两条新命令 CMD60/CMD61,并使用 CMD12/39,但从追踪信号可以看出,它并没有使用这些命令。

第二个硬件要点是,另一个流传的错误信息——声称它是 8 位 MMCPlus 卡——不仅不真实,而且引脚排列也不符合 MMC 标准。你可以在诺基亚 N91 维修手册中找到一些关于引脚排列的文档:虽然引脚编号遵循 MMCPlus 标准,但引脚映射不遵循。如果你自己接线,这是一个重要的细节:它使用相同的 MMC 连接器,但引脚映射不同,更多信息在硬件部分。

最后,请注意这是与 Claude/OpenClaw 共同开发的。我手动收集了逻辑信号,并为 OpenClaw 搭建了一个闭环测试站,以便迭代开发——分析信号并实现功能。文档大部分由 Claude 编写;我还会在文档中添加我的注释。我自己也阅读并仔细检查了文档,它应该是可靠且易于理解的。

关于 N91 信号分析的详细信息,请参阅 /docs/N91_TRACE_ANALYSIS.md,我还将 N91 维修手册和原始逻辑信号放在那里。

更多内容请参见博客文章:https://www.willwhang.dev/Reading-MK4001MTD/
实际运行视频:https://youtu.be/GC4xil3_Bbc

状态

功能完整的 USB 大容量存储,具有 PIO 加速读写和空闲电源管理。

指标数值
读取速度~985 kB/s(受 USB 全速限制)
写入速度~920 kB/s(受 USB 全速限制,支持写入缓存)
原始SDIO侧速度~2.35 MB/s 读取 / ~2.15 MB/s 写入(受硬盘限制)
容量3.75 GB(7,862,400 扇区)
文件系统FAT32 已验证(挂载/卸载/fsck 干净)
数据完整性写入+回读已验证;所有 4 条 DAT 线路上均有每块 CRC16
空闲待机空闲 5 秒或 USB 挂起 → STANDBY IMMEDIATE + 电源门控

工作原理

架构

USB 主机 ←→ USB MSC (TinyUSB) ←→ ATA 层 ←→ SDIO 层 (PIO) ←→ MK4001MTD

固件包含四层:

  1. USB MSC (msc_device.c) — TinyUSB 大容量存储类。将 SCSI READ(10)/WRITE(10) 转换为 ATA 扇区操作。32 KB EP 缓冲区,每 USB 传输可批处理多达 64 个扇区。硬盘 I/O 与 USB 双向重叠,类似于具有缓存磁盘的真实 ATA-USB 桥接器:顺序读取预取在将前一块流式传输到主机的同时获取下一块,写入在 USB 接收下一块的同时进行暂存和刷新。设备通告其写入缓存(缓存模式页面,WCE=1 — 主机报告“写入缓存:已启用”并在 fsync/卸载/挂起时发出 SYNCHRONIZE CACHE,固件会执行此命令)。后台刷新失败会在下一次 WRITE 或 SYNCHRONIZE CACHE 时显示为 MEDIUM ERROR;对已知坏扇区的写入采用严格的同步路径。

  2. ATA-over-SDIO (ata_sdio.c) — 通过将 ATA 寄存器写入映射到 SDIO 功能 1 地址空间的 CMD52 来实现 ATA 命令(IDENTIFY、READ SECTORS、WRITE SECTORS),并通过 CMD53 传输扇区数据。在 CMD、数据和 ATA 级别采用 3 层重试逻辑。

  3. PIO SDIO (sdio_pio.c, sdio.pio) — 使用 RP2040 的 PIO 外设(4 位总线,10 MHz,每个位 4 个 PIO 周期,绕过输入同步器)实现硬件加速的 SDIO。三个 PIO 程序通过动态程序交换共享一个状态机:

    • CMD 发送/接收(24 条指令)— 发送 SDIO 命令并接收响应
    • DAT 读取(12 条指令)— 通过字节交换 DMA(无需 CPU 重新打包)从 4 位 DAT 总线读取数据块;块 N 的 CRC 验证与块 N+1 的流式传输同时进行
    • DAT 写入(14 条指令)— 通过 DMA 将数据块写入 4 位 DAT 总线,包含内置 CRC 状态接收和忙等待;块 N+1 的半字节流在块 N 传输时构建
  4. 引脚/电源 (sdio_hw.c) — GPIO 初始化和硬盘电源控制。所有 SDIO 通信均使用 PIO。

人工备注:有趣的是,Claude 非常不情愿在 PIO 中实现 SDIO,许多开发周期都在 PIO 和位碰撞之间来回浪费。

SDIO-ATA 协议

MK4001MTD 将自身呈现为带有一个 I/O 功能的 SDIO 卡。标准 SDIO 卡初始化(CMD5/CMD3/CMD7)设置总线,然后通过 SDIO 命令访问 ATA 寄存器:

寄存器访问 (CMD52): 每个 ATA 寄存器映射到一个功能 1 地址:

地址寄存器用途
0x00DATACMD53 扇区数据目标地址
0x01ERR/FEAT错误(读取)/ 特性(写入)
0x02SECCOUNT扇区计数
0x03LBA_LOLBA 位 0-7
0x04LBA_MIDLBA 位 8-15
0x05LBA_HILBA 位 16-23
0x06DEV/HEAD设备/磁头 + LBA 位 24-27
0x07CMD/STATUS命令(写入)/ 状态(读取)

数据传输 (CMD53): 扇区数据通过以块模式发出针对 DATA 寄存器(地址 0x00)的 CMD53 传输。对于多扇区读取,单个 block_count=N 的 CMD53 在一个 SDIO 多块事务中传输 N × 512 字节。

中断信号: 硬盘通过置位 SDIO 中断(CCCR 寄存器 0x05 中的 INT_PENDING 位 1)来指示扇区准备就绪。读取 ATA STATUS 寄存器会清除中断。

读取路径(多块 PIO)

对于 16 扇区读取:

1. 通过 PIO CMD52 写入 ATA 寄存器:
     SECCOUNT=16, LBA_LO/MID/HI, DEV/HEAD=0xE0, CMD=0x20

2. 通过 CMD52 轮询 STATUS 直到 DRQ(位 3)置位

3. 交换 PIO 到 DAT 读取程序
4. 发送 CMD53:block_mode=1, fn=1, addr=0x0000, block_count=16

5. PIO DAT 读取:对于 16 个块中的每一个:
   a. 等待起始位(所有 DAT 线为低)
   b. DMA 从 PIO RX FIFO 读取 1024 个半字节(512 字节)到缓冲区
   c. 等待 SM 完成时钟 CRC+结束半字节(轮询 SM PC)
   d. 就地重新打包半字节 → 字节

6. 交换 PIO 回到 CMD 程序

写入路径(多块 PIO)

对于 16 扇区写入:

1. 通过 PIO CMD52 写入 ATA 寄存器:
     SECCOUNT=16, LBA, DEV/HEAD=0xE0, CMD=0x30

2. 通过 CMD52 轮询 STATUS 直到 DRQ(位 3)置位
   (STATUS 0xD8 = BSY+DRQ 视为 DRQ 就绪,根据 N91 信号)

3. 交换 PIO 到 DAT 写入程序
4. 发送 CMD53:block_mode=1, fn=1, addr=0x0000, block_count=16

5. PIO DAT 写入:对于 16 个块中的每一个:
   a. 预计算每条 DAT 线的 CRC16-CCITT(4 个独立 CRC)
   b. 构建半字节流:start(0x0) + data(1024 半字节) + CRC(16) + end(0xF)
   c. DMA 半字节流到 PIO TX FIFO
   d. PIO 时钟输出所有半字节,然后:
      - 将 DAT 切换为输入
      - 时钟 16 个周期以从卡接收 CRC 状态
      - 轮询 DAT0 直到卡释放忙状态
      - 触发 IRQ 0 以指示块完成

6. 交换 PIO 回到 CMD 程序

PIO 程序交换

RP2040 PIO 每个块有 32 个指令槽。我们的三个程序总共需要 55 条指令,因此它们无法共存。相反,使用 PIO0 上的单个 SM0,通过直接写入 PIO 指令内存来交换程序:

static void load_program_raw(const pio_program_t *program) {
    for (uint i = 0; i < program->length; i++)
        pio->instr_mem[FIXED_OFFSET + i] = program->instructions[i];
}

这绕过了 SDK 的 pio_add_program/pio_remove_program 分配器。程序交换大约需要 1 µs。每次交换后都需要执行特定于程序的重新初始化,设置引脚映射、移位方向和时钟分频器。

电源管理

对诺基亚 N91 逻辑信号的分析显示了激进的电源管理:

  • 空闲模式: 即使没有 I/O,也大约每 7.5 秒 执行 STANDBY IMMEDIATE (0xE0)。每次待机都会触发完整的 SDIO 重新初始化(CMD5 重试 → CMD3 → CMD7 → CCCR 设置)。在一个空闲会话中观察到了 28 个待机周期。
  • 活动模式: 在 I/O 突发之间发出待机(在 USB 驱动器文件操作期间观察到了 24 个周期)。
  • 未观察到其他电源命令(IDLE、SLEEP、CHECK POWER MODE)或 CCCR 电源寄存器访问。

固件使用可配置的空闲超时复制了此行为:

#define IDLE_STANDBY_MS 5000  // 在 main.c 中

两种路径触发硬盘电源门控:

  1. 空闲超时(5 秒)— 主循环检测到无 I/O 活动
  2. USB 挂起 — 主机挂起 USB 端口

两种路径都发送 ATA STANDBY IMMEDIATE (0xE0) 以刷新写入缓存并停放磁头,然后通过 GP9 切断电源。

唤醒序列(由首次 READ/WRITE 触发):

  1. 硬盘上电,等待 500ms 等待电源轨稳定
  2. 基于 PIO 的 SDIO 重新初始化:CMD5 (OCR) → 10ms 稳定 → CMD3 (RCA) → CMD7 (选择)
  3. 切换到快速 PIO 时钟,通过 CMD52 配置 CCCR(4 位总线,512B 块,fn1 使能)
  4. 轮询 fn1 就绪(CCCR IO_READY 位 1)
  5. N91 风格的 30ms DRDY 状态轮询,直到驱动器就绪 ATA

坏扇区处理

当多扇区传输遇到坏扇区时:

  1. 块读取失败 → 错误恢复(IO_ABORT + fn1 复位,~500ms)
  2. 回退到逐扇区 I/O 以精确定位失败块
  3. ATA 层等待足够长的时间以捕获最终的 STATUS/ERROR 位,而不是将失败归结为通用的 DRQ timeout
  4. 任何未恢复的块立即导致 SCSI 命令失败,返回 MEDIUM ERROR(读取:03/11/00,写入:03/0C/00)
  5. 缓存坏扇区 LBA → 重复读取快速失败,而不重新访问硬盘(针对主机重试风暴的反锤击保护)
  6. 写入始终接触介质,根据 SBC 标准 — 对缓存为坏的 LBA 的成功写入会将其从缓存中清除,就像驱动器清除待处理扇区一样

第 6 点并非理论:该驱动器在 LBA 1952 处存在一个长期不可读的扇区(READ: ST=0x51 ERR ERR=0x40 UNC)。一旦桥接器允许实际写入到达该扇区,驱动器就会重写该扇区,并且此后读取一直正常:

[ATA] FAST-RD: ST=0x51 ERR ERR=0x40 UNC LBA=1952
[MSC] BAD SECTOR read LBA=1952
[MSC] Bad sector LBA=1952 repaired by write

编译

先决条件

  • Raspberry Pi Pico SDK — 未修改的发行版,锁定在 2.2.0
  • ARM 工具链 (arm-none-eabi-gcc)
  • CMake

SDK 版本是锁定的:如果设置了 PICO_SDK_PATH(环境变量或 CMake 变量),则使用该路径,并检查其版本是否与锁定的版本匹配——如果不匹配,配置失败并显示说明(可通过 -DMK4001_ALLOW_SDK_MISMATCH=ON 覆盖)。如果完全没有 PICO_SDK_PATH,则会在配置时自动从 GitHub 获取锁定的 SDK 版本,因此简单的 git clone && cmake && make 是完全可重现的。

该固件需要一个修补过的 TinyUSB MSC 类驱动程序(在读取/写入错误时保留应用程序感知数据 + 一个带有 WCE=1 的缓存模式页面)。该文件已在此仓库中供应商化,位于 lib/tinyusb_patched/msc_device.c — 构建会自动编译该文件而不是 SDK 中的副本,因此永远不需要修改 SDK。与上游 TinyUSB(0.18.0,随 pico-sdk 2.2.0 捆绑)的差异位于 lib/tinyusb_patched/;SDK 锁定正是因为这个供应商化文件必须与 SDK 的 TinyUSB 版本保持一致。

编译与烧录

cd /home/pi/mk4001_bridge/build
cmake ..
make -j4

sudo openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg \
  -c "adapter speed 1000" -c "init" -c "reset halt" -c "sleep 200" \
  -c "program /home/pi/mk4001_bridge/build/mk4001_bridge.elf verify" \
  -c "reset run" -c "exit"

硬件接线

下载工具