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

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

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

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

工具目录

分类

查看所有分类
Loading categories
MK4001MTD-USB-Bridge — RP2040固件,将东芝MK4001MTD 0.85英寸SDIO微型硬盘桥接为USB大容量存储设备,从头实现完整的SDIO-ATA协议栈,支持PIO加速的读写及坏道恢复。 | Kitploit
工具/GitHubGitHub/will127534/mk4001mtd-usb-bridge
嵌入式系统安全逆向工程数据恢复硬件黑客硬件安全硬件与物联网安全固件分析
GitHubwill127534/mk4001mtd-usb-bridge

MK4001MTD-USB-Bridge

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

查看仓库
42121个月前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 加速读写和空闲电源管理。

工作原理

架构

root@kitploit:~
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 地址:

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

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

读取路径(多块 PIO)

对于 16 扇区读取:

root@kitploit:~
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 扇区写入:

root@kitploit:~
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 指令内存来交换程序:

root@kitploit:~
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 电源寄存器访问。

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

root@kitploit:~
#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)。一旦桥接器允许实际写入到达该扇区,驱动器就会重写该扇区,并且此后读取一直正常:

root@kitploit:~
[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 版本保持一致。

编译与烧录

root@kitploit:~
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"

硬件接线

注意: 该特定 Pico 单元上的 GP0 和 GP1 已损坏。所有 SDIO 引脚分配均偏移了 +2。

人工备注:Claude 在这里出错了,因为它没有意识到 GP0 和 GP1 在其构建配置中用于 UART 终端。它反复忘记这一点,以至于我干脆将 SDIO GPIO 移出了那个 UART。

HDD_PWR 并非必需。你不必通过掉电重启来使用硬盘;它更像是开发便利功能,用于在大量内容硬编码时重置硬盘。也就是说,如果你想要省电,可以使用那个信号,但硬盘在热复位下也可以正常工作。

你将通过 UART 看到调试消息。它们不通过 USB-CDC,因为对于 Claude 来说,设置一个独立的 UART 转 USB 日志链路更容易,该链路在早期开发期间不会断开或变得不稳定。

UART 日志还每 30 秒报告一次驱动器温度(当驱动器活动时):[TEMP] drive temperature: 29 C。该传感器是通过逆向工程东芝供应商命令 0xC2 发现的——N91 在每个驱动器会话开始时读取它,以强制执行其 HDD 工作温度限制。详细信息请参见 docs/N91_TRACE_ANALYSIS.md 第 4 节。

以下是日志示例:

root@kitploit:~
========================================
  MK4001MTD USB Bridge v0.11
  SDIO-ATA → USB Mass Storage (PIO)
========================================

[MAIN] Pre-delay 5000ms...
[PIO] Init OK: clkdiv=3.12 (~10.0 MHz), CMD@0
[MAIN] Power cycling HDD...
[SDIO] HDD power OFF
[SDIO] HDD power ON
[MAIN] SDIO init (PIO)...
[SDIO] CMD5 ready (OCR=0x901F8000)
[SDIO] RCA=0x0001
[SDIO] fn1 ready (attempt 0)
[MAIN] ATA IDENTIFY...
[ATA] IDENTIFY complete
Model:    [TOSHIBA MK4001MTD]
Serial:   [           763B004HA]
Firmware: [VH173A]
Sectors:  7862400 (3839 MB)
SMART:    not supported (supported=0, enabled=0)
IDENTIFY: W0=0040 W47=0000 W49=0000 W59=0000
  ATA W80=0000  Cmd W82=0000 W83=0000 W84=0000
  En  W85=0000 W86=0000 W87=0000  W89=0008 W128=0001

[DIAG] === Drive Diagnostics ===
[DIAG] Standard SMART: not supported (IDENTIFY W82 bit0 = 0)
[DIAG] Toshiba vendor CMD 0xC2:
  FEAT=0x01 unknown_01                       → SC=00 LBA=02/00/00 ST=50
  FEAT=0x02 unknown_02                       â SC=00 LBA=02/00/00 ST=50
  FEAT=0x03 unknown_03                       → SC=00 LBA=02/00/00 ST=50
  FEAT=0x04 unknown_04                       → SC=00 LBA=02/00/00 ST=50
  FEAT=0x10 diag_10 (LBA_LO varies)          → SC=00 LBA=00/00/00 ST=50
  FEAT=0x11 diag_11                          → SC=00 LBA=00/00/00 ST=50
  FEAT=0x12 diag_12 (LBA_LO varies)          → SC=00 LBA=01/00/00 ST=50
  FEAT=0x20 query_20 (N91: SC=0xFF always)   → SC=FE LBA=00/FF/00 ST=50
  FEAT=0x21 query_21 (N91: SC varies per boot) → SC=1B LBA=00/FF/00 ST=50

[MAIN] MBR: valid 0x55AA
[MAIN] Warming up...
[MAIN] PIO OK, STATUS=0x50
[MAIN] Drive: 7862400 sectors (3839 MB)
[MAIN] Ready.
[PWR] Idle 5000ms → STANDBY + power gate
[PWR] STANDBY IMMEDIATE → power gate
[SDIO] HDD power OFF

最后,以下是到实际硬盘的接线。
_DSC1176-2 这是从 N91 原理图中截取的部分,你也可以映射引脚编号。 image

附注:这是一个 3V 驱动器,但我认为 3.3V 也可以,主要是为了省去电平转换的工作。

专门为此驱动器设计的硬件位于 /hardware! image

源文件

版本历史

测试

root@kitploit:~
# 检查设备是否出现
lsblk -dno NAME,MODEL | grep MK4001

# 文件系统测试 — 挂载、复制文件、验证
sudo mount /dev/sdX1 /mnt/mk4001
cp /tmp/testfile /mnt/mk4001/
sync
md5sum /tmp/testfile /mnt/mk4001/testfile    # 应一致
sudo umount /mnt/mk4001

# 速度基准测试(原始设备,请勿先挂载 — 会损坏文件系统)
# 使用文件系统之后的安全偏移或未分区的驱动器
sudo dd if=/dev/sdX of=/dev/null bs=64k count=128 iflag=direct     # 读取
sudo dd if=/dev/zero of=/dev/sdX bs=64k count=64 oflag=direct seek=1024  # 写入(偏移过 FS)

人工备注,有趣的事实:当它第一次开始速度测试时,它实际上直接 dd 到驱动器并损坏了文件系统..... 谢天谢地,在开发过程中这并不太重要,但请始终记住当你操作 OpenClaw 的配置时要小心。

许可证

我不在意。

下载工具
指标数值
读取速度~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 + 电源门控
地址寄存器用途
0x00DATACMD53 扇区数据目标地址
0x01ERR/FEAT错误(读取)/ 特性(写入)
0x02SECCOUNT扇区计数
0x03LBA_LOLBA 位 0-7
0x04LBA_MIDLBA 位 8-15
0x05LBA_HILBA 位 16-23
0x06DEV/HEAD设备/磁头 + LBA 位 24-27
0x07CMD/STATUS命令(写入)/ 状态(读取)
Pico GPIO功能备注
GP2SDIO_CLK主机时钟输出
GP3SDIO_CMD双向命令线
GP4SDIO_DAT0数据位 0
GP5SDIO_DAT1数据位 1
GP6SDIO_DAT2数据位 2
GP7SDIO_DAT3数据位 3
GP9HDD_EN驱动器电源使能(高电平=开)
GP12UART TX调试输出 @ 115200
GP13UART RX调试输入
GP16LED:硬盘电源低电平有效
GP17LED:硬盘正常低电平有效
GP18LED:读取低电平有效
GP19LED:写入低电平有效
文件行数用途
main.c210初始化、空闲待机、USB 挂起/恢复
msc_device.c400USB MSC 回调、电源门控唤醒、坏扇区缓存
ata_sdio.c390ATA 命令、错误恢复、供应商诊断
sdio_pio.c635PIO SDIO:CMD52、CMD53 读/写、程序交换、CRC16
sdio_hw.c45引脚初始化 + 硬盘电源控制
sdio.pio200PIO 汇编 + C SDK 初始化助手
led.h37LED 助手(GP16–GP19,低电平有效)
usb_descriptors.c77USB 设备/配置/字符串描述符
tusb_config.h20TinyUSB 配置(MSC,32KB EP 缓冲区)
版本读取写入关键变化
v0.1–v0.3105 kB/s93 kB/s位碰撞 SDIO、CRC16、重试逻辑
v0.5374 kB/s—单 SM PIO、直接指令内存交换
v0.6583 kB/s93 kB/s多块 CMD53 读取、CRC 时钟耗尽修复
v0.8588 kB/s274 kB/sPIO 写入、OSR 刷新修复
v0.9475 kB/s371 kB/s64 扇区块、CRC16 读取验证
v0.10453 kB/s329 kB/sLED 重新映射、HDD EN 引脚、GP12/GP13 上的 UART
v0.11~450 kB/s~340 kB/sHDD 电源门控、PIO 唤醒、坏扇区感知、USB 挂起
v0.12~985 kB/s~920 kB/s驱动器/USB 重叠(读取预取 + 带写后缓存的通告写入缓存)、流水线 PIO 块、bswap DMA、4 周期 PIO 循环、SBC 风格坏扇区语义(写修复)、供应商化 TinyUSB MSC 驱动程序