# 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 с **логическим каналом 0-19** (стандартные каналы из b2-b1, расширенные 4-19 из b4-b1 по TS 102 221 §10.3), secure messaging и chaining команд, имена INS из таблицы спецификаций, учёт SFI в P1/P2 для READ/UPDATE BINARY и команд с записями; ответ MANAGE CHANNEL показывает назначенный картой канал - Статусные слова — полный набор 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, безопасен для усечённых ATR) и FCP/FCI TLV; исторические байты разбираются как COMPACT-TLV объекты UICC (TS 102 221 §6.3.1) — card service data, **card capabilities** (ISO 7816-4 §8.1.1.2.7 таблица 88: способы назначения и заявленное максимальное число логических каналов, `8 or more` у карт с расширенными каналами) и завершающий индикатор состояния (LCS/SW) **PWA SIMtrace Analyser** ([`frontend/`](frontend/)): - Окно трассировки с режимами «Карта»/«Список» — proactive, TERMINAL RESPONSE, ENVELOPE и AUTH-команды выделяются в режиме карты - Сессии с прямыми ссылками (`#session=N`), импорт PCAP (`.pcap/.pcapng/.cap`), импорт трасс COMPRION (текстовый экспорт «ISO Protocol Layer» из Device Manager), поиск и **фильтр команд с множественным выбором** — любые команды и/или готовые пресеты («Проактивные сессии»: FETCH, TERMINAL PROFILE и все команды с ответом SW 91xx; «Файловые операции»: SELECT, READ/UPDATE BINARY и READ/UPDATE RECORD, вместе с GET RESPONSE, несущим данные выбранной команды) - Устанавливаемая 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 skipped=S` и `Capture stopped` (либо `ERROR: capture thread stopped on error …`, если поток захвата неожиданно умер). Неверно сформированные или не-SIM датаграммы GSMTAP отбрасываются и считаются (`dropped`), а не молча убивают захват; `tpdu`-пакет, вовсе не похожий на APDU (короче заголовка, недопустимый CLA `FF` или зарезервированный ISO класс `001x`), пропускается и считается (`skipped`) — посимвольный или миссемплированный поток больше не засоряет декодирование. Интервал 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** показывают те же имена файлов, что и вид Map (изменение со стороны карты выглядит как чтение со ссылкой **Изменено на карте!**, открывающей подробности), а щелчок по строке переходит прямо к APDU в трассе и оставляет его выделенным, пока не выбран другой (список APDU сохраняет позицию читателя: новые сообщения прижимают вид к низу, только пока читатель уже внизу, поэтому переход не срывается живым опросом и не снимает выделение). У строк с записанным изменением (изменение со стороны карты либо запись, изменившая или впервые показавшая байты) есть кнопка **Show diff** («Изменения»): окно показывает полный путь файла (`MF/EF_ICCID (2FE2)`, `ADF/EF_IMSI (6F07)`, `DF_TELECOM/DF_PHONEBOOK/4FA1`, `AID …/4F09` — вся цепочка DF из пути выбора в трассе, каждый DF назван по словарю спецификаций, а AID — по меткам из EF.DIR, если они есть) и затронутую единицу — файл целиком или изменившуюся запись — **до и после** изменения в виде hex-вида (до 16 байт в строке: короткий файл/запись заканчивает строку, а не дополняется `--`, и столбец смещений появляется, только когда представление занимает больше одной строки), причём изменённые байты подсвечены одним цветом, а впервые наблюдаемые — другим; ниже — счётчики изменённых/новых байт, ссылка на APDU и легенда цветов под представлениями, перечисляющая только реально показанные пометки. Изображения отдаёт `GET /api/sessions//files/image?key=…&change=…`; они восстанавливаются из текущего образа отменой более поздних изменений (`filetrack.FileTracker.change_images`) — поэтому ничего не дублируется по событиям. Каждое изменение значения записывается, так что значения точны; байт, который был лишь *впервые показан* после выбранного изменения, может отображаться известным в обоих видах. Данные даёт `simtrace_analyser/filetrack.py` (`GET /api/sessions//files`) — однопроходный анализатор сессии, который ведёт по ключу (DF, FID) и **через сбросы карты**: счётчики доступа (`select`/`read`/`update`, только успешные операции), разреженный образ наблюдаемого содержимого и журнал изменений. Изменения со стороны устройства — это операции UPDATE из трассы; изменения со стороны карты выводятся, когда чтение возвращает байты, отличающиеся от уже известных образу (никогда не виденные байты просто заполняют образ — это наблюдение, а не изменение). Файлы, доступные по SFI, попадают в ту же запись, что и доступ по пути/FID: соответствия SFI ↔ FID берутся из нормативных таблиц (анекс H.1 TS 102 221 и анексы H.1–H.12 TS 31.102; строки SFI ISIM — из TS 31.103) и из каждого показанного 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 отдаёт, но в этом виде пока не показываются. Имена файлов **привязаны к DF**: `simtrace_analyser/ef_tables.py` содержит полные таблицы файлов и SFI по каждому DF, извлечённые из закреплённых спецификаций (TS 102 221, TS 31.102 включая анексы SFI H.1–H.12, TS 31.103, TS 51.011), поэтому FID, повторно используемый в разных DF, разрешается в имя своего контекста — `4F09` это EF_SUPI_NAI в DF_5GS, но EF_PROSE_GC в DF_ProSe; `6F07` — EF_IMSI в USIM/SIM, но EF_IST в ISIM; `6F4E` — EF_EXT4 в DF_TELECOM, но EF_EXT5 в ADF USIM, — а плоская таблица служит запасным вариантом, когда DF неизвестен. `tests/extract_spec_tables.py` пересобирает закреплённый фикстур (`tests/data/spec_files.json`), `--check` повторно сверяет таблицы с PDF. Параметры сервера: ``` --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 (по умолчанию: /frontend) ``` Затем откройте **http://127.0.0.1:8081/** в браузере — PWA отдаётся сервером, поэтому UI и API используют один origin, и настройка CORS/Private-Network-Access не требуется. Используйте `--web-dir PATH`, чтобы отдавать другой каталог PWA (по умолчанию: `/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-листенером этого проекта).