build(rawcli): make prompt_toolkit a core dependency + document rawcli
`pm3py rawcli` is a headline tool, so prompt_toolkit moves from the [rawcli] extra into core dependencies — a plain `pip install` gets it and the command just works. The extra is removed; the missing-module guard now points at a reinstall (it only fires on a broken env). README: add the rawcli feature bullet, a dedicated "pm3py rawcli" section (breadcrumb, entry toggle, identify, function-style commands, live trace, TLV, completion), prompt_toolkit in the dependency list, and cli/ in the architecture tree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
43
README.md
43
README.md
@@ -7,6 +7,7 @@ Pure-Python async client for the [Proxmark3](https://github.com/RfidResearchGrou
|
||||
- **Async-first, REPL-friendly** — `async`/`await` core with a synchronous proxy (`Proxmark3.sync()`) that supports `dir()`, `help()`, and tab-completion.
|
||||
- **Live sniff & simulation** — stream decoded reader↔tag frames into your REPL in real time; edit an emulated tag and push it live.
|
||||
- **Software-defined tags** — pure-Python transponder models (ISO 14443-A, ISO 15693, MIFARE Classic, NTAG/Type-2, NXP ICODE/SLIX2/NTAG5, LF) served from a firmware response table.
|
||||
- **Interactive raw console** — [`pm3py rawcli`](#pm3py-rawcli--interactive-raw-command-console) drops into a Proxmark-style TUI for raw command entry: a live annotated trace, hex/binary toggle, and function-style transponder commands.
|
||||
|
||||
## Install
|
||||
|
||||
@@ -19,8 +20,8 @@ cd pm3py
|
||||
pip install -e . # or `pip install .`
|
||||
```
|
||||
|
||||
Requires **Python 3.10+**; dependencies (`pyserial-asyncio`, `bitarray`, `pycryptodome`)
|
||||
install automatically. It's pure Python + pyserial, so it also runs on a **Raspberry Pi**
|
||||
Requires **Python 3.10+**; dependencies (`pyserial-asyncio`, `bitarray`, `pycryptodome`,
|
||||
`prompt_toolkit`) install automatically. It's pure Python + pyserial, so it also runs on a **Raspberry Pi**
|
||||
(aarch64) — see [Flashing firmware](#flashing-firmware-pm3flash) for the Pi bring-up.
|
||||
|
||||
## Quick Start
|
||||
@@ -489,6 +490,43 @@ method (`pm3.hf.dropfield()`) for those.
|
||||
> method names, and signatures (e.g. `rdbl(block, uid=None)`) instead of an opaque
|
||||
> `(*args, **kwargs)` wrapper.
|
||||
|
||||
## `pm3py rawcli` — interactive raw-command console
|
||||
|
||||
`pm3py rawcli` opens an interactive, Proxmark-style console for driving a connected device at the
|
||||
raw level — a live annotated trace, flexible hex/binary entry, and function-style transponder
|
||||
commands. It's a **separate tool from the Python REPL**: its own command grammar, not Python.
|
||||
|
||||
```bash
|
||||
pm3py rawcli # auto-detect the device (runs offline if none attached)
|
||||
```
|
||||
|
||||
```text
|
||||
[raw / 0x] identify # probe the field; sets HF/LF + transponder, keeps the link
|
||||
NTAG213 (HF / 14a) UID 04A1B2C3
|
||||
|
||||
[raw / hf / NTAG213 / 0x] READ(4) # function-style command from the tag's catalog
|
||||
[Rdr] Reader → Tag: 30 04 READ page 4
|
||||
[Rdr] Tag → Reader: 01 02 03 04 …
|
||||
|
||||
[raw / hf / NTAG213 / 0x] 30 04 # …or send raw bytes directly
|
||||
[raw / hf / NTAG213 / 0x] tlv 00 03 09 D1 01 05 54 02 65 6E 48 69 FE # decode a TLV / NDEF block
|
||||
```
|
||||
|
||||
- **Breadcrumb prompt** `[raw / hf|lf / <transponder> / 0x|0b]` reflects the field, the identified
|
||||
tag, and the current numeric entry mode.
|
||||
- **Entry modes** — type hex (`30 04`) or binary; **Ctrl-/** toggles `0x`/`0b`, digits auto-group
|
||||
into bytes, and `0x`/`0b` prefixes can be intermixed on one line.
|
||||
- **`identify`** probes 14a → 15693 → LF and keeps the tag selected; **`transponder`** / **`close`**
|
||||
manage the connection.
|
||||
- **Function-style commands** — `READ(4)`, `GET_VERSION()`, `WRITE(4, 0x01020304)` from the
|
||||
identified tag's catalog (Type 2 / MIFARE Classic / ISO 15693 seeded). `help` lists them,
|
||||
`help READ` shows one.
|
||||
- **Live annotated trace** — every exchange prints the reader↔tag frames with decoded annotations.
|
||||
- **TLV / NDEF** — `tlv <hex>` prints a structured multi-line breakdown; a multi-line editor
|
||||
composes long TLVs.
|
||||
- **ipython-style completion** — command/argument menus with each command's help as the tooltip,
|
||||
plus history auto-suggestion.
|
||||
|
||||
## Firmware Compatibility
|
||||
|
||||
On connect, pm3py pings the device and fetches the firmware version. The result is on
|
||||
@@ -581,6 +619,7 @@ pm3py/
|
||||
│ ├── decode_iso14a.py / decode_iso15.py # per-protocol decoders
|
||||
│ ├── ndef.py # NDEF TLV/record decode
|
||||
│ └── format.py # ANSI color trace formatting
|
||||
├── cli/ # `pm3py` console-script: rawcli (interactive raw-command TUI)
|
||||
├── sniff/ # SniffSession — protocol detection, live streaming lifecycle
|
||||
├── sim/ # card-sim infrastructure: SimSession, response-table compiler,
|
||||
│ # Medium/Reader models, MCU bridge, replay/relay/fuzzer
|
||||
|
||||
@@ -25,9 +25,8 @@ def main(argv: list[str] | None = None) -> int:
|
||||
import prompt_toolkit # noqa: F401
|
||||
except ModuleNotFoundError:
|
||||
print(
|
||||
"pm3py rawcli needs prompt_toolkit, which isn't installed.\n"
|
||||
" install it with: pip install 'pm3py[rawcli]'\n"
|
||||
" or directly: pip install prompt_toolkit",
|
||||
"pm3py rawcli needs prompt_toolkit (a core dependency) but it isn't importable.\n"
|
||||
" reinstall pm3py, or: pip install prompt_toolkit",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 2
|
||||
|
||||
@@ -38,11 +38,11 @@ dependencies = [
|
||||
"pyserial-asyncio>=0.6",
|
||||
"bitarray>=2.0",
|
||||
"pycryptodome>=3.10",
|
||||
"prompt_toolkit>=3.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = ["pytest"]
|
||||
rawcli = ["prompt_toolkit>=3.0"]
|
||||
|
||||
[project.scripts]
|
||||
pm3flash = "pm3py.flash_cli:main"
|
||||
|
||||
Reference in New Issue
Block a user