Experimental research into recovering NVMe SSDs exhibiting firmware-level write protection after FTL corruption. This is a project
- xram injection technical blog post
- overview / storytime blog post
- Full paper - and tell your friends to also write papers for fun
I used this nvme stick and the ASM2362 every day all day between 2017 and 2020 for all my laptop computing. It happpily ran Tails, ubuntu budgie, and even windows via a little usb 3 enclosure velcroed to my laptop during this time. I haven't thrown it away because it kept me safe under political duress, because I am stubborn, and because I think that it should continue to work forever.
- 256GB Silicon Power NVMe SSD connected via ASMedia ASM2362 USB bridge now exhibits silent write failure after a Windows hibernate + power loss event.
- It is impossible to write zeros to this drive. Amazing! Never seen this before.
| Operation | Reports | Actual |
|---|---|---|
dd if=/dev/zero of=/dev/sdb |
Success | Data unchanged |
wipefs --all |
Success | Signatures remain |
| SCSI READ | Success | Data readable |
| SCSI WRITE | Success | Silent failure |
| NVMe admin commands | Fail | "Medium not present" (ASC=0x3A) |
Standard Linux disk tools (fdisk, gdisk, blkdiscard, sg_format) all fail to modify the drive despite reporting success.
+---------------------------------------------+
| NVMe SSD: Silicon Power 256GB |
| Controller: Phison PS5012-E12 |
| State: FTL corrupted, read-only mode |
+----------------------+----------------------+
| M.2 PCIe
+----------------------+----------------------+
| USB Bridge: ASMedia ASM2362 |
| VID:PID = 0x174c:0x2362 |
| Passthrough: 0xe6 CDB (NVMe tunneling) |
+----------------------+----------------------+
| USB 3.1 Gen 2
+----------------------+----------------------+
| Linux: UAS driver -> /dev/sdb |
+---------------------------------------------+
The NVMe SMART/Health log contains critical warning flags at byte offset 0:
| Bit | Mask | Meaning |
|---|---|---|
| 0 | 0x01 | Available spare capacity below threshold |
| 1 | 0x02 | Temperature exceeded threshold |
| 2 | 0x04 | NVM subsystem reliability degraded |
| 3 | 0x08 | Media placed in read-only mode |
| 4 | 0x10 | Volatile memory backup failed |
Bit 3 (0x08) is our target. When set, the controller has entered firmware-level protection mode. Per NVMe spec:
"The media has been placed in read only mode. Vendor specific recovery may be required."
When NVMe admin commands fail through the USB bridge:
| Field | Value | Meaning |
|---|---|---|
| Sense Key | 0x02 | NOT READY |
| ASC | 0x3A | MEDIUM NOT PRESENT |
| ASCQ | 0x00 | - |
This is generated by the USB bridge, not the NVMe controller. The bridge returns this SCSI error when the NVMe controller doesn't respond as expected during initialization.
The ASMedia ASM2362 uses a proprietary 16-byte CDB with opcode 0xe6 to tunnel NVMe commands:
Byte 0: 0xe6 ASMedia passthrough opcode
Byte 1: NVMe opcode (0x06=Identify, 0x02=GetLog, etc.)
Byte 2: Reserved
Byte 3: CDW10[7:0]
Byte 4-5: Reserved
Byte 6: CDW10[23:16]
Byte 7: CDW10[31:24]
Bytes 8-11: CDW13 (big-endian)
Bytes 12-15: CDW12 (big-endian)
Limitation: CDW11, CDW14, CDW15 cannot be passed, restricting some admin commands.
| Opcode | Command | Status |
|---|---|---|
| 0x02 | Get Log Page | Works |
| 0x06 | Identify | Works |
| 0x09 | Set Features | Blocked by whitelist |
| 0x0A | Get Features | Blocked by whitelist |
| 0x80 | Format NVM | Blocked by whitelist |
| 0x81 | Security Receive | Blocked by whitelist |
| 0x82 | Security Send | Blocked by whitelist |
| 0x84 | Sanitize | Blocked by whitelist |
Blocked opcodes can be sent via XRAM injection (0xE4/0xE5) instead. See docs/archive/dead-ends-e6-whitelist.md for details.
NVMe 1.4+ namespace write protection states:
| Value | State | Persistence |
|---|---|---|
| 0x00 | No Write Protect | N/A |
| 0x01 | Write Protect | Survives power cycles |
| 0x02 | Write Protect Until Power Cycle | Clears on reboot |
| 0x03 | Permanent Write Protect | Irreversible |
| Protocol | Purpose |
|---|---|
| 0x00 | Discovery - list supported protocols |
| 0xEF | ATA Device Server Password |
| 0x01-0x06 | TCG Opal |
| Opcode | Direction | Command | Purpose |
|---|---|---|---|
| 0xE0 | Read | Read Config | 128 bytes bridge configuration |
| 0xE1 | Write | Write Config | Modify bridge configuration |
| 0xE2 | Read | Flash Read | Read SPI flash contents |
| 0xE3 | Write | Firmware Write | Flash firmware (from address 0x80) |
| 0xE4 | Read | XDATA Read | Read up to 255 bytes from bridge XRAM |
| 0xE5 | Write | XDATA Write | Write single byte to bridge XRAM |
| 0xE6 | Read | NVMe Admin | NVMe passthrough (only 0x02, 0x06) |
| 0xE8 | None | Reset | 0x00=CPU reset, 0x01=PCIe/soft reset |
| Address | Size | Contents |
|---|---|---|
| 0x07F0-0x07F5 | 6B | Firmware version string |
| 0xA000-0xAFFF | 4KB | NVMe I/O Submission Queue |
| 0xB000-0xB1FF | 512B | NVMe Admin Submission Queue (depth=4, slots 0-3 active) |
| 0xB200-0xB296 | ~150B | PCIe TLP engine registers |
| 0xB800-0xBBFF | 1KB | NVMe I/O Completion Queue |
| 0xBC00-0xBFFF | 1KB | NVMe Admin Completion Queue |
| 0xF000-0xFFFF | 4KB | NVMe data buffer (Identify cache) |
- CPU: 8051-compatible core, ~114.3 MHz
- XRAM: 64KB mapped memory
- No firmware signature verification
- UART debug: 921600 8N1, 3.3V (pins 62/63)
Source: cyrozap/usb-to-pcie-re
The drive was successfully recovered via XRAM-based NVMe command injection. Sanitize Block Erase (SANACT=2), injected directly into the Admin Submission Queue at XRAM 0xB000 and triggered via PCIe TLP doorbell, cleared the FTL corruption and restored full write capability.
| Approach | Result |
|---|---|
| XRAM SQ injection + PCIe TLP doorbell | Sanitize Block Erase succeeded, FTL rebuilt |
| Sanitize Block Erase (opcode 0x84, SANACT=2) | Cleared write protection |
| BOT mode (usb-storage quirks) | Required for 0xE4/0xE5 vendor commands |
| Approach | Why |
|---|---|
| 0xE6 passthrough for Format/Sanitize | Silently dropped by bridge firmware whitelist |
| Format NVM via injection | Accepted by controller but didn't clear FTL corruption |
| Sanitize Crypto Erase | Not supported (SANICAP=0x02, block erase only) |
| Wine + SP Toolbox | Wine lacks IOCTL_SCSI_PASS_THROUGH_DIRECT |
| Set Features 0x84 | Separate mechanism from firmware read-only mode |
| Injection to SQ slots 4-7 | Admin SQ depth is 4, not 8 — slots beyond 3 are ignored |
The XRAM buffer at 0xB000-0xB1FF has room for 8 entries (512 bytes), but the firmware configures a queue depth of 4. Commands must be injected into slots 0-3 only. The SQHD field in the Completion Queue reveals the actual depth.
See docs/research/deep-research-synthesis.md for the full research history.
Requires Zig 0.13.0 or later.
zig buildsudo ./zig-out/bin/asm2362-tool probe /dev/sdX # Detect bridge type
sudo ./zig-out/bin/asm2362-tool identify /dev/sdX # NVMe Identify Controller
sudo ./zig-out/bin/asm2362-tool smart /dev/sdX --json # SMART log (JSON output)# Read-only probe — tests 0xE4 support, dumps Admin SQ, MMIO, data buffer
sudo ./zig-out/bin/asm2362-tool xram-probe /dev/sdX
# Raw XRAM read/write
sudo ./zig-out/bin/asm2362-tool xram-read --addr=0xB000 --len=64 /dev/sdX
sudo ./zig-out/bin/asm2362-tool xram-dump --addr=0xB000 --len=512 /dev/sdX
sudo ./zig-out/bin/asm2362-tool xram-write --addr=0xB000 --byte=0x00 /dev/sdX
# NVMe command injection via XRAM (dry-run by default)
sudo ./zig-out/bin/asm2362-tool inject --inject-cmd=format /dev/sdX # dry-run
sudo ./zig-out/bin/asm2362-tool inject --inject-cmd=format --force /dev/sdX # live
# Bridge reset
sudo ./zig-out/bin/asm2362-tool reset --reset-type=1 /dev/sdX # PCIe soft resetzig build test # main.zig tests
zig build test-all # all module tests (sg_io, sense, passthrough, replay, xram)| Component | Status |
|---|---|
| SCSI SG_IO layer | Complete |
| ASM2362 0xe6 passthrough | Complete (Identify + SMART work; others blocked by whitelist) |
| XRAM access (0xE4/0xE5/0xE8) | Complete |
| XRAM NVMe command injection | Complete (dry-run default, doorbell via PCIe reset) |
| NVMe Identify + SMART | Complete |
| Format NVM / Sanitize (via 0xe6) | Archived — dead end |
| Frida hooks for Windows | Complete |
| Command replay | Complete |
~4,000 lines of Zig with 35 unit tests.
src/
main.zig CLI entry point
scsi/ SG_IO wrapper, sense parsing
asm2362/
passthrough.zig 0xe6 CDB passthrough (Identify, SMART only)
xram.zig 0xE4/0xE5/0xE8 XRAM access, NVMe SQ injection, PCIe TLP doorbell
commands.zig NVMe command helpers (SMART log parsing)
nvme/ NVMe command implementations (identify)
frida/ Windows DeviceIoControl hooks + replay
analysis/ Capability probing
docs/
blog/ Blog posts (mdsvex-compatible .md)
paper/ LaTeX technical paper
notes/ Research findings
research/ Deep research synthesis
archive/ Dead-end code + writeups (format.zig, sanitize.zig)
See docs/INDEX.md for full documentation navigation.
- cyrozap/usb-to-pcie-re -- ASM2362 firmware RE, XRAM map, vendor commands
- NVMe Base Specification 2.0 -- Admin commands, queue structures, doorbell registers
- smartmontools sntasmedia -- Reference 0xe6 implementation
- smx-smx/ASMTool -- ASMedia firmware dumper
- sg3_utils -- SG_IO ioctl reference
Research code - use at your own risk. No warranty implied.