- a card-side change is discovered by a read operation, so the timeline row now reads "Read EF_IMSI" — the row keeps its warn colour and layout — with "Changed on card!" as the trailing underlined link that opens the change modal; an update row keeps the generic "Details" link - the 'Changed on card' verb string is gone; the new 'Changed on card!' key carries the Russian «Изменено на карте!» - tests: the verb/label cases updated (card_change -> Read, "Read EF_LOCI") and the Files-view wiring test pins the kind-dependent link label; 527 Python + 61 Node tests - README/README_RUS describe the new row wording; PWA_VERSION/pyproject/ version.py 2.6.4, SW cache simtrace-analyser-v78
31 KiB
SIMtrace Analyser
English | Русский
Анализатор APDU-трассировок в формате GSMTAP-SIM — захватывает обмен с
SIM/UICC из UDP-потока GSMTAP (или импортирует файлы PCAP/PCAPNG),
декодирует его и отдаёт устанавливаемое веб-приложение для просмотра.
В репозиторий также включён simtrace2-pysniff — замена simtrace2-sniff
на Python, которая захватывает трафик с оборудования Osmocom SIMtrace2
и может передавать GSMTAP этому анализатору.
Компоненты
- Анализирующий сервер + PWA (
simtrace-analyser-server) — сохраняет захваченный трафик в SQLite, декодирует его и отдаёт PWA (frontend/) вместе с HTTP API на одном origin. Три режима захвата: GSMTAP-листенер, захват напрямую с оборудования SIMtrace2 или отключённый захват (только просмотр существующих записей). - Утилита захвата (
simtrace2-pysniff) — захватывает трафик оборудования SIMtrace2 и выводит его в stdout, GSMTAP (Wireshark) или PCAP-файлы. Это один из нескольких источников GSMTAP, которые принимает анализатор (см. также sigrok_iso7816_stream). - PWA (
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/):
- Окно трассировки с режимами «Карта»/«Список» — 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-устройству)
- Оборудование SIMtrace2 с прошивкой в режиме trace (только для
прямого захвата; VID
1d50, PID60e3, USB classff, subclass01). О сборке и прошивке trace-прошивки см. проект SIMtrace2.
Других зависимостей нет. Вывод PCAP, GSMTAP и hex dump использует только стандартную библиотеку Python.
Установка
git clone https://github.com/anttro/simtrace-analyser.git
cd simtrace-analyser
pip install pyusb
Или установите пакет в своё окружение:
pip install -e .
# затем запуск: simtrace-analyser-server [opts]
# или: simtrace2-pysniff [opts]
Запуск
Анализирующий сервер + PWA (simtrace-analyser-server)
Сохраняет захваченный APDU-трафик в SQLite и отдаёт его по HTTP вместе
со встроенной PWA SIMtrace Analyser (frontend/) на том же origin.
Три режима захвата:
# Слушать 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 сохраняет позицию читателя:
новые сообщения прижимают вид к низу, только пока читатель уже внизу,
поэтому переход не срывается живым опросом и не снимает выделение). У строк с записанным изменением
(изменение со стороны карты либо запись, изменившая или впервые показавшая
байты) есть кнопка Details: окно показывает полный путь файла (MF/EF_ICCID (2FE2),
ADF/EF_IMSI (6F07), DF_GSM_ACCESS/EF_KcGPRS (4F52), AID …/4F09 — DF
называется по меткам из EF.DIR, если они есть) и затронутую единицу — файл
целиком или изменившуюся запись — до и после изменения в виде 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 берутся из
нормативных таблиц (анекс 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 (по умолчанию: <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
с теми же параметрами (проверка прав не требуется).
./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
Параметры также можно задавать через переменные окружения:
FORMAT=timestamp GSMTAP=127.0.0.1 ./sniff-start.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 ., используйте консольный
скрипт напрямую:
simtrace2-pysniff --format atr-time
Доступ к USB-устройству
Нужен для захвата с оборудования SIMtrace2 (утилита и режим
--capture direct сервера); для источников GSMTAP и PCAP не требуется.
Linux
Для доступа к устройству SIMtrace2 без root нужно udev-правило.
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, чтобы заменить драйвер по умолчанию:
- Скачайте и запустите Zadig
- Подключите устройство SIMtrace2
- Выберите его в выпадающем списке (VID
1D50, PID60E3— "Osmocom SIMtrace 2") - Выберите WinUSB (или libusbK) как драйвер замены
- Нажмите 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 00timestamp: строки TPDU с локальным временем —[14:32:05.123] TPDU: a0 a4 00 00 02 3f 00atr-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 обычным образом.
Для постоянной работы в фоне с выводом в файлы:
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 &
Использование как библиотеки
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 — вышестоящий проект оборудования/прошивки SIMtrace2, с которого утилита захвата снимает трафик.
- sigrok_iso7816_stream — пассивный ISO 7816 сниффер на базе FX2LP, выдающий такой же поток GSMTAP-SIM на UDP 4729 (принимается GSMTAP-листенером этого проекта).
