3.2 KiB
3.2 KiB
pm3py — Development Guide
What is this?
A pure-Python async library that speaks the Proxmark3 NG wire protocol directly over USB serial. Returns structured dicts, not text. No dependency on the C client binary.
Quick reference
# Run tests (no hardware needed, all mocked)
cd /home/work/pm3py
python -m pytest tests/ -v
# Install for development
pip install -e .
Architecture
protocol.py— Wire constants, CRC-16/A, Cmd enum, PM3Status enum. Source of truth for command IDs and frame formats.transport.py— Frame encode/decode (encode_ng_frame,encode_mix_frame,decode_response_frame),PM3Transportasync serial class. USB skips CRC (uses magic0x3361), FPC UART uses CRC with byte-swapped wire format ((low << 8) | high).client.py—Proxmark3class (async-first,Proxmark3.sync()for REPL),_SyncProxy,FirmwareInfo, port auto-detection.hw.py— Hardware commands. LED API is platform-aware (Easy vs RDV4 have different color-to-pin mappings). PWM validation fetches capabilities on first use.lf.py— LF commands +T55xxCommands+LFSearchResult. Full tag demod (EM410x, HID, etc.) is NOT implemented — that requires the C client's DSP stack.hf.py— HF core (tune, search, sniff, dropfield).hf_14a.py— ISO 14443-A (scan, raw, sniff, sim). Uses MIX frames.hf_15.py— ISO 15693 (scan, rdbl, wrbl, sniff). Uses NG frames with ISO command payloads.hf_mf.py— MIFARE Classic (rdbl, wrbl, rdsc, chk, sniff, sim, nested, cident). Read uses NG, write uses MIX.
Wire protocol essentials
- NG frame (client→device): magic
0x61334d50+length|0x8000+ cmd + payload + crc/nocrc - MIX frame: Same but
ngbit unset, payload starts with 3x uint64 args (24 bytes) - Response frame: magic
0x62334d50+length|ng+ status + reason + cmd + payload + crc/nocrc - USB: no CRC (postamble =
0x3361cmd /0x3362resp). C client setssend_with_crc_on_usb = false. - FPC UART: CRC-16/A with byte-swapped wire encoding
Key patterns
- All command methods are
async. The sync wrapper in_SyncProxyintercepts via__getattr__and callsloop.run_until_complete(). - Command classes hold a
self._treference toPM3Transport(or mock in tests). - Tests use
AsyncMockfor transport. Sethw._is_rdv4 = Falseto skip capabilities fetch in LED tests. capabilities()response parsed at known byte/bit offsets from the C struct (version=7 format).
Platform differences (PM3 Easy vs RDV4)
| Color | Easy | RDV4 |
|---|---|---|
| green | A (0x01) | B (0x02) |
| red | B (0x02) | C (0x04) |
| orange | C (0x04) | A (0x01) |
| blue | D (0x08) | (none) |
| red2 | (none) | D (0x08) |
PWM-capable: Easy = A,B. RDV4 = A,D.
Adding new commands
- Find the
CMD_*constant ininclude/pm3_cmd.hand add toCmdenum inprotocol.py - Check if the C client uses
SendCommandNG(→send_ng) orSendCommandMIX(→send_mix) - Check the payload struct in
pm3_cmd.hand usestruct.packto build it - Parse the response using
struct.unpack_fromonresp.data - Return a dict with human-readable keys
- Write test with
AsyncMocktransport — no hardware needed