Rename project to SIMtrace Analyser (v2.0.0)
- move the analysis server out of simtrace2_pysniff into simtrace_analyser; simtrace2_pysniff stays as the simtrace2-sniff replacement capture utility - new server CLI: simtrace-analyser-server; python -m simtrace_analyser (simtrace2-pysniff-server and python -m simtrace2_pysniff.server dropped) - simtrace2_pysniff lazy-imports hardware symbols, so the server no longer pulls PyUSB at startup; the GSMTAP/PCAP codecs work without libusb - version 2.0.0 everywhere (was 1.22.21); PWA SW cache simtrace-analyser-v54 - frontend: server name, clone URL and i18n strings updated; vendored-parser reference otaman -> SIMple - README/README_RUS reworked product-first (analyser first, utility second) - DB dir ~/.simtrace-analyser, /api status payload and hosted PWA unchanged
This commit is contained in:
1 parent
8441b56e99
commit
38650b4797
20 files changed
+390
-321
No files matched your search
@@ -1,15 +1,29 @@
|
|||||||
# simtrace2-pysniff
|
# SIMtrace Analyser
|
||||||
|
|
||||||
English | [Русский](README_RUS.md)
|
English | [Русский](README_RUS.md)
|
||||||
|
|
||||||
Python-based replacement for `simtrace2-sniff` — SIM card communication sniffer
|
APDU trace analyser for the GSMTAP-SIM format — captures SIM/UICC
|
||||||
for Osmocom SIMtrace2 hardware (firmware in **trace** mode). It bundles the
|
communication from a UDP GSMTAP stream (or imports PCAP/PCAPNG files),
|
||||||
**simtrace-analyser** PWA (in [`frontend/`](frontend/)) and an analysis server
|
decodes it, and serves an installable web app for browsing. The repository
|
||||||
that both captures APDU traffic and serves that PWA.
|
also bundles **simtrace2-pysniff**, a Python replacement for
|
||||||
|
`simtrace2-sniff` that captures from Osmocom SIMtrace2 hardware and can feed
|
||||||
|
GSMTAP into this analyser.
|
||||||
|
|
||||||
**Single dependency**: PyUSB (libusb wrapper). No libosmocore, no libosmosim.
|

|
||||||
|
|
||||||

|
## 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 import, EN/RU UI.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -38,7 +52,7 @@ that both captures APDU traffic and serves that PWA.
|
|||||||
decryption for ciphered packets
|
decryption for ciphered packets
|
||||||
- ATR parse (clock-rate conversion, Fi/Di, T-bitmask) and FCP/FCI TLV
|
- ATR parse (clock-rate conversion, Fi/Di, T-bitmask) and FCP/FCI TLV
|
||||||
|
|
||||||
**simtrace-analyser PWA** ([`frontend/`](frontend/)):
|
**SIMtrace Analyser PWA** ([`frontend/`](frontend/)):
|
||||||
|
|
||||||
- Trace view with Map/List modes — proactive, TERMINAL RESPONSE, ENVELOPE
|
- Trace view with Map/List modes — proactive, TERMINAL RESPONSE, ENVELOPE
|
||||||
and AUTH commands get distinctive styling in map view
|
and AUTH commands get distinctive styling in map view
|
||||||
@@ -49,20 +63,22 @@ that both captures APDU traffic and serves that PWA.
|
|||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- **Python 3.9+**
|
- **Python 3.9+**
|
||||||
- **PyUSB** — `pip install pyusb`
|
- **PyUSB** — `pip install pyusb` (needed only for capture from SIMtrace2
|
||||||
|
hardware; GSMTAP/UDP capture and PCAP import work without it)
|
||||||
- **libusb** — system library, present by default on Linux; on Windows the
|
- **libusb** — system library, present by default on Linux; on Windows the
|
||||||
SIMtrace2 driver must be swapped (see [USB Device Access](#usb-device-access))
|
SIMtrace2 driver must be swapped (see [USB Device Access](#usb-device-access))
|
||||||
- **SIMtrace2 hardware** running firmware in *trace* mode
|
- **SIMtrace2 hardware** running firmware in *trace* mode (only for direct
|
||||||
(VID `1d50`, PID `60e3`, USB class `ff`, subclass `01`). For building and
|
capture; VID `1d50`, PID `60e3`, USB class `ff`, subclass `01`). For
|
||||||
flashing trace firmware, see the [upstream SIMtrace2 project](https://gitea.osmocom.org/sim-card/simtrace2).
|
building and flashing trace firmware, see the
|
||||||
|
[upstream SIMtrace2 project](https://gitea.osmocom.org/sim-card/simtrace2).
|
||||||
|
|
||||||
No other dependencies. PCAP, GSMTAP, and hex dump output use only Python stdlib.
|
No other dependencies. PCAP, GSMTAP, and hex dump output use only Python stdlib.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/anttro/simtrace2-pysniff.git
|
git clone https://github.com/anttro/simtrace-analyser.git
|
||||||
cd simtrace2-pysniff
|
cd simtrace-analyser
|
||||||
pip install pyusb
|
pip install pyusb
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -70,52 +86,27 @@ Or install the package into your environment:
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip install -e .
|
pip install -e .
|
||||||
# then run: simtrace2-pysniff [opts]
|
# then run: simtrace-analyser-server [opts]
|
||||||
|
# or: simtrace2-pysniff [opts]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
Two tools are provided:
|
### Analysis server + PWA (`simtrace-analyser-server`)
|
||||||
|
|
||||||
### Sniffer CLI (`simtrace2-pysniff`)
|
|
||||||
|
|
||||||
Captures SIMtrace2 traffic and writes it to stdout, GSMTAP (Wireshark), or PCAP files.
|
|
||||||
|
|
||||||
### Startup script
|
|
||||||
|
|
||||||
`./sniff-start.sh` 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
|
|
||||||
```
|
|
||||||
|
|
||||||
### Analysis server + PWA (`simtrace2-pysniff-server`)
|
|
||||||
|
|
||||||
Stores captured APDU traffic in SQLite and serves it over HTTP, together with
|
Stores captured APDU traffic in SQLite and serves it over HTTP, together with
|
||||||
the bundled **simtrace-analyser** PWA (`frontend/`) on the same origin. Three
|
the bundled **SIMtrace Analyser** PWA (`frontend/`) on the same origin. Three
|
||||||
capture modes:
|
capture modes:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Listen for GSMTAP from simtrace2-pysniff (or original simtrace2-sniff):
|
# Listen for GSMTAP from simtrace2-pysniff (or original simtrace2-sniff):
|
||||||
simtrace2-pysniff-server --capture gsmtap
|
simtrace-analyser-server --capture gsmtap
|
||||||
|
|
||||||
# Capture directly from SIMtrace2 hardware (no external tool needed):
|
# Capture directly from SIMtrace2 hardware (no external tool needed):
|
||||||
simtrace2-pysniff-server --capture direct
|
simtrace-analyser-server --capture direct
|
||||||
|
|
||||||
# Browse/analyse existing captures without a capture backend:
|
# Browse/analyse existing captures without a capture backend:
|
||||||
simtrace2-pysniff-server --capture disabled
|
simtrace-analyser-server --capture disabled
|
||||||
```
|
```
|
||||||
|
|
||||||
The server logs its capture liveness to stderr (each line timestamped
|
The server logs its capture liveness to stderr (each line timestamped
|
||||||
@@ -166,7 +157,7 @@ setup is involved. Use `--web-dir PATH` to serve a different PWA directory
|
|||||||
|
|
||||||
A standalone copy of the PWA is hosted at **https://simtrace.atroshin.ru**.
|
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
|
It is a pure frontend: point it (Settings → Server URL) at a locally running
|
||||||
`simtrace2-pysniff-server`.
|
`simtrace-analyser-server`.
|
||||||
|
|
||||||
> **Browser restriction:** when the PWA is served from a public HTTPS host,
|
> **Browser restriction:** when the PWA is served from a public HTTPS host,
|
||||||
> reaching a local server (`http://127.0.0.1:8081`) requires two things: the
|
> reaching a local server (`http://127.0.0.1:8081`) requires two things: the
|
||||||
@@ -176,7 +167,31 @@ It is a pure frontend: point it (Settings → Server URL) at a locally running
|
|||||||
> (or accept the permission prompt). Without the browser permission, the
|
> (or accept the permission prompt). Without the browser permission, the
|
||||||
> request to `127.0.0.1` is blocked before any preflight is sent.
|
> request to `127.0.0.1` is blocked before any preflight is sent.
|
||||||
|
|
||||||
### Python module (cross-platform)
|
### 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
|
```sh
|
||||||
python -m simtrace2_pysniff
|
python -m simtrace2_pysniff
|
||||||
@@ -192,6 +207,9 @@ simtrace2-pysniff --format atr-time
|
|||||||
|
|
||||||
## USB Device Access
|
## 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
|
### Linux
|
||||||
|
|
||||||
A udev rule is required for non-root USB access to the SIMtrace2 device.
|
A udev rule is required for non-root USB access to the SIMtrace2 device.
|
||||||
@@ -218,7 +236,7 @@ Windows does not ship a libusb-compatible driver for SIMtrace2. Use
|
|||||||
|
|
||||||
After that, `python -m simtrace2_pysniff` works identically to Linux.
|
After that, `python -m simtrace2_pysniff` works identically to Linux.
|
||||||
|
|
||||||
## CLI Options
|
## Utility CLI Options
|
||||||
|
|
||||||
```
|
```
|
||||||
python -m simtrace2_pysniff [OPTIONS]
|
python -m simtrace2_pysniff [OPTIONS]
|
||||||
@@ -279,14 +297,14 @@ for msg in session.iter_messages():
|
|||||||
|
|
||||||
## Recovery
|
## Recovery
|
||||||
|
|
||||||
The tool survives hardware resets, cable disconnects, and firmware hangs
|
The utility survives hardware resets, cable disconnects, and firmware hangs
|
||||||
by default — it reconnects with exponential backoff (1s → 30s cap).
|
by default — it reconnects with exponential backoff (1s → 30s cap).
|
||||||
Use `--inactivity-timeout` to also trigger reconnect on silent firmware hangs.
|
Use `--inactivity-timeout` to also trigger reconnect on silent firmware hangs.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
- **[simtrace2](https://gitea.osmocom.org/sim-card/simtrace2)** — the upstream
|
- **[simtrace2](https://gitea.osmocom.org/sim-card/simtrace2)** — the upstream
|
||||||
SIMtrace2 hardware/firmware project that this tool sniffs from.
|
SIMtrace2 hardware/firmware project that the capture utility sniffs from.
|
||||||
- **[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)** —
|
- **[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)** —
|
||||||
FX2LP-based passive ISO 7816 sniffer that emits the same GSMTAP-SIM stream
|
FX2LP-based passive ISO 7816 sniffer that emits the same GSMTAP-SIM stream
|
||||||
on UDP 4729 (accepted by this project's GSMTAP listener).
|
on UDP 4729 (accepted by this project's GSMTAP listener).
|
||||||
+70
-54
@@ -1,16 +1,30 @@
|
|||||||
# simtrace2-pysniff
|
# SIMtrace Analyser
|
||||||
|
|
||||||
[English](README.md) | Русский
|
[English](README.md) | Русский
|
||||||
|
|
||||||
Замена `simtrace2-sniff` на Python — сниффер обмена с SIM-картой для
|
Анализатор APDU-трассировок в формате GSMTAP-SIM — захватывает обмен с
|
||||||
оборудования Osmocom SIMtrace2 (прошивка в режиме **trace**). Включает PWA
|
SIM/UICC из UDP-потока GSMTAP (или импортирует файлы PCAP/PCAPNG),
|
||||||
**simtrace-analyser** (в [`frontend/`](frontend/)) и анализирующий сервер,
|
декодирует его и отдаёт устанавливаемое веб-приложение для просмотра.
|
||||||
который и захватывает APDU-трафик, и отдаёт эту PWA.
|
В репозиторий также включён **simtrace2-pysniff** — замена `simtrace2-sniff`
|
||||||
|
на Python, которая захватывает трафик с оборудования Osmocom SIMtrace2
|
||||||
|
и может передавать GSMTAP этому анализатору.
|
||||||
|
|
||||||
**Единственная зависимость**: PyUSB (обёртка над libusb). Без libosmocore,
|

|
||||||
без libosmosim.
|
|
||||||
|
|
||||||

|
## Компоненты
|
||||||
|
|
||||||
|
- **Анализирующий сервер + PWA** (`simtrace-analyser-server`) — сохраняет
|
||||||
|
захваченный трафик в SQLite, декодирует его и отдаёт PWA
|
||||||
|
([`frontend/`](frontend/)) вместе с HTTP API на одном origin. Три режима
|
||||||
|
захвата: GSMTAP-листенер, захват напрямую с оборудования SIMtrace2 или
|
||||||
|
отключённый захват (только просмотр существующих записей).
|
||||||
|
- **Утилита захвата** (`simtrace2-pysniff`) — захватывает трафик
|
||||||
|
оборудования SIMtrace2 и выводит его в stdout, GSMTAP (Wireshark) или
|
||||||
|
PCAP-файлы. Это один из нескольких источников GSMTAP, которые принимает
|
||||||
|
анализатор (см. также
|
||||||
|
[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)).
|
||||||
|
- **PWA** ([`frontend/`](frontend/)) — окно трассировки с декодированными
|
||||||
|
APDU, SIM Toolkit, SMS и SCP80 OTA; сессии, импорт PCAP, интерфейс EN/RU.
|
||||||
|
|
||||||
## Возможности
|
## Возможности
|
||||||
|
|
||||||
@@ -41,7 +55,7 @@
|
|||||||
- Разбор ATR (пересчёт тактовой частоты, Fi/Di, битовая маска T)
|
- Разбор ATR (пересчёт тактовой частоты, Fi/Di, битовая маска T)
|
||||||
и FCP/FCI TLV
|
и FCP/FCI TLV
|
||||||
|
|
||||||
**PWA simtrace-analyser** ([`frontend/`](frontend/)):
|
**PWA SIMtrace Analyser** ([`frontend/`](frontend/)):
|
||||||
|
|
||||||
- Окно трассировки с режимами «Карта»/«Список» — proactive,
|
- Окно трассировки с режимами «Карта»/«Список» — proactive,
|
||||||
TERMINAL RESPONSE, ENVELOPE и AUTH-команды выделяются в режиме карты
|
TERMINAL RESPONSE, ENVELOPE и AUTH-команды выделяются в режиме карты
|
||||||
@@ -52,13 +66,14 @@
|
|||||||
## Требования
|
## Требования
|
||||||
|
|
||||||
- **Python 3.9+**
|
- **Python 3.9+**
|
||||||
- **PyUSB** — `pip install pyusb`
|
- **PyUSB** — `pip install pyusb` (нужен только для захвата с оборудования
|
||||||
|
SIMtrace2; захват GSMTAP/UDP и импорт PCAP работают без него)
|
||||||
- **libusb** — системная библиотека, обычно уже есть в Linux; в Windows
|
- **libusb** — системная библиотека, обычно уже есть в Linux; в Windows
|
||||||
нужно заменить драйвер SIMtrace2
|
нужно заменить драйвер SIMtrace2
|
||||||
(см. [Доступ к USB-устройству](#доступ-к-usb-устройству))
|
(см. [Доступ к USB-устройству](#доступ-к-usb-устройству))
|
||||||
- **Оборудование SIMtrace2** с прошивкой в режиме *trace*
|
- **Оборудование SIMtrace2** с прошивкой в режиме *trace* (только для
|
||||||
(VID `1d50`, PID `60e3`, USB class `ff`, subclass `01`). О сборке
|
прямого захвата; VID `1d50`, PID `60e3`, USB class `ff`, subclass `01`).
|
||||||
и прошивке trace-прошивки см.
|
О сборке и прошивке trace-прошивки см.
|
||||||
[проект SIMtrace2](https://gitea.osmocom.org/sim-card/simtrace2).
|
[проект SIMtrace2](https://gitea.osmocom.org/sim-card/simtrace2).
|
||||||
|
|
||||||
Других зависимостей нет. Вывод PCAP, GSMTAP и hex dump использует только
|
Других зависимостей нет. Вывод PCAP, GSMTAP и hex dump использует только
|
||||||
@@ -67,8 +82,8 @@
|
|||||||
## Установка
|
## Установка
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/anttro/simtrace2-pysniff.git
|
git clone https://github.com/anttro/simtrace-analyser.git
|
||||||
cd simtrace2-pysniff
|
cd simtrace-analyser
|
||||||
pip install pyusb
|
pip install pyusb
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -76,53 +91,27 @@ pip install pyusb
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip install -e .
|
pip install -e .
|
||||||
# затем запуск: simtrace2-pysniff [opts]
|
# затем запуск: simtrace-analyser-server [opts]
|
||||||
|
# или: simtrace2-pysniff [opts]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Запуск
|
## Запуск
|
||||||
|
|
||||||
Предоставляются два инструмента:
|
### Анализирующий сервер + PWA (`simtrace-analyser-server`)
|
||||||
|
|
||||||
### CLI сниффера (`simtrace2-pysniff`)
|
|
||||||
|
|
||||||
Захватывает трафик SIMtrace2 и выводит его в stdout, GSMTAP (Wireshark)
|
|
||||||
или файлы PCAP.
|
|
||||||
|
|
||||||
### Скрипт запуска
|
|
||||||
|
|
||||||
`./sniff-start.sh` выполняет предварительную проверку прав доступа к USB
|
|
||||||
(при неудаче печатает инструкцию по установке udev-правила) и затем
|
|
||||||
запускает модуль Python. В Windows используйте `sniff-start.bat` с теми же
|
|
||||||
параметрами (проверка прав не требуется).
|
|
||||||
|
|
||||||
```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
|
|
||||||
```
|
|
||||||
|
|
||||||
Параметры также можно задавать через переменные окружения:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
FORMAT=timestamp GSMTAP=127.0.0.1 ./sniff-start.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
### Анализирующий сервер + PWA (`simtrace2-pysniff-server`)
|
|
||||||
|
|
||||||
Сохраняет захваченный APDU-трафик в SQLite и отдаёт его по HTTP вместе
|
Сохраняет захваченный APDU-трафик в SQLite и отдаёт его по HTTP вместе
|
||||||
со встроенной PWA **simtrace-analyser** (`frontend/`) на том же origin.
|
со встроенной PWA **SIMtrace Analyser** (`frontend/`) на том же origin.
|
||||||
Три режима захвата:
|
Три режима захвата:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Слушать GSMTAP от simtrace2-pysniff (или оригинального simtrace2-sniff):
|
# Слушать GSMTAP от simtrace2-pysniff (или оригинального simtrace2-sniff):
|
||||||
simtrace2-pysniff-server --capture gsmtap
|
simtrace-analyser-server --capture gsmtap
|
||||||
|
|
||||||
# Захват напрямую с оборудования SIMtrace2 (внешний инструмент не нужен):
|
# Захват напрямую с оборудования SIMtrace2 (внешний инструмент не нужен):
|
||||||
simtrace2-pysniff-server --capture direct
|
simtrace-analyser-server --capture direct
|
||||||
|
|
||||||
# Просмотр/анализ существующих записей без бэкенда захвата:
|
# Просмотр/анализ существующих записей без бэкенда захвата:
|
||||||
simtrace2-pysniff-server --capture disabled
|
simtrace-analyser-server --capture disabled
|
||||||
```
|
```
|
||||||
|
|
||||||
Сервер пишет в stderr информацию о жизненном цикле захвата (каждая строка
|
Сервер пишет в stderr информацию о жизненном цикле захвата (каждая строка
|
||||||
@@ -174,7 +163,7 @@ CORS/Private-Network-Access не требуется. Используйте `--w
|
|||||||
|
|
||||||
Автономная копия PWA размещена на **https://simtrace.atroshin.ru**.
|
Автономная копия PWA размещена на **https://simtrace.atroshin.ru**.
|
||||||
Это чистый фронтенд: укажите в нём (Settings → Server URL) адрес
|
Это чистый фронтенд: укажите в нём (Settings → Server URL) адрес
|
||||||
локально запущенного `simtrace2-pysniff-server`.
|
локально запущенного `simtrace-analyser-server`.
|
||||||
|
|
||||||
> **Ограничение браузера:** когда PWA отдаётся с публичного HTTPS-хоста,
|
> **Ограничение браузера:** когда PWA отдаётся с публичного HTTPS-хоста,
|
||||||
> доступ к локальному серверу (`http://127.0.0.1:8081`) требует двух
|
> доступ к локальному серверу (`http://127.0.0.1:8081`) требует двух
|
||||||
@@ -185,7 +174,31 @@ CORS/Private-Network-Access не требуется. Используйте `--w
|
|||||||
> разрешить сайту (или принять запрос разрешения). Без разрешения
|
> разрешить сайту (или принять запрос разрешения). Без разрешения
|
||||||
> браузера запрос к `127.0.0.1` блокируется ещё до отправки preflight.
|
> браузера запрос к `127.0.0.1` блокируется ещё до отправки preflight.
|
||||||
|
|
||||||
### Модуль Python (кроссплатформенный)
|
### Утилита захвата (`simtrace2-pysniff`)
|
||||||
|
|
||||||
|
Захватывает трафик оборудования SIMtrace2 и выводит его в stdout, GSMTAP
|
||||||
|
(Wireshark) или PCAP-файлы. Это встроенная замена оригинального
|
||||||
|
инструмента Osmocom `simtrace2-sniff`.
|
||||||
|
|
||||||
|
Скрипт запуска `./sniff-start.sh` выполняет предварительную проверку прав
|
||||||
|
доступа к USB (при неудаче печатает инструкцию по установке udev-правила)
|
||||||
|
и затем запускает модуль Python. В Windows используйте `sniff-start.bat`
|
||||||
|
с теми же параметрами (проверка прав не требуется).
|
||||||
|
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
Параметры также можно задавать через переменные окружения:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
FORMAT=timestamp GSMTAP=127.0.0.1 ./sniff-start.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Кроссплатформенный запуск модуля:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
python -m simtrace2_pysniff
|
python -m simtrace2_pysniff
|
||||||
@@ -202,6 +215,9 @@ simtrace2-pysniff --format atr-time
|
|||||||
|
|
||||||
## Доступ к USB-устройству
|
## Доступ к USB-устройству
|
||||||
|
|
||||||
|
Нужен для захвата с оборудования SIMtrace2 (утилита и режим
|
||||||
|
`--capture direct` сервера); для источников GSMTAP и PCAP не требуется.
|
||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
Для доступа к устройству SIMtrace2 без root нужно udev-правило.
|
Для доступа к устройству SIMtrace2 без root нужно udev-правило.
|
||||||
@@ -229,7 +245,7 @@ sudo udevadm trigger
|
|||||||
|
|
||||||
После этого `python -m simtrace2_pysniff` работает так же, как в Linux.
|
После этого `python -m simtrace2_pysniff` работает так же, как в Linux.
|
||||||
|
|
||||||
## Параметры CLI
|
## Параметры CLI утилиты
|
||||||
|
|
||||||
```
|
```
|
||||||
python -m simtrace2_pysniff [OPTIONS]
|
python -m simtrace2_pysniff [OPTIONS]
|
||||||
@@ -295,15 +311,15 @@ for msg in session.iter_messages():
|
|||||||
|
|
||||||
## Восстановление
|
## Восстановление
|
||||||
|
|
||||||
Инструмент по умолчанию переживает сбросы оборудования, отключения кабеля
|
Утилита по умолчанию переживает сбросы оборудования, отключения кабеля
|
||||||
и зависания прошивки — он переподключается с экспоненциальной задержкой
|
и зависания прошивки — она переподключается с экспоненциальной задержкой
|
||||||
(1с → 30с). Используйте `--inactivity-timeout`, чтобы также переподключаться
|
(1с → 30с). Используйте `--inactivity-timeout`, чтобы также переподключаться
|
||||||
при «тихих» зависаниях прошивки.
|
при «тихих» зависаниях прошивки.
|
||||||
|
|
||||||
## Связанные проекты
|
## Связанные проекты
|
||||||
|
|
||||||
- **[simtrace2](https://gitea.osmocom.org/sim-card/simtrace2)** — вышестоящий
|
- **[simtrace2](https://gitea.osmocom.org/sim-card/simtrace2)** — вышестоящий
|
||||||
проект оборудования/прошивки SIMtrace2, с которого этот инструмент
|
проект оборудования/прошивки SIMtrace2, с которого утилита захвата
|
||||||
снимает трафик.
|
снимает трафик.
|
||||||
- **[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)** —
|
- **[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)** —
|
||||||
пассивный ISO 7816 сниффер на базе FX2LP, выдающий такой же поток
|
пассивный ISO 7816 сниффер на базе FX2LP, выдающий такой же поток
|
||||||
|
|||||||
+17
-17
@@ -297,7 +297,7 @@ body { font: 14px/1.5 system-ui, sans-serif; background: var(--bg); color: var(-
|
|||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<span class="capture-chip" id="capture-chip" style="display:none"></span>
|
<span class="capture-chip" id="capture-chip" style="display:none"></span>
|
||||||
<button id="install-btn" data-l10n="Install PWA">Install PWA</button>
|
<button id="install-btn" data-l10n="Install PWA">Install PWA</button>
|
||||||
<a href="https://github.com/anttro/simtrace2-pysniff" target="_blank" rel="noopener">GitHub</a>
|
<a href="https://github.com/anttro/simtrace-analyser" target="_blank" rel="noopener">GitHub</a>
|
||||||
<button id="lang-btn" onclick="toggleLang()" title="Language / Язык">EN</button>
|
<button id="lang-btn" onclick="toggleLang()" title="Language / Язык">EN</button>
|
||||||
<button id="theme-btn" onclick="toggleTheme()">☀️</button>
|
<button id="theme-btn" onclick="toggleTheme()">☀️</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -676,7 +676,7 @@ function desCbcDecrypt(keyBytes, ivBytes, ctBytes) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ──────────────────── C-APDU parsing (vendored from otaman) ────────────────────
|
// ──────────────────── C-APDU parsing (vendored from SIMple) ────────────────────
|
||||||
// Parses C-APDU hex streams (e.g. SCP80 secured-packet application messages)
|
// Parses C-APDU hex streams (e.g. SCP80 secured-packet application messages)
|
||||||
// into {label, hex, desc, children} trees.
|
// into {label, hex, desc, children} trees.
|
||||||
const PARSE_INS = {
|
const PARSE_INS = {
|
||||||
@@ -2504,16 +2504,16 @@ function updateVersionWarn(serverVersion) {
|
|||||||
const elv = el('version-warn');
|
const elv = el('version-warn');
|
||||||
const sv = parseVersion(serverVersion);
|
const sv = parseVersion(serverVersion);
|
||||||
if (sv === null) {
|
if (sv === null) {
|
||||||
elv.textContent = t('Server version unknown — update simtrace2-pysniff-server.');
|
elv.textContent = t('Server version unknown — update simtrace-analyser-server.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const cmpMin = compareVersions(serverVersion, MIN_SERVER_VERSION);
|
const cmpMin = compareVersions(serverVersion, MIN_SERVER_VERSION);
|
||||||
if (cmpMin !== null && cmpMin < 0) {
|
if (cmpMin !== null && cmpMin < 0) {
|
||||||
elv.textContent = t('Server version {v} is outdated. Please update simtrace2-pysniff-server.').replace('{v}', serverVersion);
|
elv.textContent = t('Server version {v} is outdated. Please update simtrace-analyser-server.').replace('{v}', serverVersion);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sv[0] > parseVersion(PWA_VERSION)[0]) {
|
if (sv[0] > parseVersion(PWA_VERSION)[0]) {
|
||||||
elv.textContent = t('Server version {v} is newer than expected. Update simtrace2-pysniff for full compatibility.').replace('{v}', serverVersion);
|
elv.textContent = t('Server version {v} is newer than expected. Update SIMtrace Analyser for full compatibility.').replace('{v}', serverVersion);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
elv.textContent = '';
|
elv.textContent = '';
|
||||||
@@ -2635,9 +2635,9 @@ async function checkConnection() {
|
|||||||
el('conn-instructions').innerHTML = `
|
el('conn-instructions').innerHTML = `
|
||||||
<h3>${t('Setup')}</h3>
|
<h3>${t('Setup')}</h3>
|
||||||
<ol>
|
<ol>
|
||||||
<li>${t('Clone and install simtrace2-pysniff:')}<br>
|
<li>${t('Clone and install SIMtrace Analyser:')}<br>
|
||||||
<code>git clone https://github.com/anttro/simtrace2-pysniff.git</code><br>
|
<code>git clone https://github.com/anttro/simtrace-analyser.git</code><br>
|
||||||
<code>cd simtrace2-pysniff && pip install -e .</code></li>
|
<code>cd simtrace-analyser && pip install -e .</code></li>
|
||||||
<li>${t('Start the server in direct mode (recommended):')}<br>
|
<li>${t('Start the server in direct mode (recommended):')}<br>
|
||||||
<code>./start.sh --capture direct</code><br>
|
<code>./start.sh --capture direct</code><br>
|
||||||
<span style="color:var(--text-muted);">${t('Requires SIMtrace2 hardware connected via USB; captures directly from the device.')}</span></li>
|
<span style="color:var(--text-muted);">${t('Requires SIMtrace2 hardware connected via USB; captures directly from the device.')}</span></li>
|
||||||
@@ -2681,7 +2681,7 @@ function onFilterChange() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ──────────────────── Versioning ────────────────────
|
// ──────────────────── Versioning ────────────────────
|
||||||
const PWA_VERSION = '1.22.21';
|
const PWA_VERSION = '2.0.0';
|
||||||
const MIN_SERVER_VERSION = '1.0.0';
|
const MIN_SERVER_VERSION = '1.0.0';
|
||||||
|
|
||||||
function parseVersion(v) {
|
function parseVersion(v) {
|
||||||
@@ -2705,8 +2705,8 @@ function compareVersions(a, b) {
|
|||||||
// ──────────────────── i18n ────────────────────
|
// ──────────────────── i18n ────────────────────
|
||||||
let currentLang = 'en';
|
let currentLang = 'en';
|
||||||
|
|
||||||
const HINT_BLOCKED = 'The browser likely blocked access to the local server. Allow local network access for this site (Chrome/Edge/Vivaldi: Site settings → Local network access → Allow) and confirm simtrace2-pysniff-server is running at {url}.';
|
const HINT_BLOCKED = 'The browser likely blocked access to the local server. Allow local network access for this site (Chrome/Edge/Vivaldi: Site settings → Local network access → Allow) and confirm simtrace-analyser-server is running at {url}.';
|
||||||
const HINT_UNREACHABLE = 'Couldn\'t reach the server. Check that simtrace2-pysniff-server is running and the URL is correct ({url}). If this page is served over HTTPS, also allow local network access in the browser (Site settings → Local network access → Allow).';
|
const HINT_UNREACHABLE = 'Couldn\'t reach the server. Check that simtrace-analyser-server is running and the URL is correct ({url}). If this page is served over HTTPS, also allow local network access in the browser (Site settings → Local network access → Allow).';
|
||||||
|
|
||||||
const LANG_RU = {
|
const LANG_RU = {
|
||||||
'Trace': 'Трассировка',
|
'Trace': 'Трассировка',
|
||||||
@@ -2785,10 +2785,10 @@ const LANG_RU = {
|
|||||||
'Empty file': 'Пустой файл',
|
'Empty file': 'Пустой файл',
|
||||||
'Install from the address bar:\nclick the install icon (⊕ / monitor icon) at the right\nend of Chrome\'s address bar, then "Install SIMtrace Analyser".':
|
'Install from the address bar:\nclick the install icon (⊕ / monitor icon) at the right\nend of Chrome\'s address bar, then "Install SIMtrace Analyser".':
|
||||||
'Установка из адресной строки:\nнажмите значок установки (⊕ / значок монитора) в правом\nконце адресной строки Chrome, затем «Установить SIMtrace Analyser».',
|
'Установка из адресной строки:\nнажмите значок установки (⊕ / значок монитора) в правом\nконце адресной строки Chrome, затем «Установить SIMtrace Analyser».',
|
||||||
[HINT_BLOCKED]: 'Браузер, вероятно, заблокировал доступ к локальному серверу. Разрешите доступ к локальной сети для этого сайта (Chrome/Edge/Vivaldi: Настройки сайта → Доступ к локальной сети → Разрешить) и убедитесь, что simtrace2-pysniff-server запущен по адресу {url}.',
|
[HINT_BLOCKED]: 'Браузер, вероятно, заблокировал доступ к локальному серверу. Разрешите доступ к локальной сети для этого сайта (Chrome/Edge/Vivaldi: Настройки сайта → Доступ к локальной сети → Разрешить) и убедитесь, что simtrace-analyser-server запущен по адресу {url}.',
|
||||||
[HINT_UNREACHABLE]: 'Не удалось связаться с сервером. Проверьте, что simtrace2-pysniff-server запущен и URL указан верно ({url}). Если эта страница открыта по HTTPS, также разрешите доступ к локальной сети в браузере (Настройки сайта → Доступ к локальной сети → Разрешить).',
|
[HINT_UNREACHABLE]: 'Не удалось связаться с сервером. Проверьте, что simtrace-analyser-server запущен и URL указан верно ({url}). Если эта страница открыта по HTTPS, также разрешите доступ к локальной сети в браузере (Настройки сайта → Доступ к локальной сети → Разрешить).',
|
||||||
'Setup': 'Настройка',
|
'Setup': 'Настройка',
|
||||||
'Clone and install simtrace2-pysniff:': 'Клонируйте и установите simtrace2-pysniff:',
|
'Clone and install SIMtrace Analyser:': 'Клонируйте и установите SIMtrace Analyser:',
|
||||||
'Start the server in direct mode (recommended):': 'Запустите сервер в режиме direct (рекомендуется):',
|
'Start the server in direct mode (recommended):': 'Запустите сервер в режиме direct (рекомендуется):',
|
||||||
'Requires SIMtrace2 hardware connected via USB; captures directly from the device.': 'Требуется оборудование SIMtrace2, подключённое по USB; захват выполняется напрямую с устройства.',
|
'Requires SIMtrace2 hardware connected via USB; captures directly from the device.': 'Требуется оборудование SIMtrace2, подключённое по USB; захват выполняется напрямую с устройства.',
|
||||||
'Then set the URL above to http://127.0.0.1:8081 and return to the Trace tab.': 'Затем укажите URL выше (http://127.0.0.1:8081) и вернитесь на вкладку «Трассировка».',
|
'Then set the URL above to http://127.0.0.1:8081 and return to the Trace tab.': 'Затем укажите URL выше (http://127.0.0.1:8081) и вернитесь на вкладку «Трассировка».',
|
||||||
@@ -2797,9 +2797,9 @@ const LANG_RU = {
|
|||||||
'Start the server:': 'Запустите сервер:',
|
'Start the server:': 'Запустите сервер:',
|
||||||
'Start a GSMTAP source in another terminal — either:': 'Запустите источник GSMTAP в другом терминале — либо:',
|
'Start a GSMTAP source in another terminal — either:': 'Запустите источник GSMTAP в другом терминале — либо:',
|
||||||
'(original Osmocom tool)': '(оригинальный инструмент Osmocom)',
|
'(original Osmocom tool)': '(оригинальный инструмент Osmocom)',
|
||||||
'Server version unknown — update simtrace2-pysniff-server.': 'Версия сервера неизвестна — обновите simtrace2-pysniff-server.',
|
'Server version unknown — update simtrace-analyser-server.': 'Версия сервера неизвестна — обновите simtrace-analyser-server.',
|
||||||
'Server version {v} is outdated. Please update simtrace2-pysniff-server.': 'Версия сервера {v} устарела. Обновите simtrace2-pysniff-server.',
|
'Server version {v} is outdated. Please update simtrace-analyser-server.': 'Версия сервера {v} устарела. Обновите simtrace-analyser-server.',
|
||||||
'Server version {v} is newer than expected. Update simtrace2-pysniff for full compatibility.': 'Версия сервера {v} новее ожидаемой. Обновите simtrace2-pysniff для полной совместимости.',
|
'Server version {v} is newer than expected. Update SIMtrace Analyser for full compatibility.': 'Версия сервера {v} новее ожидаемой. Обновите SIMtrace Analyser для полной совместимости.',
|
||||||
'Capture mode: GSMTAP — the server is listening for incoming UDP/GSMTAP traffic. Ensure a GSMTAP source (e.g. simtrace2-pysniff or simtrace2-sniff) is feeding the server.': 'Режим захвата: GSMTAP — сервер ожидает входящий UDP/GSMTAP-трафик. Убедитесь, что источник GSMTAP (например, simtrace2-pysniff или simtrace2-sniff) передаёт данные серверу.',
|
'Capture mode: GSMTAP — the server is listening for incoming UDP/GSMTAP traffic. Ensure a GSMTAP source (e.g. simtrace2-pysniff or simtrace2-sniff) is feeding the server.': 'Режим захвата: GSMTAP — сервер ожидает входящий UDP/GSMTAP-трафик. Убедитесь, что источник GSMTAP (например, simtrace2-pysniff или simtrace2-sniff) передаёт данные серверу.',
|
||||||
'Capture mode: direct — the server captures from SIMtrace2 hardware. Keep the device connected.': 'Режим захвата: direct — сервер захватывает данные с устройства SIMtrace2. Не отключайте устройство.',
|
'Capture mode: direct — the server captures from SIMtrace2 hardware. Keep the device connected.': 'Режим захвата: direct — сервер захватывает данные с устройства SIMtrace2. Не отключайте устройство.',
|
||||||
'Capture is disabled — only pre-captured traces (saved sessions / PCAP import) are available.': 'Захват отключён — доступны только ранее записанные трассировки (сохранённые сессии / импорт PCAP).',
|
'Capture is disabled — only pre-captured traces (saved sessions / PCAP import) are available.': 'Захват отключён — доступны только ранее записанные трассировки (сохранённые сессии / импорт PCAP).',
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'simtrace-analyser-v53';
|
const CACHE = 'simtrace-analyser-v54';
|
||||||
|
|
||||||
const PRECACHE = [
|
const PRECACHE = [
|
||||||
'index.html',
|
'index.html',
|
||||||
|
|||||||
+5
-5
@@ -3,9 +3,9 @@ requires = ["setuptools>=64"]
|
|||||||
build-backend = "setuptools.backends._legacy:_Backend"
|
build-backend = "setuptools.backends._legacy:_Backend"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "simtrace2-pysniff"
|
name = "simtrace-analyser"
|
||||||
version = "1.22.21"
|
version = "2.0.0"
|
||||||
description = "Python-based sniffing tool for SIMtrace2 hardware (SIM card communication sniffer)"
|
description = "SIMtrace Analyser — APDU trace analyser for GSMTAP/PCAP captures (bundles the simtrace2-pysniff utility)"
|
||||||
readme = {file = "README.md", content-type = "text/markdown"}
|
readme = {file = "README.md", content-type = "text/markdown"}
|
||||||
license = {text = "GPL-2.0-or-later"}
|
license = {text = "GPL-2.0-or-later"}
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.9"
|
||||||
@@ -14,8 +14,8 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
|
simtrace-analyser-server = "simtrace_analyser.__main__:main"
|
||||||
simtrace2-pysniff = "simtrace2_pysniff.__main__:main"
|
simtrace2-pysniff = "simtrace2_pysniff.__main__:main"
|
||||||
simtrace2-pysniff-server = "simtrace2_pysniff.server.__main__:main"
|
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
include = ["simtrace2_pysniff*"]
|
include = ["simtrace_analyser*", "simtrace2_pysniff*"]
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
"""simtrace2-pysniff — Python replacement for simtrace2-sniff."""
|
"""simtrace2-pysniff — Python replacement for simtrace2-sniff.
|
||||||
|
|
||||||
from .device import SniffSession, DeviceDisconnected, find_sniffer_device
|
Capture utility bundled with SIMtrace Analyser. Hardware access (PyUSB) is
|
||||||
from .protocol import parse_message, SniffMessage
|
imported lazily, so the GSMTAP/PCAP codec modules can be used without libusb.
|
||||||
from .gsmtap import GsmtapSender, GsmtapReceiver
|
"""
|
||||||
from .pcap import PcapWriter
|
|
||||||
from .dump import FileDumper, format_message
|
from simtrace_analyser.version import __version__
|
||||||
|
|
||||||
__version__ = '1.22.21'
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'SniffSession',
|
'SniffSession',
|
||||||
'DeviceDisconnected',
|
'DeviceDisconnected',
|
||||||
@@ -20,3 +19,28 @@ __all__ = [
|
|||||||
'format_message',
|
'format_message',
|
||||||
'__version__',
|
'__version__',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
_LAZY_IMPORTS = {
|
||||||
|
'SniffSession': ('.device', 'SniffSession'),
|
||||||
|
'DeviceDisconnected': ('.device', 'DeviceDisconnected'),
|
||||||
|
'find_sniffer_device': ('.device', 'find_sniffer_device'),
|
||||||
|
'parse_message': ('.protocol', 'parse_message'),
|
||||||
|
'SniffMessage': ('.protocol', 'SniffMessage'),
|
||||||
|
'GsmtapSender': ('.gsmtap', 'GsmtapSender'),
|
||||||
|
'GsmtapReceiver': ('.gsmtap', 'GsmtapReceiver'),
|
||||||
|
'PcapWriter': ('.pcap', 'PcapWriter'),
|
||||||
|
'FileDumper': ('.dump', 'FileDumper'),
|
||||||
|
'format_message': ('.dump', 'format_message'),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def __getattr__(name):
|
||||||
|
"""Import utility symbols on first use (PEP 562)."""
|
||||||
|
try:
|
||||||
|
module_name, attr = _LAZY_IMPORTS[name]
|
||||||
|
except KeyError:
|
||||||
|
raise AttributeError(f'module {__name__!r} has no attribute {name!r}') from None
|
||||||
|
from importlib import import_module
|
||||||
|
value = getattr(import_module(module_name, __name__), attr)
|
||||||
|
globals()[name] = value
|
||||||
|
return value
|
||||||
@@ -1 +0,0 @@
|
|||||||
"""simtrace2-pysniff server sub-package."""
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
"""simtrace_analyser — server side of SIMtrace Analyser.
|
||||||
|
|
||||||
|
Stores captured GSMTAP/PCAP APDU traffic in SQLite, decodes it, and serves
|
||||||
|
the PWA (``frontend/``) plus the HTTP API on a single origin.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .version import __version__
|
||||||
|
|
||||||
|
__all__ = ['__version__']
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
"""simtrace2-pysniff-server — CLI entry point.
|
"""simtrace-analyser-server — CLI entry point.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
simtrace2-pysniff-server --capture gsmtap [--gsmtap-port 4729] [--port 8081]
|
simtrace-analyser-server --capture gsmtap [--gsmtap-port 4729] [--port 8081]
|
||||||
simtrace2-pysniff-server --capture direct [--port 8081]
|
simtrace-analyser-server --capture direct [--port 8081]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -13,19 +13,19 @@ from http.server import HTTPServer
|
|||||||
from .server import RequestHandler
|
from .server import RequestHandler
|
||||||
from .database import Database, DEFAULT_DB_PATH
|
from .database import Database, DEFAULT_DB_PATH
|
||||||
from .capture import CaptureManager, GsmtapListener, DirectSniffer
|
from .capture import CaptureManager, GsmtapListener, DirectSniffer
|
||||||
from .. import __version__
|
from .version import __version__
|
||||||
|
|
||||||
|
|
||||||
def _default_web_dir():
|
def _default_web_dir():
|
||||||
# <repo>/frontend, whether run from source or an editable install.
|
# <repo>/frontend, whether run from source or an editable install.
|
||||||
# __file__ is <repo>/simtrace2_pysniff/server/__main__.py → up three levels.
|
# __file__ is <repo>/simtrace_analyser/__main__.py → up two levels.
|
||||||
return os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'frontend')
|
return os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'frontend')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
p = argparse.ArgumentParser(
|
p = argparse.ArgumentParser(
|
||||||
prog='simtrace2-pysniff-server',
|
prog='simtrace-analyser-server',
|
||||||
description='HTTP API + PWA server for SIMtrace2 APDU capture and analysis')
|
description='HTTP API + PWA server for APDU trace capture and analysis')
|
||||||
p.add_argument('--host', default='127.0.0.1',
|
p.add_argument('--host', default='127.0.0.1',
|
||||||
help='HTTP bind address (default: 127.0.0.1)')
|
help='HTTP bind address (default: 127.0.0.1)')
|
||||||
p.add_argument('--port', type=int, default=8081,
|
p.add_argument('--port', type=int, default=8081,
|
||||||
@@ -63,7 +63,7 @@ def main():
|
|||||||
server = HTTPServer((args.host, args.port), RequestHandler)
|
server = HTTPServer((args.host, args.port), RequestHandler)
|
||||||
server.web_dir = args.web_dir
|
server.web_dir = args.web_dir
|
||||||
|
|
||||||
print(f'simtrace2-pysniff-server v{__version__} — http://{args.host}:{args.port}', file=sys.stderr)
|
print(f'simtrace-analyser-server v{__version__} — http://{args.host}:{args.port}', file=sys.stderr)
|
||||||
print(f' capture mode: {args.capture}', file=sys.stderr)
|
print(f' capture mode: {args.capture}', file=sys.stderr)
|
||||||
if args.capture == 'gsmtap':
|
if args.capture == 'gsmtap':
|
||||||
print(f' GSMTAP port: {args.gsmtap_port}', file=sys.stderr)
|
print(f' GSMTAP port: {args.gsmtap_port}', file=sys.stderr)
|
||||||
@@ -6,7 +6,7 @@ import threading
|
|||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from ..gsmtap import (GsmtapReceiver, GSMTAP_SIM_ATR,
|
from simtrace2_pysniff.gsmtap import (GsmtapReceiver, GSMTAP_SIM_ATR,
|
||||||
GSMTAP_SIM_PPS_REQ, GSMTAP_SIM_PPS_RSP,
|
GSMTAP_SIM_PPS_REQ, GSMTAP_SIM_PPS_RSP,
|
||||||
GSMTAP_SIM_RST_EVENT, GSMTAP_SIM_VCC_EVENT)
|
GSMTAP_SIM_RST_EVENT, GSMTAP_SIM_VCC_EVENT)
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ class GsmtapListener:
|
|||||||
|
|
||||||
class DirectSniffer:
|
class DirectSniffer:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
from ..device import SniffSession
|
from simtrace2_pysniff.device import SniffSession
|
||||||
self._session = SniffSession(
|
self._session = SniffSession(
|
||||||
reconnect=True,
|
reconnect=True,
|
||||||
reconnect_delay_min=1.0,
|
reconnect_delay_min=1.0,
|
||||||
@@ -98,7 +98,7 @@ class DirectSniffer:
|
|||||||
self._session.close()
|
self._session.close()
|
||||||
|
|
||||||
def iter_messages(self):
|
def iter_messages(self):
|
||||||
from ..device import DeviceDisconnected
|
from simtrace2_pysniff.device import DeviceDisconnected
|
||||||
try:
|
try:
|
||||||
for msg in self._session.iter_messages():
|
for msg in self._session.iter_messages():
|
||||||
if not self._running:
|
if not self._running:
|
||||||
@@ -398,7 +398,7 @@ class Database:
|
|||||||
# Only card-level changes invalidate the selection. A
|
# Only card-level changes invalidate the selection. A
|
||||||
# waiting-time timeout interrupts a single TPDU but leaves the
|
# waiting-time timeout interrupts a single TPDU but leaves the
|
||||||
# card selected, so the phone usually just retries.
|
# card selected, so the phone usually just retries.
|
||||||
from ..protocol import (
|
from simtrace2_pysniff.protocol import (
|
||||||
CHANGE_FLAG_CARD_INSERT, CHANGE_FLAG_CARD_EJECT,
|
CHANGE_FLAG_CARD_INSERT, CHANGE_FLAG_CARD_EJECT,
|
||||||
CHANGE_FLAG_RESET_ASSERT, CHANGE_FLAG_RESET_DEASSERT,
|
CHANGE_FLAG_RESET_ASSERT, CHANGE_FLAG_RESET_DEASSERT,
|
||||||
)
|
)
|
||||||
@@ -4,7 +4,7 @@ Pure Python, no dependencies. Decodes raw TPDU bytes captured by
|
|||||||
the SIMtrace2 sniffer into structured dicts for the PWA to display.
|
the SIMtrace2 sniffer into structured dicts for the PWA to display.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ..gsmtap import GSMTAP_FLAG_BAD_FCS
|
from simtrace2_pysniff.gsmtap import GSMTAP_FLAG_BAD_FCS
|
||||||
|
|
||||||
# ──────────────────── Status Word names ────────────────────
|
# ──────────────────── Status Word names ────────────────────
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
"""HTTP API + static PWA server for simtrace2-pysniff."""
|
"""HTTP API + static PWA server for SIMtrace Analyser."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@@ -11,9 +11,9 @@ from urllib.parse import urlparse, parse_qs, quote
|
|||||||
|
|
||||||
from .database import Database
|
from .database import Database
|
||||||
from .capture import CaptureManager, GsmtapListener, DirectSniffer, _log
|
from .capture import CaptureManager, GsmtapListener, DirectSniffer, _log
|
||||||
from ..gsmtap import build_gsmtap_packet, GSMTAP_SIM_ATR, GSMTAP_SIM_APDU
|
from simtrace2_pysniff.gsmtap import build_gsmtap_packet, GSMTAP_SIM_ATR, GSMTAP_SIM_APDU
|
||||||
from ..pcap import build_pcap, parse_pcap, parse_pcapng
|
from simtrace2_pysniff.pcap import build_pcap, parse_pcap, parse_pcapng
|
||||||
from .. import __version__
|
from .version import __version__
|
||||||
|
|
||||||
|
|
||||||
# Static file serving (the PWA lives in <repo>/frontend, served by this server
|
# Static file serving (the PWA lives in <repo>/frontend, served by this server
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"""Single source of truth for the SIMtrace Analyser version."""
|
||||||
|
|
||||||
|
__version__ = '2.0.0'
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
@echo off
|
@echo off
|
||||||
setlocal enabledelayedexpansion
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
:: simtrace2-pysniff-server startup script for Windows
|
:: simtrace-analyser-server startup script for Windows
|
||||||
::
|
::
|
||||||
:: Usage: start.bat [--capture gsmtap|direct] [--port PORT] [--db PATH] [--gsmtap-port PORT]
|
:: Usage: start.bat [--capture gsmtap|direct] [--port PORT] [--db PATH] [--gsmtap-port PORT]
|
||||||
::
|
::
|
||||||
@@ -47,8 +47,8 @@ shift
|
|||||||
goto :parse_args
|
goto :parse_args
|
||||||
|
|
||||||
:run
|
:run
|
||||||
echo Starting simtrace2-pysniff-server...
|
echo Starting simtrace-analyser-server...
|
||||||
echo PWA (simtrace-analyser) will be served at http://127.0.0.1:%PORT%/
|
echo PWA (simtrace-analyser) will be served at http://127.0.0.1:%PORT%/
|
||||||
|
|
||||||
set "PYTHONPATH=%~dp0"
|
set "PYTHONPATH=%~dp0"
|
||||||
python -m simtrace2_pysniff.server --capture %CAPTURE% --port %PORT% --gsmtap-port %GSMTAP_PORT% %EXTRA_ARGS%
|
python -m simtrace_analyser --capture %CAPTURE% --port %PORT% --gsmtap-port %GSMTAP_PORT% %EXTRA_ARGS%
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# simtrace2-pysniff-server startup script
|
# simtrace-analyser-server startup script
|
||||||
#
|
#
|
||||||
# Usage: ./start.sh [--capture gsmtap|direct] [--port PORT] [--db PATH] [--gsmtap-port PORT]
|
# Usage: ./start.sh [--capture gsmtap|direct] [--port PORT] [--db PATH] [--gsmtap-port PORT]
|
||||||
#
|
#
|
||||||
@@ -71,6 +71,6 @@ ARGS+=(--port "$PORT")
|
|||||||
[[ "$CAPTURE" == "gsmtap" ]] && ARGS+=(--gsmtap-port "$GSMTAP_PORT")
|
[[ "$CAPTURE" == "gsmtap" ]] && ARGS+=(--gsmtap-port "$GSMTAP_PORT")
|
||||||
ARGS+=("${EXTRA_ARGS[@]}")
|
ARGS+=("${EXTRA_ARGS[@]}")
|
||||||
|
|
||||||
echo "Starting simtrace2-pysniff-server..." >&2
|
echo "Starting simtrace-analyser-server..." >&2
|
||||||
echo "PWA (simtrace-analyser) will be served at http://127.0.0.1:$PORT/" >&2
|
echo "PWA (simtrace-analyser) will be served at http://127.0.0.1:$PORT/" >&2
|
||||||
exec env PYTHONPATH="$PROJECT_DIR" python3 -m simtrace2_pysniff.server "${ARGS[@]}"
|
exec env PYTHONPATH="$PROJECT_DIR" python3 -m simtrace_analyser "${ARGS[@]}"
|
||||||
@@ -7,8 +7,8 @@ import tempfile
|
|||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from simtrace2_pysniff.server.capture import CaptureManager, gsmtap_msg_type
|
from simtrace_analyser.capture import CaptureManager, gsmtap_msg_type
|
||||||
from simtrace2_pysniff.server.database import Database
|
from simtrace_analyser.database import Database
|
||||||
|
|
||||||
|
|
||||||
class TestGsmtapSubtypeMapping(unittest.TestCase):
|
class TestGsmtapSubtypeMapping(unittest.TestCase):
|
||||||
@@ -67,7 +67,7 @@ class TestCaptureManager(unittest.TestCase):
|
|||||||
self.assertEqual(db.count_messages(sid), 1)
|
self.assertEqual(db.count_messages(sid), 1)
|
||||||
|
|
||||||
def test_direct_sniffer_device_disconnect(self):
|
def test_direct_sniffer_device_disconnect(self):
|
||||||
from simtrace2_pysniff.server.capture import DirectSniffer
|
from simtrace_analyser.capture import DirectSniffer
|
||||||
sn = DirectSniffer()
|
sn = DirectSniffer()
|
||||||
sn._session = _DisconnectingSession()
|
sn._session = _DisconnectingSession()
|
||||||
self.assertEqual(list(sn.iter_messages()), [])
|
self.assertEqual(list(sn.iter_messages()), [])
|
||||||
@@ -84,7 +84,7 @@ class TestCaptureManager(unittest.TestCase):
|
|||||||
self.assertEqual([m['type'] for m in msgs], ['gap', 'tpdu'])
|
self.assertEqual([m['type'] for m in msgs], ['gap', 'tpdu'])
|
||||||
|
|
||||||
def test_direct_sniffer_connected(self):
|
def test_direct_sniffer_connected(self):
|
||||||
from simtrace2_pysniff.server.capture import DirectSniffer
|
from simtrace_analyser.capture import DirectSniffer
|
||||||
|
|
||||||
class _FakeSession:
|
class _FakeSession:
|
||||||
connected = True
|
connected = True
|
||||||
@@ -96,7 +96,7 @@ class TestCaptureManager(unittest.TestCase):
|
|||||||
self.assertFalse(sn.connected)
|
self.assertFalse(sn.connected)
|
||||||
|
|
||||||
def test_device_connected_gsmtap_none(self):
|
def test_device_connected_gsmtap_none(self):
|
||||||
from simtrace2_pysniff.server.capture import GsmtapListener
|
from simtrace_analyser.capture import GsmtapListener
|
||||||
listener = GsmtapListener(bind_port=0)
|
listener = GsmtapListener(bind_port=0)
|
||||||
self.addCleanup(listener.stop)
|
self.addCleanup(listener.stop)
|
||||||
mgr, _ = self._manager(listener)
|
mgr, _ = self._manager(listener)
|
||||||
@@ -170,7 +170,7 @@ class _RaisingBackend:
|
|||||||
|
|
||||||
class TestGsmtapListenerRobustness(unittest.TestCase):
|
class TestGsmtapListenerRobustness(unittest.TestCase):
|
||||||
def test_listener_survives_bad_packet(self):
|
def test_listener_survives_bad_packet(self):
|
||||||
from simtrace2_pysniff.server.capture import GsmtapListener
|
from simtrace_analyser.capture import GsmtapListener
|
||||||
listener = GsmtapListener(bind_port=0)
|
listener = GsmtapListener(bind_port=0)
|
||||||
listener._receiver.close() # drop the real socket before swapping
|
listener._receiver.close() # drop the real socket before swapping
|
||||||
listener._receiver = _FlakyReceiver(listener, ['raise_non_sim', 'good'])
|
listener._receiver = _FlakyReceiver(listener, ['raise_non_sim', 'good'])
|
||||||
@@ -181,7 +181,7 @@ class TestGsmtapListenerRobustness(unittest.TestCase):
|
|||||||
self.assertEqual(listener.dropped, 1)
|
self.assertEqual(listener.dropped, 1)
|
||||||
|
|
||||||
def test_listener_survives_short_packet(self):
|
def test_listener_survives_short_packet(self):
|
||||||
from simtrace2_pysniff.server.capture import GsmtapListener
|
from simtrace_analyser.capture import GsmtapListener
|
||||||
listener = GsmtapListener(bind_port=0)
|
listener = GsmtapListener(bind_port=0)
|
||||||
listener._receiver.close() # drop the real socket before swapping
|
listener._receiver.close() # drop the real socket before swapping
|
||||||
listener._receiver = _FlakyReceiver(listener, ['raise_short', 'good'])
|
listener._receiver = _FlakyReceiver(listener, ['raise_short', 'good'])
|
||||||
|
|||||||
+146
-146
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -4,7 +4,7 @@ import struct
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from simtrace2_pysniff.pcap import build_pcap, PCAP_MAGIC, LINKTYPE_ETHERNET
|
from simtrace2_pysniff.pcap import build_pcap, PCAP_MAGIC, LINKTYPE_ETHERNET
|
||||||
from simtrace2_pysniff.server.server import _content_disposition
|
from simtrace_analyser.server import _content_disposition
|
||||||
|
|
||||||
FRAMING = 14 + 20 + 8 # Ethernet + IPv4 + UDP
|
FRAMING = 14 + 20 + 8 # Ethernet + IPv4 + UDP
|
||||||
|
|
||||||
|
|||||||
@@ -147,8 +147,8 @@ class TestTsresol(unittest.TestCase):
|
|||||||
|
|
||||||
class TestImportEndpoint(unittest.TestCase):
|
class TestImportEndpoint(unittest.TestCase):
|
||||||
def _handler(self, body):
|
def _handler(self, body):
|
||||||
from simtrace2_pysniff.server.server import RequestHandler
|
from simtrace_analyser.server import RequestHandler
|
||||||
from simtrace2_pysniff.server.database import Database
|
from simtrace_analyser.database import Database
|
||||||
|
|
||||||
class Fake(RequestHandler):
|
class Fake(RequestHandler):
|
||||||
def __init__(self, body, db):
|
def __init__(self, body, db):
|
||||||
@@ -224,12 +224,12 @@ class TestImportEndpoint(unittest.TestCase):
|
|||||||
class TestVersion(unittest.TestCase):
|
class TestVersion(unittest.TestCase):
|
||||||
def test_package_version(self):
|
def test_package_version(self):
|
||||||
from simtrace2_pysniff import __version__
|
from simtrace2_pysniff import __version__
|
||||||
self.assertEqual(__version__, '1.22.21')
|
self.assertEqual(__version__, '2.0.0')
|
||||||
|
|
||||||
def test_status_includes_version(self):
|
def test_status_includes_version(self):
|
||||||
import os
|
import os
|
||||||
from simtrace2_pysniff.server.server import RequestHandler
|
from simtrace_analyser.server import RequestHandler
|
||||||
from simtrace2_pysniff.server.database import Database
|
from simtrace_analyser.database import Database
|
||||||
from simtrace2_pysniff import __version__
|
from simtrace2_pysniff import __version__
|
||||||
|
|
||||||
class FakeCapture:
|
class FakeCapture:
|
||||||
@@ -259,8 +259,8 @@ class TestVersion(unittest.TestCase):
|
|||||||
class TestCaptureDisabled(unittest.TestCase):
|
class TestCaptureDisabled(unittest.TestCase):
|
||||||
def _handler(self, capture_mode):
|
def _handler(self, capture_mode):
|
||||||
import os
|
import os
|
||||||
from simtrace2_pysniff.server.server import RequestHandler
|
from simtrace_analyser.server import RequestHandler
|
||||||
from simtrace2_pysniff.server.database import Database
|
from simtrace_analyser.database import Database
|
||||||
|
|
||||||
class Fake(RequestHandler):
|
class Fake(RequestHandler):
|
||||||
def __init__(self, db, capture_mode):
|
def __init__(self, db, capture_mode):
|
||||||
|
|||||||
Reference in new issue
Block a user