Files
simtrace-analyser/README.md
T
catarrh 6fbe57052f README: drop the CORS aside from Quick start, keep it where origins differ
The first-run step explained that the PWA and the API share an origin and no
CORS/Private-Network-Access setup is involved - noise for the default setup,
which always serves both from the same server.  The line is gone (both
languages), the Usage server intro no longer repeats it either, and the one
place where it matters - the hosted PWA, which talks to a local server from
another origin - now says so explicitly before the browser requirements.
2026-10-27 11:00:27 +03:00

549 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SIMtrace Analyser
English | [Русский](README_RUS.md)
APDU trace analyser for the GSMTAP-SIM format — captures SIM/UICC
communication from a UDP GSMTAP stream (or imports PCAP/PCAPNG files),
decodes it, and serves an installable web app for browsing. The repository
also bundles **simtrace2-pysniff**, a Python replacement for
`simtrace2-sniff` that captures from Osmocom SIMtrace2 hardware and can feed
GSMTAP into this analyser.
📢 Project news and updates: [atroshin.ru](https://atroshin.ru) | [Telegram channel](https://t.me/youeyeseesee)
![SIMtrace Analyser PWA](frontend.png)
## Prerequisites
**Required**
- **Python 3.9+** — GSMTAP capture, PCAP import and browsing use only the
standard library.
**Only for capturing from Osmocom SIMtrace2 hardware** (`--capture direct`, and
the bundled `simtrace2-pysniff` capture utility):
- **PyUSB** — `pip install pyusb`
- **libusb** — system library, present by default on Linux; on Windows the
SIMtrace2 driver must be swapped (see [USB Device Access](#usb-device-access))
- **SIMtrace2 hardware** running firmware in *trace* mode (VID `1d50`, PID
`60e3`, USB class `ff`, subclass `01`); building and flashing trace firmware
is described in the
[upstream SIMtrace2 project](https://gitea.osmocom.org/sim-card/simtrace2).
The analyser needs none of this for `--capture gsmtap` (the GSMTAP source may
even run on another machine), `--capture disabled`, or PCAP imports.
## Quick start
Three ways to get it running; all of them serve the same PWA at
http://127.0.0.1:8081/.
### 1. Wheel (pip)
The wheel bundles the PWA, so the server serves the UI by itself:
```sh
pip install ./simtrace_analyser-2.7.13-py3-none-any.whl
simtrace-analyser-server --capture gsmtap
```
If your system Python is externally managed (PEP 668 — common on Arch and
Fedora), install into a virtual environment first:
`python3 -m venv .venv && . .venv/bin/activate`.
Every artifact is on the
[latest release](https://github.com/anttro/simtrace-analyser/releases/latest).
`pip` can fetch the wheel directly (use the version shown there), or the GitHub
CLI can pick the latest one:
```sh
pip install https://github.com/anttro/simtrace-analyser/releases/download/v2.7.13/simtrace_analyser-2.7.13-py3-none-any.whl
# or:
gh release download --pattern '*.whl'
pip install ./simtrace_analyser-*.whl
```
### 2. Zip (Windows-friendly, no installer)
Download `simtrace-analyser-2.7.13.zip` from the release page and extract it
anywhere: it is the runtime tree (the Python packages, the PWA, the launchers
and the docs — no tests, no git). Then run:
Windows:
```bat
start.bat
```
Linux/macOS:
```sh
./start.sh
```
Python 3 has to be on `PATH`; `pip install pyusb` is only needed for direct
SIMtrace2 capture. The launchers accept the server's options
(`start.bat --capture direct`, `./start.sh --capture direct --port 8080`).
(The release's *Source code* archive is the full tree including tests; it
works like a clone below.)
### 3. Git clone (development)
```sh
git clone https://github.com/anttro/simtrace-analyser.git
cd simtrace-analyser
./start.sh # Linux/macOS; on Windows: start.bat
```
The launchers put the checkout on `PYTHONPATH` and run
`python -m simtrace_analyser`, so nothing has to be installed for browsing and
GSMTAP capture; their options are the server's
(`./start.sh --capture direct --port 8080`). Direct hardware capture needs
**PyUSB** (`pip install pyusb`). To get the console scripts
(`simtrace-analyser-server`, `simtrace2-pysniff`) install the package:
```sh
pip install -e .
simtrace-analyser-server --capture gsmtap
```
### First run
Open **http://127.0.0.1:8081/** in a browser. `Ctrl+C` stops the server.
- `--capture gsmtap` (default) — listen for GSMTAP-SIM on UDP 4729, e.g. from
`simtrace2-pysniff` or `sigrok_iso7816_stream`;
- `--capture direct` — read SIMtrace2 hardware directly (needs PyUSB; Windows
also needs the driver swap, see [USB Device Access](#usb-device-access));
- `--capture disabled` — only browse stored captures.
The full runtime guide (server options, the UI views, the capture utility) is
in [Usage](#usage) below.
## Usage
### Analysis server + PWA (`simtrace-analyser-server`)
Stores captured APDU traffic in SQLite and serves it over HTTP, together with
the bundled **SIMtrace Analyser** PWA (`frontend/` in a source checkout,
bundled into the package on install) on the same origin. The commands below
use the console script; from a source checkout the launchers take the same
options (`./start.sh --capture gsmtap`, `start.bat --capture gsmtap`). Three
capture modes:
```sh
# Listen for GSMTAP from simtrace2-pysniff (or original simtrace2-sniff):
simtrace-analyser-server --capture gsmtap
# Capture directly from SIMtrace2 hardware (no external tool needed):
simtrace-analyser-server --capture direct
# Browse/analyse existing captures without a capture backend:
simtrace-analyser-server --capture disabled
```
The server logs its capture liveness to stderr (each line timestamped
with the local time): `Capture started` on begin, a periodic
`Capture alive: session=N messages=M bytes=B dropped=D` heartbeat, and
`Capture stopped` (or `ERROR: capture thread stopped on error …` if the
capture thread died unexpectedly). A mis-framed or non-SIM GSMTAP
datagram is dropped and counted (`dropped`) rather than silently
killing the capture; a `tpdu` packet that is not APDU-shaped at all
(shorter than the header, the invalid CLA `FF` or an ISO-reserved class)
is skipped and counted (`skipped`) — a character-level or mis-sampled
stream no longer pollutes the decode. The heartbeat interval defaults to 60 s and is
tunable via `--log-interval SECONDS`.
The GSMTAP listener also accepts the **sigrok-iso7816-stream** decoder
(https://github.com/anttro/sigrok_iso7816_stream) — an FX2-logic-analyzer
passive SIM sniffer that emits the same GSMTAP-SIM format on UDP 4729. Its
standard PPS request/response sub-types (`0x02`/`0x03`) are decoded as PPS,
and its custom sub-types for RST/VCC line events (`0x10`/`0x11`, added for
compatibility with that project) are decoded as `RESET ASSERTED` /
`RST de-asserted — ATR follows` and `VCC ON/OFF` timeline markers, and a
reset-assert or power-removal
correctly invalidates the file-selection tracking. When an RST event carries
the measured CLK frequency (decoder v1.9.0+, optional payload extension), the
following ATR is annotated with the CLK frequency, data rate and ETU derived
from its TA1 Fi/Di (`data_rate = clk_hz × D / F`); the measurement is
consumed by the ATR so a later ATR without a reset gets no stale rate (a
late-arriving measurement is backfilled onto the ATR it belongs to). GSMTAP
packets carrying the `GSMTAP_FLAG_BAD_FCS` desync flag (set by that decoder
on mis-framed exchanges) are surfaced as a flagged/desynced entry in the
capture timeline, so such artefacts are easy to spot when reviewing a capture
rather than silently altering the decode.
### COMPRION text export import
The **Import COMPRION trace** button accepts the tab-separated **ISO Protocol
Layer** export of COMPRION's Device Manager — the byte-accurate protocol view
(ATR, PPS, APDUs with their return codes). The *ISO Application Layer* export
carries no raw response bytes and is not needed; selecting it answers with a
pointer to the Protocol Layer file (the same check runs in the PWA before the
upload and on the server for API callers).
Exchanges are re-encoded into the same wire-shaped TPDUs a GSMTAP capture
carries (a case-4 exchange becomes the command, a `61 xx` status word and a
GET RESPONSE with the response data and the final SW), so command/response
pairing, FCP/file decoding and the filters behave exactly as for a live
capture.
**Exact timestamps:** the import dialog asks for the *ISO Protocol Layer*
export (required) and, optionally, the matching *ISO Application Layer*
export — two separate pickers, both file names visible, and an error stays in
the dialog until it is fixed or cancelled. With both files the Application
Layer's per-exchange wall-clock times become the trace's timeline — the
session window and the inter-APDU gaps are then exact; without it the
timeline is the estimate described below. Before anything is imported the
two files are verified to belong to the same session (`Session Name`,
falling back to `Working directory`; the export timestamps of the two layers
differ and are not compared) and their exchange counts must align (the two
exports must cover the same set of exchanges) — a mismatch refuses the import
instead of mixing sessions. The ATR and the PPS exchange have no Application
Layer blocks, so they carry the timestamp of the preceding exchange.
**Protocol Layer alone:** the `Delay` column measures only card/line activity
— host pauses between APDU bursts are absent — so the timeline keeps the
message order and the short-term gaps but is much shorter than the real
session. Its unit comes from an assumed 3.8 MHz interface clock (`CLK_HZ` in
`simtrace_analyser/comprion.py`), which is a scale, not a measurement: the
export carries no CLK, and the import deliberately does not invent one (the
ATR shows the F/D the file states, with no data-rate/ETU annotation, unlike
a capture from the sigrok-iso7816-stream sniffer). The session's wall-clock
window is taken from the file's `Session Name` timestamp when it carries
one.
Exchanges larger than 255 bytes (extended-length APDUs) cannot be represented
in the stored wire shape and are skipped with a note in the import report.
### Files view (per-file tracking)
The Trace tab has a view selector (*Map* / *Stats* / **Files**) in its topbar,
right-aligned over the side panel with the panel's width, so it stays visible
while the panel scrolls (a drop-down, since three tab captions no longer
fit). The Files view is a
trace-wide timeline of file operations — every **Read** and **Update** row
shows the same file name as the Map view (a card-side change appears as a
read with a **Changed on card!** link that opens its details), and clicking
a row jumps straight to the APDU in the trace and leaves it highlighted
until another one is focused (the APDU list keeps the reader's position:
new messages pin the view to the bottom only while the reader is already
there, so a jump is never pulled away or un-highlighted by the live poll). Rows that carry a recorded change (a
card-side change, or an update that wrote or first observed bytes) also offer
**Show diff**: a modal showing the full file path (`MF/EF_ICCID (2FE2)`,
`ADF/EF_IMSI (6F07)`, `DF_TELECOM/DF_PHONEBOOK/4FA1`, `AID …/4F09` — the whole
DF chain the trace's selection path spells out, each DF named from the
specification dictionary and the AID from the EF.DIR labels when known) and
the affected unit — the whole
file, or the single record that changed — **before and after** the change, as
hex views (up to 16 bytes per row — a shorter file/record ends its row there
instead of padding with `--`, and the offset column only appears when a view
spans more than one row) with the bytes that changed highlighted in one colour
and the bytes the operation first observed in another, the changed/new byte
counts, the APDU reference and a colour legend below the views that lists only
the marks actually shown.
Phonebook files have issuer-assigned FIDs, so the dictionary cannot name
them: when the trace carries the card's **EF_PBR** records, they are decoded
(TS 31.102 §4.4.2.1 — the `A8`/`A9`/`AA` type groups and the `C0`…`CC`
primitive tags, each with its FID and, for a three-byte object, its SFI) and
the Files view names the files from the card's own declaration — e.g.
`DF_TELECOM/DF_PHONEBOOK/EF_PBC (4FA1)` — while the declared SFIs feed the
DF's SFI→FID map (the `88`-less FCP fallback of TS 102 221 §11.1.1.4.8 still
applies for other files).
The images come from `GET /api/sessions/<id>/files/image?key=…&change=…` and
are reconstructed from the current image by undoing the later changes
(`filetrack.FileTracker.change_images`) — nothing is duplicated per event.
Every value change is recorded, so the values are exact; a byte that was only
*first observed* after the selected change can appear as known in both views.
The data comes from `simtrace_analyser/filetrack.py`
(`GET /api/sessions/<id>/files`), a single-pass tracker over the decoded
session that keeps, keyed by (DF, FID) and **across card resets**: access
counters (`select`/`read`/`update`, successful operations only), a sparse image
of the observed contents and the change log. Device-initiated changes are the
UPDATE operations seen in the trace; card-initiated ones are inferred when a
read returns bytes differing from what the image already knew — bytes never
seen before just fill the image (an observation, not a change). Files reached
by SFI share the entry of their path/FID accesses: the SFI ↔ FID relations come
from the normative tables (TS 102 221 Annex H.1 and the TS 31.102 H.1–H.12
annexes; the ISIM SFI rows come from TS 31.103) and from every
surfaced FCP carrying the `88` DO, including the spec's "SFI = the 5 LSBs of the
FID" rule when the DO is absent; an SFI used before its relation is learned
stays provisional and merges into the resolved entry. A file's DF context
follows TS 102 221 §8.4/Table 8.2 (selecting an EF leaves its parent DF current,
a path selection included); applications selected by an unknown AID get the AID
as their DF identity, named from EF.DIR when the trace surfaced it. Bounds: the
image freezes past 64 KB of observed bytes per file (`image_truncated`) and the
change log keeps the last 1000 events per file (`changes_total` keeps counting).
Counters are returned by the API but not shown in this view yet.
File names are **DF-scoped**: `simtrace_analyser/ef_tables.py` holds the complete
per-DF file tables and SFI lists extracted from the pinned specs (TS 102 221,
TS 31.102 including its H.1–H.12 SFI annexes, TS 31.103, TS 51.011), so a FID
reused across DFs resolves to the file its DF context names — `4F09` is
EF_SUPI_NAI under DF_5GS but EF_PROSE_GC under DF_ProSe, `6F07` is EF_IMSI in
the USIM/SIM but EF_IST in the ISIM, `6F4E` is EF_EXT4 in DF_TELECOM but
EF_EXT5 in the USIM ADF — with the flat table as the fallback when no DF is
known. `tests/extract_spec_tables.py` regenerates the pinned fixture
(`tests/data/spec_files.json`) and `--check` re-verifies the tables against the
PDFs.
Server options:
```
--host ADDR HTTP bind address (default: 127.0.0.1)
--port PORT HTTP server port (default: 8081)
--db FILE SQLite database path (default: ~/.simtrace-analyser/sessions.db)
--capture MODE gsmtap | direct | disabled (default: gsmtap)
--gsmtap-port PORT UDP port for the GSMTAP listener (default: 4729)
--gsmtap-bind ADDR UDP bind address for the GSMTAP listener (default: 127.0.0.1;
use 0.0.0.0 to receive from a Docker bridge or another host)
--web-dir PATH PWA static files directory (default: the PWA bundled in
the installed package, or <repo>/frontend from source)
```
Then open **http://127.0.0.1:8081/** in a browser. Use `--web-dir PATH` to
serve a different PWA directory (default: the bundled PWA, or
`<repo>/frontend` in a source checkout).
### Hosted PWA (landing page)
A standalone copy of the PWA is hosted at **https://simtrace.atroshin.ru**.
It is a pure frontend: point it (Settings → Server URL) at a locally running
`simtrace-analyser-server`.
> **Browser restriction:** this is the one setup where the PWA and the API have
> different origins, so the browser applies CORS and Private Network Access
> rules. Reaching a local server (`http://127.0.0.1:8081`) requires two
> things: the server must send `Access-Control-Allow-Private-Network: true`
> (this server does), and the browser must be allowed to access the local
> network — in Chrome/Edge/Vivaldi: Site settings → Local network access →
> allow the site (or accept the permission prompt). Without the browser
> permission, the request to `127.0.0.1` is blocked before any preflight is
> sent.
### Capture utility (`simtrace2-pysniff`)
Captures SIMtrace2 hardware traffic and writes it to stdout, GSMTAP
(Wireshark), or PCAP files. It is the bundled replacement for the original
Osmocom `simtrace2-sniff`.
The `./sniff-start.sh` startup script performs a pre-flight USB permission
check (prints udev install instructions if the SIMtrace2 device is not
writable) and then launches the Python module. On Windows, use
`sniff-start.bat` with the same options (no permission check needed).
```sh
./sniff-start.sh
./sniff-start.sh --format timestamp
./sniff-start.sh --gsmtap 127.0.0.1:4729
./sniff-start.sh --pcap trace.pcap --format atr-time
```
OPTIONS can also be set via environment variables:
```sh
FORMAT=timestamp GSMTAP=127.0.0.1 ./sniff-start.sh
```
Cross-platform module invocation:
```sh
python -m simtrace2_pysniff
python -m simtrace2_pysniff --format timestamp --gsmtap 127.0.0.1:4729
python -m simtrace2_pysniff --pcap trace.pcap
```
If installed via `pip install -e .`, use the console script directly:
```sh
simtrace2-pysniff --format atr-time
```
### Utility CLI Options
```
python -m simtrace2_pysniff [OPTIONS]
--format, -f FORMAT Output format (default: hex)
hex — plain hex dump
timestamp — TPDU lines prefixed with [HH:MM:SS.mmm]
atr-time — TPDU lines prefixed with [SSSS.mmm] from last ATR
--gsmtap HOST[:PORT] Send ATR/TPDU as GSMTAP over UDP (default port 4729)
--pcap FILE Write sniffed data to PCAP file for Wireshark
--output, -o FILE Write hex dump to file
--no-stdout Suppress hex dump to stdout
--vendor HEX USB vendor ID (default: 0x1d50)
--product HEX USB product ID (default: auto-detect)
Recovery options:
--no-reconnect Exit on USB disconnect instead of reconnecting
--reconnect-delay-min SEC Minimum reconnect delay (default: 1.0)
--reconnect-delay-max SEC Maximum reconnect delay cap (default: 30.0)
--backoff-factor N Exponential backoff multiplier (default: 1.5)
--inactivity-timeout SEC Reconnect after N seconds of silence (default: disabled)
```
### Output Formats
- **`hex`** (default): plain hex dump — `ATR: 3b 9e ...`, `TPDU: a0 a4 00 00 02 3f 00`
- **`timestamp`**: TPDU lines prefixed with local time — `[14:32:05.123] TPDU: a0 a4 00 00 02 3f 00`
- **`atr-time`**: TPDU lines prefixed with seconds since last ATR — `[0001.234] TPDU: a0 a4 00 00 02 3f 00`
Non-TPDU messages (ATR, PPS, card state changes, Fi/Di) print identically in all formats.
- **GSMTAP**: ATR and APDU messages over UDP (port 4729) to Wireshark
- **PCAP**: ATR/TPDU messages as Ethernet/IP/UDP/GSMTAP packets, openable in Wireshark
### Background / Daemon
SIGHUP is automatically ignored when stdin is not a terminal (i.e. run in
background or via a pipe). Foreground terminal sessions still respond to
SIGHUP normally.
To run persistently in the background with output to files:
```sh
nohup ./sniff-start.sh --output /tmp/sniff.log &> /tmp/sniff-err.log &
# or
FORMAT=timestamp nohup python -m simtrace2_pysniff --output /tmp/sniff.log &> /tmp/sniff-err.log &
```
### Library Usage
```python
from simtrace2_pysniff import SniffSession
session = SniffSession(inactivity_timeout=30.0)
for msg in session.iter_messages():
print(f"{msg.type}: {msg.data.hex()}")
```
### Recovery
The utility survives hardware resets, cable disconnects, and firmware hangs
by default — it reconnects with exponential backoff (1s → 30s cap).
Use `--inactivity-timeout` to also trigger reconnect on silent firmware hangs.
## USB Device Access
Required for capture from SIMtrace2 hardware (the utility and the server's
`--capture direct` mode); not needed for GSMTAP or PCAP sources.
### Linux
A udev rule is required for non-root USB access to the SIMtrace2 device.
```sh
sudo cp 70-simtrace2-pysniff.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
```
Then reconnect the SIMtrace2 USB cable. The rule uses `TAG+="uaccess"`
(systemd-logind ACL) — no group membership or world-writable permissions needed.
### Windows
Windows does not ship a libusb-compatible driver for SIMtrace2. Use
[Zadig](https://zadig.akeo.ie/) to replace the default driver:
1. Download and run **Zadig**
2. Plug in the SIMtrace2 device
3. Select it in the dropdown (VID `1D50`, PID `60E3` — "Osmocom SIMtrace 2")
4. Choose **WinUSB** (or libusbK) as the replacement driver
5. Click **Replace Driver** (one-time operation)
After that, `python -m simtrace2_pysniff` works identically to Linux.
The Windows launchers match their shell counterparts and pass every option
through: `start.bat --capture direct` starts the analyser server and
`sniff-start.bat` the capture utility. Both call the `python` command, so
Python 3 must be on `PATH` (verify with `python --version` in a new command
prompt).
## Components
- **Analysis server + PWA** (`simtrace-analyser-server`) — stores captured
traffic in SQLite, decodes it, and serves the PWA ([`frontend/`](frontend/))
plus the HTTP API on a single origin. Three capture modes: a GSMTAP
listener, direct SIMtrace2 hardware capture, or disabled (browse existing
captures only).
- **Capture utility** (`simtrace2-pysniff`) — captures SIMtrace2 hardware
traffic and writes it to stdout, GSMTAP (Wireshark), or PCAP files. It is
one GSMTAP source of several that the analyser accepts (see also
[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)).
- **PWA** ([`frontend/`](frontend/)) — trace browser with decoded APDU,
SIM Toolkit, SMS and SCP80 OTA detail; sessions, PCAP and COMPRION trace
import, EN/RU UI.
## Features
**APDU decoder** (server-side, rendered by the PWA):
- Full APDU decode — CLA per ISO 7816-4 / ETSI coding with the **logical
channel 0-19** (standard channels from b2-b1, the extended channels 4-19
from b4-b1 per TS 102 221 §10.3), secure messaging and command chaining,
INS names from a spec table, SFI-aware P1/P2 for READ/UPDATE BINARY and
record commands; MANAGE CHANNEL replies show the channel the card
assigned
- Status words — full ISO 7816-4 set plus UICC-specific values:
`91XX` (proactive command pending), `62F1/F2/F3`, `63F1/F2` (more data),
`9300` (SAT busy), `9850`, `9862–64`
- SIM Toolkit — SAT (GSM 11.14), CAT (TS 102 223) and USAT (TS 31.111)
proactive commands with decoded qualifiers, TERMINAL RESPONSE, ENVELOPE
(Menu Selection, Call Control, SMS-PP download…); tolerates known
non-compliant legacy-card quirks and preserves unrecognized TLVs
- TERMINAL CAPABILITY (TS 102 221 §11.1.19) — terminal power supply
(voltage class, maximum current, clock), extended logical channels,
additional interfaces (UICC-CLF) and eUICC device capabilities
(SGP.22/SGP.32)
- SMS TPDU — SMS-DELIVER/SUBMIT, SCTS timestamps, UDH information elements
- SCP80 OTA secured packets — SPI bits (ciphering, PoR requirement/mode,
RC/CC/DS, counter), KIc/KID algorithm + key set, TAR, CNTR/PCNTR;
Response Packet (PoR) decode on GET RESPONSE; **optional client-side
decryption** of ciphered packets — paste the card's 3DES/AES key in the
detail view to reveal the real CNTR and secured application data (the
key stays in the browser, in-memory only; ciphered on-wire CNTR/PCNTR/RC
are ciphertext and labeled as such); the secured application message is
decoded as a stream of **C-APDUs** (SELECT, UPDATE BINARY, …) with
full CLA/INS/P1/P2/Lc/Le breakdown — plaintext immediately, or after
decryption for ciphered packets
- ATR parse (clock-rate conversion, Fi/Di, T-bitmask, truncated-ATR safe)
and FCP/FCI TLV; the historical bytes are decoded as the UICC's
COMPACT-TLV objects (TS 102 221 §6.3.1) — card service data, the **card
capabilities** (ISO 7816-4 §8.1.1.2.7 Table 88: the assignment methods
and the announced maximum number of logical channels, `8 or more` for the
extended-capable cards) and the trailing status indicator (LCS/SW)
**SIMtrace Analyser PWA** ([`frontend/`](frontend/)):
- Trace view with Map/List modes — proactive, TERMINAL RESPONSE, ENVELOPE
and AUTH commands get distinctive styling in map view
- Sessions with deep links (`#session=N`), PCAP import (`.pcap/.pcapng/.cap`),
COMPRION trace import (the Device Manager *ISO Protocol Layer* text export),
search and a **multi-select command filter** — pick any number of commands
and/or the conversation presets (*Proactive sessions*: FETCH, TERMINAL
PROFILE and every command answered with SW 91xx; *File operations*: SELECT,
READ/UPDATE BINARY and READ/UPDATE RECORD, with the GET RESPONSE carrying a
selected command's data)
- Installable PWA, dark theme, English/Russian UI
## Related
- **[simtrace2](https://gitea.osmocom.org/sim-card/simtrace2)** — the upstream
SIMtrace2 hardware/firmware project that the capture utility sniffs from.
- **[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)** —
FX2LP-based passive ISO 7816 sniffer that emits the same GSMTAP-SIM stream
on UDP 4729 (accepted by this project's GSMTAP listener).
## License
MIT — see [LICENSE](LICENSE). The decoders and the PWA's DES/3DES
implementation are original, written from the ISO 7816, ETSI and 3GPP
specifications; no third-party code is bundled. PyUSB (BSD-3-Clause) is a
runtime dependency for direct hardware capture.