ESPM Overview¶
ESPM (the Espilon Portable Module system) is the dynamic code loader at the heart of the platform. It turns a hollow-shell ESP32 firmware into a runtime-extensible system: every offensive, recon, or utility capability arrives as a signed, relocatable ELF object pushed over the encrypted Management Protocol channel, loaded into RAM, executed, and erased. Nothing capability-bearing lives in flash at rest.
ESPM is not a git submodule
ESPM lives at espilon-test/ESPM/ and is copied into the firmware at
Espilon-Firmware/components/espm/. Older documentation that called it a submodule is stale.
At a glance¶
| Property | Value |
|---|---|
| Version | v0.2.0 |
| Syscalls | 102, grouped in ~20 categories (see Syscall Reference) |
| Module format | Relocatable ELF object (ET_REL), Xtensa or RISC-V |
| Signature | HMAC-SHA256, 32-byte tag prepended to the ELF |
| Execution | One FreeRTOS task per module, runs from IRAM (or PSRAM on S3) |
| Isolation | Per-module watchdog (30 s default) + Xtensa panic isolation |
| Concurrency | Up to 8 modules (ESPM_MAX_MODULES) |
| Size limits | 64 KB code, 32 KB data per module |
What ESPM gives you¶
- A stable syscall ABI. Modules call firmware services (sockets, WiFi, BLE, GPIO, crypto, C2 messaging) through a function-pointer table (
espm_sys_t) rather than linking against ESP-IDF. A module is a plain C file that includes one header. - Architecture-aware loading. The loader applies Xtensa or RISC-V relocations at load time, so the same source compiles for any supported chip with the right toolchain.
- Strong isolation. Each module runs in its own task with an independent watchdog. On Xtensa, a faulting module is killed without taking the device down (panic isolation). On RISC-V the device reboots.
- No persistence by default. Module memory (IRAM/PSRAM) is zeroed on unload. A power cycle leaves no trace unless a module was explicitly persisted to encrypted NVS.
Architecture¶
flowchart LR
OP["C3PO operator"] -->|"compile + sign"| BLOB["[32B HMAC][ELF]"]
BLOB -->|"mod_chunk x N + mod_load (C2)"| DEV
subgraph DEV["ESP32 agent"]
V["Verify HMAC"] --> R["Relocate ELF"]
R --> L["Load to IRAM/PSRAM"]
L --> T["FreeRTOS task<br/>espm_init -> espm_exec"]
T --> WD["Watchdog + panic isolation"]
end
Where to go next¶
- Module Lifecycle - the full load/run/unload state machine
- Writing a Module - author guide with a worked example
- Building and Injecting - compile, sign, and transfer
- Syscall Reference - all 102 syscalls by category
- Security - signing, isolation, and zeroization