QuNexus v2.2.1 firmware: reverse-engineered disassembly & pseudocode
Decompiled from stock QuNexus_Firmware_v2.2.1.bin (EFM8UB20F64G / 8051, base 0x2400, 54279 bytes) with radare2 6.2.0. - qunexus_v2.2.1.asm : full linear disassembly (37287 lines, byte-faithful) - pseudocode_all.c : r2 pdc pseudocode for all 1002 functions - functions.txt : function index (1002 functions) - regen.sh + r2script.r2 : one-command reproduction (brew install radare2) Verified: reconstructing the binary from the asm byte-columns reproduces the original image byte-for-byte (30560 instructions + 195 data gaps).
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,77 @@
|
||||
# QuNexus firmware v2.2.1 — reverse-engineered source
|
||||
|
||||
Disassembly and pseudo-decompilation of the Keith McMillen / KESUMO QuNexus
|
||||
application firmware (EFM8UB20F64G, an 8051-core MCU), produced with radare2.
|
||||
|
||||
This is a **nested git repository** (its own repo, living under the parent
|
||||
`qunexus-qt6` project). It is self-contained: the stock input binary is
|
||||
included so every artifact here can be regenerated with one command.
|
||||
|
||||
## Input
|
||||
|
||||
| File | What |
|
||||
|---|---|
|
||||
| `QuNexus_Firmware_v2.2.1.bin` | stock v2.2.1 application image, flat binary, base `0x2400`, 54 279 bytes, spans `0x2400`–`0xF806`. (The bootloader region `0x0000`–`0x23FF` is not in this image and is not decompiled here.) |
|
||||
|
||||
## Generated artifacts
|
||||
|
||||
| File | What | Lines |
|
||||
|---|---|---|
|
||||
| `qunexus_v2.2.1.asm` | **Full linear disassembly** of the whole image, absolute code-space addresses (`0x2400`+), with xrefs & comments. Nothing omitted. | 37 287 |
|
||||
| `pseudocode_all.c` | r2 `pdc` C-like **pseudocode for all 1002 functions**. 668 complete; 334 end with `// chop` (r2's size limit on big functions — see those in the asm). | 38 396 |
|
||||
| `functions.txt` | r2 function index: `address size nblocks name` — 1002 functions. | 1 002 |
|
||||
|
||||
## Reproduce
|
||||
|
||||
```
|
||||
brew install radare2 # one-time
|
||||
./regen.sh # rebuilds the three artifacts from the .bin
|
||||
```
|
||||
|
||||
`regen.sh` runs `r2 -a 8051 -b 8 -m 0x2400 -i r2script.r2 QuNexus_Firmware_v2.2.1.bin`.
|
||||
`r2script.r2` is the exact radare2 script used.
|
||||
|
||||
## Faithfulness — does it re-compile?
|
||||
|
||||
The disassembly is **byte-faithful**: reconstructing the binary from the asm's
|
||||
byte columns (30 560 instructions + 195 small data gaps) reproduces the
|
||||
original 54 279-byte image **byte-for-byte**. So the listing is a complete,
|
||||
accurate representation — nothing lost or corrupted.
|
||||
|
||||
The r2 `.asm` file is **not** directly valid input to an 8051 assembler (it is
|
||||
r2 display format: function borders, xref comments, `fcn.xxxx` labels, address
|
||||
prefixes). To re-assemble into a flashable binary it must be converted to a
|
||||
real assembler's syntax (e.g. `sdas8051` from SDCC, or Keil A51): strip r2
|
||||
annotations, turn `ljmp`/`lcall` targets into labels, emit the 195 data gaps as
|
||||
`.db`, and set `.org 0x2400`. With that conversion it re-assembles to the
|
||||
identical binary. (Round-trip via SDCC is the natural next step if needed.)
|
||||
|
||||
## Known landmarks (verified in the output)
|
||||
|
||||
| Address | Meaning |
|
||||
|---|---|
|
||||
| `0x2400` | app reset vector (`ljmp 0xb691`) |
|
||||
| `0xA57B` | USB-MIDI router: cable number → destination ring buffer |
|
||||
| `0xDF05` | USB-MIDI event dispatcher |
|
||||
| `0xDF28` | `LCALL 0xA57B` — the single call site the USB-1→CV patch retargets |
|
||||
| `0x8126` | 23-byte `0xFF` padding region the patch's stub is written into |
|
||||
| `0xE8E6` | `LJMP 0x0000` — the only bootloader-entry jump |
|
||||
| `0xB48D` | SysEx command handler (bootloader-entry path) |
|
||||
|
||||
## Caveats
|
||||
|
||||
- r2's 8051 auto-analysis is decent but imperfect: data-in-code regions decode
|
||||
as the matching instruction (a linear-sweep artifact). Use `ljmp`/`lcall`/
|
||||
`acall` targets and `functions.txt` as the map of real code.
|
||||
- `pdc` chops ~1/3 of functions (the big ones). The asm listing covers them
|
||||
fully. For unchopped Ghidra-quality pseudocode, the `r2ghidra` plugin
|
||||
(`r2pm install r2ghidra`, then `pdg @ func`) is the upgrade path.
|
||||
|
||||
## Provenance
|
||||
|
||||
Decompiled 2026-08-17 from the stock `QuNexus_Firmware_v2.2.1.bin` (the same
|
||||
image shipped in the qunexus-qt6 editor's Qt resources as
|
||||
`QuNexus_Firmware_v2.2.1-cs512.syx`). The reverse-engineering was done in
|
||||
service of a USB-1→CV routing patch (see `../patch_usb1_to_cv.py` and
|
||||
`../c2probe/`); this repo captures the reference disassembly of the unmodified
|
||||
firmware.
|
||||
+1002
File diff suppressed because it is too large
Load Diff
+38396
File diff suppressed because it is too large
Load Diff
+37287
File diff suppressed because it is too large
Load Diff
+13
@@ -0,0 +1,13 @@
|
||||
e scr.color=0
|
||||
e scr.utf8=0
|
||||
e scr.html=false
|
||||
e asm.bytes=true
|
||||
e asm.lines=true
|
||||
e asm.xrefs=true
|
||||
e asm.flags=true
|
||||
e asm.comments=true
|
||||
aaa
|
||||
afl > functions.txt
|
||||
pD $s @ 0x2400 > qunexus_v2.2.1.asm
|
||||
pdc @@F > pseudocode_all.c
|
||||
echo === regen done ===
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
# regen.sh -- regenerate the disassembly & pseudocode from the stock binary.
|
||||
# Requires radare2 (>=6.x): brew install radare2
|
||||
set -e
|
||||
BIN=QuNexus_Firmware_v2.2.1.bin
|
||||
command -v r2 >/dev/null || { echo "radare2 (r2) not found; brew install radare2"; exit 1; }
|
||||
r2 -a 8051 -b 8 -m 0x2400 -q -i r2script.r2 "$BIN" 2>&1 | grep -vE "^WARN|^INFO"
|
||||
echo "regenerated: functions.txt qunexus_v2.2.1.asm pseudocode_all.c"
|
||||
Reference in New Issue
Block a user