Repository navigation
Getting Started
This page walks through the entire path: cloning the repo, compiling firmware, flashing a real board, and getting a working console — no prior Rockchip, cross-compiling, or RT-Thread knowledge assumed.
| Requirement | Detail |
|---|---|
| Build host | Any machine that can run Docker (macOS, Linux, Windows+WSL2) |
| Flashing host | A Linux x86-64 machine or VM with real USB passthrough to the board (native Linux, or Linux running in a VM with USB passthrough — see the note below) |
| Disk | ~40 GB free for build objects |
| Cable | A USB-C data cable between the board and whichever machine is flashing it |
| Board | An RV1106 SDK board (SPI NAND, 256 MB) |
Why does flashing need a separate Linux machine? Rockchip's official flashing tool,
upgrade_tool, is a Linux x86-64 ELF binary — it does not run on macOS, and it needs direct USB access to the board that a container runtime without real USB passthrough (e.g. Docker Desktop on macOS) cannot provide. Building does not have this restriction — any Docker host works for that step. If your build machine is already Linux x86-64 with USB access, you can build and flash on the same machine.
git clone <this-repo-url> PWNCUBE-FIRMWARE
cd PWNCUBE-FIRMWARE/FirmwareThis repository excludes two large, third-party pieces to stay clone-friendly — you need both before building:
-
Toolchains (ARM
arm-rockchip830-linux-uclibcgnueabihf+ RISC-Vxpack-riscv-none-embed-gcc-10.2.0, ~2.6 GB) — extract intotoolchain/at theFirmware/root. -
rkbin(Rockchip's proprietary boot-stage binaries) — place atsrc/rkbin/.
See Firmware/docs/getting-started.md for exact download sources for your build of this SDK.
The build needs a native Docker volume, not a bind-mount to the host filesystem — the rootfs assembly step (mke2fs) needs extended attributes that bind-mounts on macOS (virtiofs) don't support.
# One-time setup
docker volume create pwncube-work
docker run -d --name pwncube-build --platform linux/amd64 \
-v "$(pwd):/sdk-src:ro" \
-v pwncube-work:/work \
-w /work ubuntu:22.04 sleep infinity
docker exec pwncube-build bash -c "apt-get update -qq && apt-get install -y -qq rsync"
# Copy the source into the container's native volume
docker exec pwncube-build bash -c "rsync -rlptD --exclude=output /sdk-src/ /work/"
# Install build dependencies (once per container)
docker exec pwncube-build bash -c "cd /work && ./build.sh deps"
docker exec pwncube-build bash -c "apt-get install -y python-is-python3 file"
# Full build: mcu -> uboot -> kernel -> packages -> rootfs -> pack
docker exec pwncube-build bash -c "cd /work && source scripts/00-setup-toolchain.sh && ./build.sh"This produces /work/output/images/update.img — the single image you flash. Copy it out of the container:
docker cp pwncube-build:/work/output/images/update.img ./output/images/update.imgIf you edit anything in the source tree afterward, re-sync before rebuilding:
docker exec pwncube-build bash -c "rsync -rlptD --exclude=output /sdk-src/ /work/"Rebuilding a single component (faster than a full rebuild — e.g. after touching just the rootfs):
docker exec pwncube-build bash -c "cd /work && source scripts/00-setup-toolchain.sh && bash scripts/03-build-rootfs.sh"
docker exec pwncube-build bash -c "cd /work && source scripts/00-setup-toolchain.sh && bash scripts/04-pack-image.sh"
⚠️ Order matters if you touch the MCU firmware:ubootembeds the MCU'srtthread.bininsideuboot.img. Always runmcubeforeuboot, or the board boots with stale MCU firmware.
If building and flashing on the same Linux x86-64 machine, skip this step. Otherwise, copy update.img to your Linux flashing host (scp, a shared folder, etc.) along with the tools/upgrade_tool binary from this repo's Firmware/tools/ directory.
On the physical board:
- Hold
BOOT. - Without releasing
BOOT, press and releaseRST. - Keep holding
BOOTfor ~5 seconds, then release.
Confirm the flashing host sees it:
sudo ./upgrade_tool LD
# expected: DevNo=1 Vid=0x2207,Pid=...,... Mode=MaskromIf nothing shows up, repeat the button sequence — Mode=Maskrom is the only reliable confirmation. (With the board already booted into Linux, you can also enter maskrom without touching buttons: reboot loader from a shell.)
Flash — always UF (full image); DI -b reports success but does not actually write to this board's SPI-NAND:
sudo ./upgrade_tool UF update.imgExpected output: Download Firmware Success / Upgrade firmware ok.
Quick sanity check that it booted, without a shell yet:
lsusb; dmesg | tail -20A healthy post-flash boot briefly shows a device idVendor=2207 idProduct=110c (USB-MSC, the U-Boot/rockusb handoff) that disconnects on its own — that's normal, not an error. If the gadget console is enabled in your build (it is, by default, in this repo), a second device then appears and stays connected: idVendor=2207 idProduct=0011 — that's your console, ready for the next step.
The board has no external UART/Ethernet by default. Command and debug access goes through a USB gadget console instead — a vendor-specific bulk interface (VID:PID 2207:0011) that lands on a menu-driven shell (pwnsat-console) once Linux has booted.
Option A — from a Linux host (the same one you used to flash, or any other):
modprobe usbserial vendor=0x2207 product=0x0011 # creates /dev/ttyUSB0
screen /dev/ttyUSB0 115200 # interactive sessionOption B — directly from macOS (or any host with pyusb), no VM needed: the gadget is a raw vendor class (0xff), so macOS won't assign it a /dev/cu.* device automatically — but you can talk to it directly over libusb:
pip3 install pyusb # once
python3 Firmware/pwncube_console.py "uname -a"
python3 Firmware/pwncube_console.py --interactiveThis is the simpler path for day-to-day use: the Linux/VM step is only needed for flashing (upgrade_tool is Linux x86-64 only) — talking to an already-flashed, already-booted board works directly from any host with pyusb.
From the console menu:
PWNSAT CONSOLE: Press the number to select.
1) Sensor / Telemetry Dashboard
2) SpaceCAN Bus Console
3) Debug Shell
4) Exit console (quiet mode)
Press M anytime to return here.
Option 3 (Debug Shell) is where radio_test, sensor_test, and everything the attack scripts drive actually run.
| Symptom | Cause |
|---|---|
upgrade_tool LD shows nothing |
Board not actually in maskrom yet — repeat the button sequence, don't retry blindly |
Download Boot Fail / please check ddr
|
Maskrom state goes stale after failed attempts — re-enter maskrom fresh and flash on the first try |
Board only shows idVendor=2207 idProduct=110c and nothing else after flashing |
The gadget console isn't coming up — confirm your build includes the pwnsat-console package and the rootfs init changes (enabled by default in this repo) |
Toolchain not configured during build |
Run source scripts/00-setup-toolchain.sh in the current terminal (it doesn't persist across shells) |
| Docker Desktop on macOS can't flash | Expected — it has no real /dev/bus/usb passthrough. Use it only for building; flash from a real Linux x86-64 machine or a VM with genuine USB passthrough (VMware Fusion/Workstation with passthrough enabled, not Docker Desktop) |