Files
simtrace-analyser/README_RUS.md
T
catarrh cbb73536f4 Files view: drop the ASCII column from the change-details hex views
- the modal's hex views carried a 16-byte ASCII column per row; file data is
  rarely text and the column halved the room for the two before/after views,
  so it is gone: `hexImageRows` no longer builds an `ascii` field,
  `hexImageHtml` no longer emits it and the `.hex-ascii` CSS rule is removed
- tests: the hex-row test asserts the field is gone (so it cannot come back
  silently); README/README_RUS wording updated
- SW cache simtrace-analyser-v67 (cosmetic, no version bump)
2026-10-17 21:29:31 +03:00

417 lines
28 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.md) | Русский
Анализатор APDU-трассировок в формате GSMTAP-SIM — захватывает обмен с
SIM/UICC из UDP-потока GSMTAP (или импортирует файлы PCAP/PCAPNG),
декодирует его и отдаёт устанавливаемое веб-приложение для просмотра.
В репозиторий также включён **simtrace2-pysniff** — замена `simtrace2-sniff`
на Python, которая захватывает трафик с оборудования Osmocom SIMtrace2
и может передавать GSMTAP этому анализатору.
![PWA SIMtrace Analyser](frontend.png)
## Компоненты
- **Анализирующий сервер + 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 и трасс COMPRION,
интерфейс EN/RU.
## Возможности
**Декодер APDU** (на стороне сервера, отображается в PWA):
- Полное декодирование APDU — CLA по ISO 7816-4 / ETSI (логический канал,
secure messaging, chaining команд), имена INS из таблицы спецификаций,
учёт SFI в P1/P2 для READ/UPDATE BINARY и команд с записями
- Статусные слова — полный набор ISO 7816-4 плюс специфичные для UICC
значения: `91XX` (ожидается proactive-команда), `62F1/F2/F3`,
`63F1/F2` (больше данных), `9300` (SAT busy), `9850`, `9862–64`
- SIM Toolkit — SAT (GSM 11.14), CAT (TS 102 223) и USAT (TS 31.111):
proactive-команды с декодированными квалификаторами, TERMINAL RESPONSE,
ENVELOPE (Menu Selection, Call Control, SMS-PP download…); учитываются
известные несоответствия старых карт, нераспознанные TLV сохраняются
- TERMINAL CAPABILITY (TS 102 221 §11.1.19) — питание терминала (класс
напряжения, максимальный ток, тактовая частота), расширенные логические
каналы, дополнительные интерфейсы (UICC-CLF) и возможности eUICC
(SGP.22/SGP.32)
- SMS TPDU — SMS-DELIVER/SUBMIT, метки времени SCTS, информационные
элементы UDH
- SCP80 OTA secured packets — биты SPI (шифрование, требование/режим PoR,
RC/CC/DS, счётчик), алгоритм KIc/KID + набор ключей, TAR, CNTR/PCNTR;
декодирование Response Packet (PoR) по GET RESPONSE; **опциональная
расшифровка на клиенте** зашифрованных пакетов — вставьте 3DES/AES-ключ
карты в панели деталей, чтобы увидеть настоящие CNTR и защищённые данные
приложения (ключ остаётся в браузере, только в памяти; зашифрованные
в эфире CNTR/PCNTR/RC — это шифротекст, и они так и помечены); защищённое
сообщение приложения декодируется как поток **C-APDU** (SELECT,
UPDATE BINARY, …) с полной разбивкой CLA/INS/P1/P2/Lc/Le — сразу для
открытого текста или после расшифровки для зашифрованных пакетов
- Разбор ATR (пересчёт тактовой частоты, Fi/Di, битовая маска T)
и FCP/FCI TLV
**PWA SIMtrace Analyser** ([`frontend/`](frontend/)):
- Окно трассировки с режимами «Карта»/«Список» — proactive,
TERMINAL RESPONSE, ENVELOPE и AUTH-команды выделяются в режиме карты
- Сессии с прямыми ссылками (`#session=N`), импорт PCAP
(`.pcap/.pcapng/.cap`), импорт трасс COMPRION (текстовый экспорт
«ISO Protocol Layer» из Device Manager), поиск и фильтрация по типу
- Устанавливаемая PWA, тёмная тема, интерфейс на английском/русском
## Требования
- **Python 3.9+**
- **PyUSB** — `pip install pyusb` (нужен только для захвата с оборудования
SIMtrace2; захват GSMTAP/UDP и импорт PCAP работают без него)
- **libusb** — системная библиотека, обычно уже есть в Linux; в Windows
нужно заменить драйвер SIMtrace2
(см. [Доступ к USB-устройству](#доступ-к-usb-устройству))
- **Оборудование SIMtrace2** с прошивкой в режиме *trace* (только для
прямого захвата; VID `1d50`, PID `60e3`, USB class `ff`, subclass `01`).
О сборке и прошивке trace-прошивки см.
[проект SIMtrace2](https://gitea.osmocom.org/sim-card/simtrace2).
Других зависимостей нет. Вывод PCAP, GSMTAP и hex dump использует только
стандартную библиотеку Python.
## Установка
```sh
git clone https://github.com/anttro/simtrace-analyser.git
cd simtrace-analyser
pip install pyusb
```
Или установите пакет в своё окружение:
```sh
pip install -e .
# затем запуск: simtrace-analyser-server [opts]
# или: simtrace2-pysniff [opts]
```
## Запуск
### Анализирующий сервер + PWA (`simtrace-analyser-server`)
Сохраняет захваченный APDU-трафик в SQLite и отдаёт его по HTTP вместе
со встроенной PWA **SIMtrace Analyser** (`frontend/`) на том же origin.
Три режима захвата:
```sh
# Слушать GSMTAP от simtrace2-pysniff (или оригинального simtrace2-sniff):
simtrace-analyser-server --capture gsmtap
# Захват напрямую с оборудования SIMtrace2 (внешний инструмент не нужен):
simtrace-analyser-server --capture direct
# Просмотр/анализ существующих записей без бэкенда захвата:
simtrace-analyser-server --capture disabled
```
Сервер пишет в stderr информацию о жизненном цикле захвата (каждая строка
с локальным временем): `Capture started` при старте, периодический
heartbeat `Capture alive: session=N messages=M bytes=B dropped=D`
и `Capture stopped` (либо `ERROR: capture thread stopped on error …`,
если поток захвата неожиданно умер). Неверно сформированные или не-SIM
датаграммы GSMTAP отбрасываются и считаются (`dropped`), а не молча
убивают захват. Интервал heartbeat по умолчанию 60 с, настраивается
через `--log-interval SECONDS`.
GSMTAP-листенер также принимает декодер **sigrok-iso7816-stream**
(https://github.com/anttro/sigrok_iso7816_stream) — пассивный SIM-сниффер
на базе FX2-логического анализатора, выдающий тот же формат GSMTAP-SIM
на UDP 4729. Его стандартные подтипы PPS-запроса/ответа (`0x02`/`0x03`)
декодируются как PPS, а пользовательские подтипы для событий линий RST/VCC
(`0x10`/`0x11`, добавлены для совместимости с тем проектом) — как временные
маркеры `RESET ASSERTED` / `RST de-asserted — ATR follows` и `VCC ON/OFF`;
сброс или пропадание питания корректно сбрасывает отслеживание выбранного
файла. Когда событие
RST несёт измеренную частоту CLK (декодер v1.9.0+, опциональное расширение
полезной нагрузки), следующий ATR получает частоту CLK, скорость передачи
данных и ETU, вычисленные из его TA1 Fi/Di
(`data_rate = clk_hz × D / F`); измерение потребляется ATR, поэтому
следующий ATR без сброса не получит устаревшую скорость (поздно пришедшее
измерение восстанавливается для «своего» ATR). GSMTAP-пакеты с флагом
десинхронизации `GSMTAP_FLAG_BAD_FCS` (устанавливается тем декодером при
нарушении кадрирования) отображаются в таймлайне как
помеченная/десинхронизированная запись, чтобы такие артефакты было легко
заметить при разборе, а не молча искажали декодирование.
### Импорт текстового экспорта COMPRION
Кнопка **Import COMPRION trace** принимает экспорт **ISO Protocol Layer**
(табулированный текст) из COMPRION Device Manager — байтовое представление
протокола (ATR, PPS, APDU с кодами возврата). Экспорт *ISO Application
Layer* не содержит необработанных байтов ответов и не нужен; при его выборе
выводится указание на файл Protocol Layer (та же проверка выполняется в PWA
до загрузки и на сервере — для вызовов API).
Обмены перекодируются в такие же «проводные» TPDU, какие несёт захват
GSMTAP (обмен case 4 превращается в команду, статусное слово `61 xx` и GET
RESPONSE с данными ответа и итоговым SW), поэтому связывание
команда/ответ, декодирование FCP и файлов и фильтры работают так же, как
для живого захвата.
**Точные метки времени:** диалог импорта запрашивает экспорт *ISO Protocol
Layer* (обязательный) и, при наличии, соответствующий экспорт *ISO
Application Layer* — два отдельных поля выбора, имена выбранных файлов
видны, а ошибка остаётся в диалоге до исправления или отмены. С обоими
файлами метки времени Application Layer по обменам становятся таймлайном
трассы: окно сессии и интервалы между APDU точные; без него таймлайн —
оценка, описанная ниже. Перед импортом проверяется, что файлы относятся к
одной сессии (`Session Name`, при отсутствии — `Working directory`; метки
времени экспорта у слоёв различаются и не сравниваются) и что число обменов
совпадает (экспорты должны покрывать один и тот же набор обменов) — при
несовпадении импорт отклоняется, а не смешивает сессии. У ATR и обмена PPS
блоков в Application Layer нет, поэтому они получают метку времени
предыдущего обмена.
**Только Protocol Layer:** столбец `Delay` измеряет лишь активность
карты/линии — паузы хоста между сериями APDU в него не входят — поэтому
таймлайн сохраняет порядок сообщений и короткие интервалы, но заметно
короче реальной сессии. Его единица измерения задаётся предполагаемой
тактовой частотой 3,8 МГц (`CLK_HZ` в `simtrace_analyser/comprion.py`) —
это масштаб, а не измерение: в экспорте нет CLK, и импорт намеренно его не
выдумывает (ATR показывает F/D из файла, без аннотации скорости/ETU, в
отличие от захвата сниффером sigrok-iso7816-stream). Окно настенных часов
сессии берётся из метки времени `Session Name` в файле, если она там есть.
Обмены длиннее 255 байт (APDU с расширенной длиной) невозможно представить
в хранимом «проводном» виде — они пропускаются с пометкой в отчёте импорта.
### Просмотр файлов (сопровождение состояния файлов)
В боковой панели вкладки «Трассировка» появился выбор вида (*Map* / *Stats* /
**Files** — выпадающий список, три подписи-вкладки уже не помещались). Вид
Files — это таймлайн файловых операций по всей трассе: строки **Read**,
**Update** и **Changed on card** показывают те же имена файлов, что и вид Map,
а щелчок по строке переходит к APDU в трассе. У строк с записанным изменением
(изменение со стороны карты либо запись, изменившая или впервые показавшая
байты) есть кнопка **Details**: окно показывает затронутую единицу — файл
целиком или изменившуюся запись — **до и после** изменения в виде hex-вида
(16 байт в строке, никогда не наблюдавшиеся байты — `--`), причём изменённые
байты подсвечены одним цветом, а впервые наблюдаемые — другим; ниже —
счётчики изменённых/новых байт и ссылка на APDU.
Изображения отдаёт `GET /api/sessions/<id>/files/image?key=…&change=…`; они
восстанавливаются из текущего образа отменой более поздних изменений
(`filetrack.FileTracker.change_images`) — поэтому ничего не дублируется по
событиям. Каждое изменение значения записывается, так что значения точны;
байт, который был лишь *впервые показан* после выбранного изменения, может
отображаться известным в обоих видах.
Данные даёт `simtrace_analyser/filetrack.py` (`GET /api/sessions/<id>/files`) —
однопроходный анализатор сессии, который ведёт по ключу (DF, FID) и **через
сбросы карты**: счётчики доступа (`select`/`read`/`update`, только успешные
операции), разреженный образ наблюдаемого содержимого и журнал изменений.
Изменения со стороны устройства — это операции UPDATE из трассы; изменения со
стороны карты выводятся, когда чтение возвращает байты, отличающиеся от уже
известных образу (никогда не виденные байты просто заполняют образ — это
наблюдение, а не изменение). Файлы, доступные по SFI, попадают в ту же
запись, что и доступ по пути/FID: соответствия SFI ↔ FID берутся из
нормативных таблиц (TS 102 221 Annex H, TS 31.102 Annex H) и из каждого
показанного FCP с DO `88`, включая правило спецификации «SFI = 5 младших бит
FID» при отсутствии DO; SFI, использованный до того, как связь выучена,
остаётся предварительным и сливается с разрешённой записью. Контекст DF
следует TS 102 221 §8.4/Table 8.2 (выбор EF оставляет текущим его родительский
DF, включая выбор по пути); приложения, выбранные по неизвестному AID,
получают в качестве идентичности DF сам AID и имя из EF.DIR, если он
показан в трассе. Ограничения: образ замораживается после 64 КБ
наблюдаемых байт на файл (`image_truncated`), журнал изменений хранит
последние 1000 событий на файл (`changes_total` продолжает считать).
Счётчики API отдаёт, но в этом виде пока не показываются.
Параметры сервера:
```
--host ADDR адрес привязки HTTP (по умолчанию: 127.0.0.1)
--port PORT порт HTTP-сервера (по умолчанию: 8081)
--db FILE путь к базе SQLite (по умолчанию: ~/.simtrace-analyser/sessions.db)
--capture MODE gsmtap | direct | disabled (по умолчанию: gsmtap)
--gsmtap-port PORT UDP-порт листенера GSMTAP (по умолчанию: 4729)
--web-dir PATH каталог статических файлов PWA (по умолчанию: <repo>/frontend)
```
Затем откройте **http://127.0.0.1:8081/** в браузере — PWA отдаётся
сервером, поэтому UI и API используют один origin, и настройка
CORS/Private-Network-Access не требуется. Используйте `--web-dir PATH`,
чтобы отдавать другой каталог PWA (по умолчанию: `<repo>/frontend`).
### Размещённая PWA (лендинг)
Автономная копия PWA размещена на **https://simtrace.atroshin.ru**.
Это чистый фронтенд: укажите в нём (Settings → Server URL) адрес
локально запущенного `simtrace-analyser-server`.
> **Ограничение браузера:** когда PWA отдаётся с публичного HTTPS-хоста,
> доступ к локальному серверу (`http://127.0.0.1:8081`) требует двух
> вещей: сервер должен отправлять
> `Access-Control-Allow-Private-Network: true` (этот сервер отправляет),
> и браузер должен иметь разрешение на доступ к локальной сети —
> в Chrome/Edge/Vivaldi: Настройки сайта → Доступ к локальной сети →
> разрешить сайту (или принять запрос разрешения). Без разрешения
> браузера запрос к `127.0.0.1` блокируется ещё до отправки preflight.
### Утилита захвата (`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
python -m simtrace2_pysniff
python -m simtrace2_pysniff --format timestamp --gsmtap 127.0.0.1:4729
python -m simtrace2_pysniff --pcap trace.pcap
```
Если пакет установлен через `pip install -e .`, используйте консольный
скрипт напрямую:
```sh
simtrace2-pysniff --format atr-time
```
## Доступ к USB-устройству
Нужен для захвата с оборудования SIMtrace2 (утилита и режим
`--capture direct` сервера); для источников GSMTAP и PCAP не требуется.
### Linux
Для доступа к устройству SIMtrace2 без root нужно udev-правило.
```sh
sudo cp 70-simtrace2-pysniff.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
```
Затем переподключите USB-кабель SIMtrace2. Правило использует
`TAG+="uaccess"` (ACL systemd-logind) — членство в группе и права на
запись для всех не нужны.
### Windows
В Windows нет libusb-совместимого драйвера для SIMtrace2. Используйте
[Zadig](https://zadig.akeo.ie/), чтобы заменить драйвер по умолчанию:
1. Скачайте и запустите **Zadig**
2. Подключите устройство SIMtrace2
3. Выберите его в выпадающем списке (VID `1D50`, PID `60E3` — "Osmocom SIMtrace 2")
4. Выберите **WinUSB** (или libusbK) как драйвер замены
5. Нажмите **Replace Driver** (однократная операция)
После этого `python -m simtrace2_pysniff` работает так же, как в Linux.
## Параметры CLI утилиты
```
python -m simtrace2_pysniff [OPTIONS]
--format, -f FORMAT Формат вывода (по умолчанию: hex)
hex — обычный hex-дамп
timestamp — строки TPDU с префиксом [ЧЧ:ММ:СС.мс]
atr-time — строки TPDU с префиксом [СССС.мс] от последнего ATR
--gsmtap HOST[:PORT] Отправлять ATR/TPDU как GSMTAP по UDP (порт по умолчанию 4729)
--pcap FILE Записывать перехваченные данные в PCAP для Wireshark
--output, -o FILE Записывать hex-дамп в файл
--no-stdout Не выводить hex-дамп в stdout
--vendor HEX USB vendor ID (по умолчанию: 0x1d50)
--product HEX USB product ID (по умолчанию: автоопределение)
Параметры восстановления:
--no-reconnect Выходить при отключении USB вместо переподключения
--reconnect-delay-min SEC Минимальная задержка переподключения (по умолчанию: 1.0)
--reconnect-delay-max SEC Максимальная задержка переподключения (по умолчанию: 30.0)
--backoff-factor N Множитель экспоненциальной задержки (по умолчанию: 1.5)
--inactivity-timeout SEC Переподключаться после N секунд тишины (по умолчанию: выключено)
```
## Форматы вывода
- **`hex`** (по умолчанию): обычный hex-дамп — `ATR: 3b 9e ...`,
`TPDU: a0 a4 00 00 02 3f 00`
- **`timestamp`**: строки TPDU с локальным временем —
`[14:32:05.123] TPDU: a0 a4 00 00 02 3f 00`
- **`atr-time`**: строки TPDU с секундами от последнего ATR —
`[0001.234] TPDU: a0 a4 00 00 02 3f 00`
Сообщения, не являющиеся TPDU (ATR, PPS, изменения состояния карты, Fi/Di),
выводятся одинаково во всех форматах.
- **GSMTAP**: сообщения ATR и APDU по UDP (порт 4729) в Wireshark
- **PCAP**: сообщения ATR/TPDU как пакеты Ethernet/IP/UDP/GSMTAP,
открываются в Wireshark
## Фоновый режим / демон
SIGHUP автоматически игнорируется, когда stdin не является терминалом
(т.е. при запуске в фоне или через канал). Сессии в терминале на переднем
плане по-прежнему обрабатывают SIGHUP обычным образом.
Для постоянной работы в фоне с выводом в файлы:
```sh
nohup ./sniff-start.sh --output /tmp/sniff.log &> /tmp/sniff-err.log &
# или
FORMAT=timestamp nohup python -m simtrace2_pysniff --output /tmp/sniff.log &> /tmp/sniff-err.log &
```
## Использование как библиотеки
```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()}")
```
## Восстановление
Утилита по умолчанию переживает сбросы оборудования, отключения кабеля
и зависания прошивки — она переподключается с экспоненциальной задержкой
(1с → 30с). Используйте `--inactivity-timeout`, чтобы также переподключаться
при «тихих» зависаниях прошивки.
## Связанные проекты
- **[simtrace2](https://gitea.osmocom.org/sim-card/simtrace2)** — вышестоящий
проект оборудования/прошивки SIMtrace2, с которого утилита захвата
снимает трафик.
- **[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)** —
пассивный ISO 7816 сниффер на базе FX2LP, выдающий такой же поток
GSMTAP-SIM на UDP 4729 (принимается GSMTAP-листенером этого проекта).