Авторизованное исследование собственного автомобиля. Владелец изучает штатную телематику своей машины на изолированном лабораторном стенде. Сервер принимает кадры только от синтетического клиента (
client/fake_tbox.py) и/или собственного T-BOX владельца в бортовой сети; он не подключается к серверам производителя, ничего не пишет в живую машину и не перехватывает чужой трафик. Реальные хосты платформы используются исключительно как имена SAN в локальном лабораторном CA.
Приёмный сервер телематического протокола DCVP (Dongfeng Cloud Vehicle Platform) для блока T-BOX автомобиля VOYAH Courage / 岚图知音 四驱全球版.
Цель — принимать выгрузку штатного T-BOX на свой сервер (локальный, в бортовой сети),
без китайского облака: переиспользовать штатные датчики/GPS/антенны для собственной телеметрии
и заодно снять паразитный разряд 12 В (радио в flight, аплоад по Ethernet). Протокол
восстановлен статическим реверсом прошивки tsp_manager (T_BOX_H37A3630849BA), без правок
на живой машине.
⚠️ Исследовательский инструмент для собственного автомобиля. Секреты конкретной машины (ключи, сертификаты, ICCID/IMEI/MAC/VIN, GPS) в репозитории отсутствуют и не должны здесь появляться.
-
Схема данных —
proto/vcp_custom_message.proto(пакетtboxev.vcp, proto2, 68 сообщений / 534 поля), извлечена из встроенногоFileDescriptorProtoв.rodataбинаряtsp_manager. Ключевые:in_binding_info(идентификационный хендшейк),in_veh_data(97 полей состояния),in_location_data,in_driving_report/in_alarm_info,in_general_config(адрес платформы по протоколу). -
Обёртка кадра — GB/T 32960 (китайский нацстандарт телематики ЭМ), поверх mTLS-сокета:
off size поле 0 2 23 23(##, 起始符)2 1 cmd— командный флаг3 1 resp— флаг ответа (0xFE=запрос,0x01=успешный ACK)4 17 VIN (ASCII) 21 1 enc— шифрование data-unit (0x01=нет,0x02=AES)22 2 len— длина data-unit, big-endian24 N data-unit 24+N 1 BCC— XOR байтов[2 .. 24+N)Полный кадр =
25 + Nбайт. protobuftboxev.vcp.*едет в data-unit подcmd = 0xCB(заводской диапазон0x80..0xFE), а не отдельным каналом. -
Последовательность сессии:
login(0x01) → ACK → heartbeat(0x07) каждые heartBeat_Time сек + realtime(0x02)/custom(0xCB) по событию → logout(0x04). До логина данные буферизуются. При 3 неудачных логинах — ретрай-цикл 30 мин. -
Порты/транспорт:
6504/6507— DCVP-сессия (TCP mTLS, GB/T 32960);6553— HTTPSPOST /log/logReceiveGateway(загрузка логов, отдельный канал); KPI/PKI — HTTPS-JSON. -
mTLS: по разбору прошивки, T-BOX проверяет сервер по trust store в перезаписываемой области блока; владелец может (вручную, вне этого репо) указать своему блоку локальный CA из
certs/, чтобы блок доверял лабораторному серверу. Клиентский сертификат сервер принимает любой (илиVERIFYPEER=0) — упрощение стенда. -
Bootstrap PKI: до провижининга клиентского сертификата T-BOX поднимает TLS на заводском
tbox.pfx, затемCSR → client.crt.
- Терминирует mTLS нашим CA (порты
6504/6507). - deframe GB/T 32960: поиск старта
##, разбор заголовка, длина BE по смещению 22, проверка XOR-BCC, ресинхронизация сквозь мусор, придержка недочитанного кадра. - Диспетч по
cmd; custom-кадры (0xCB) распаковывает как protobuftboxev.vcp.*;enc=0x02(AES) помечает, не пытаясь парсить. - Генерирует GB-ACK (
resp=0x01): дляlogin/logout— эхоtime+serial, дляheartbeat— header-only. Без ACK T-BOX ловитresponse_TimeOutи переоткрывает сессию. - Отдельный HTTPS-хендлер на
6553дляPOST /log/logReceiveGateway. - Персистит всё принятое (см. «Хранение»): кадр сохраняется до отправки ACK, чтобы переоткрытие сессии клиентом не теряло уже принятое. Битые (BCC) кадры сохраняются, но не ACK'аются.
Каталог данных — DCVP_LOGDIR (в контейнере /app/logs, том ./logs, gitignored). Реализация —
server/store.py, SQLite из stdlib (WAL), сторонних зависимостей нет.
logs/dcvp.sqlite3 база: sessions / frames / log_uploads / meta (версия схемы)
logs/uploads/<ts>_<name> тела журналов T-BOX (POST /log/logReceiveGateway), как пришли
logs/raw/<ts>_<port>_<peer>.raw сырой захват каждого соединения (см. ниже)
| Таблица | Что | Ключевые поля |
|---|---|---|
sessions |
одно TCP-соединение на порту сессии | opened_ts/closed_ts, peer, port, vin, login_ts (момент login — до него кадры небиндингованные), frames, close_reason |
frames |
каждый кадр GB/T 32960 | ts (UTC, мс), session_id, vin, cmd/cmd_name, resp, enc, bcc_ok, logged_in, status, pb_type, pb_json, raw (кадр целиком: заголовок + data-unit + BCC), acked |
log_uploads |
загрузка журнала | query-параметры запроса (filename, sha256, …), content_type, size, заявленный/фактический sha256 и их совпадение, путь к файлу тела |
status кадра: decoded — custom-payload распознан как tboxev.vcp.* (в pb_json; при неоднозначности
типа рядом список _alternatives — разбор перебором типов, это эвристика), structural — data-unit
стандарта (login/logout), encrypted — enc=0x02, сырьё без разбора, undecoded — custom не распознан,
bad_bcc — контроль не сошёлся, empty — без data-unit (heartbeat).
Сырьё хранится всегда: структура ACK/login и точные теги полей статически не выведены, и именно по
raw их можно будет сверить с живым перехватом, не теряя уже накопленное.
- Схема версионируется (
meta.schema_version, миграции вstore.MIGRATIONS— только добавлять новые номера, старые не править). База новее кода → сервер не стартует. - Retention:
DCVP_RETENTION_DAYS(или--retention-days),0= хранить всё; чистка на старте и раз в час. Вручную:dcvp_export.py purge --days N. - Экспорт:
make stats,make export [SINCE=… TABLE=frames|sessions|log_uploads VIN=…]→logs/export.jsonl(raw— hex). Утилитаserver/dcvp_export.pyработает параллельно с сервером. - В консоль координаты (
lng/lat), ICCID и серийники блока/BT не печатаются — только в базу; data-unit login в консоли обрезан доtime+serial.
Страховка на первое время живого трафика: раскладка кадра выведена статически, и если она окажется не
такой, gb_deframe начнёт «терять» байты, а база получит bad_bcc/undecoded. Поэтому server/rawcap.py
пишет всё, что пришло из сокета и что мы ответили, до кадрирования — один файл на соединение
(и для сессии, и для канала логов). Записи: 'DR' | ts float64 | dir (0=rx, 1=tx, 2=meta) | len u32 | payload; файл открыт без буферизации, обрыв процесса ничего не теряет; пустые соединения файла не оставляют.
Хвост, не разобранный на кадры к моменту закрытия, дополнительно печатается в консоль как TAIL.
- Включён по умолчанию:
DCVP_RAW_CAPTURE=1; выключить0, когда формат подтверждён и базы достаточно. - Смотреть:
make raw(весь каталог) /make raw FILE=<имя>.raw LIMIT=0— hexdump по записям с метками времени и направлением. Программно —rawcap.iter_records(path). - Файлы подпадают под retention вместе с базой.
- Это plaintext после TLS: неудачный handshake (например, блок не доверяет нашему CA) сюда не попадает —
в консоли будет
TLS … handshake failed, а сам TLS-обмен можно снять только на хосте:tcpdump -i <iface> -w tbox.pcap 'host <ip блока> and (port 6504 or port 6507 or port 6553)'.
- Точная структура data-unit ACK сервера (какие поля кроме
serial_numberвалидирует T-BOX). Текущий ACK минимальный, но по коду достаточный для удержания сессии. - Реальные
heartBeat_Time/таймауты и включён ли AES data-unit (enc=0x02) — берутся из конфига блока. - Точная 54-байтная раскладка login data-unit (время/流水号/ICCID видны, порядок подсистем — по GB/T 32960.2).
- Приём remote-config
in_general_configот произвольного пира (второй вектор задания адреса платформы).
proto/ vcp_custom_message.proto схема выгрузки (tboxev.vcp)
server/ dcvp_server.py + Dockerfile приёмник GB/T 32960 + GB-ACK + HTTPS-логи
store.py хранилище: SQLite (кадры/сессии/загрузки) + uploads/
rawcap.py сырой захват соединений до кадрирования (logs/raw/)
dcvp_export.py stats / JSONL-экспорт / purge / hexdump raw-захвата
postman/ коллекция + environment проба HTTPS-канала логов (6553) из Postman, см. postman/README.md
client/ fake_tbox.py синтетический клиент: GB/T 32960-сессия без машины
harness/ README.md план qemu-стенда: живой tsp_manager → наш сервер
certs/ gen-ca.sh генератор своего CA + server/client cert (сами cert'ы gitignored)
docker-compose.yml · Makefile
make certs # свой CA + server/client cert (генерятся локально, в git не идут)
make build # образ; protobuf-байндинги генерятся внутри из .proto
make up # сервер на :6504/6507 (сессия) + :6553 (HTTPS-логи), лог в консоль
# в другом окне — синтетический клиент (GB/T 32960-сессия end-to-end):
make client
make stats # что накопилось в logs/dcvp.sqlite3
make export # logs/export.jsonl
make raw # hexdump сырого захвата соединений (logs/raw/)Канал логов (6553) можно пробовать из Postman — postman/README.md. Сессию GB/T 32960 Postman не умеет
(сырой TCP) — только make client.
Отладка без TLS/клиентского cert: добавить в command сервера --plaintext или --no-client-cert.
Синтетический клиент прогоняет login → binding → location → N×(heartbeat + veh_data) → logout и печатает ACK.
certs/ — наш лабораторный CA (не секреты машины), генерится локально и в git не коммитится
(.gitignore). ⛔ Реальные device-секреты T-BOX (rsa_aes_tbox.key, боевой client.crt, tbox.pfx,
настоящий ca.pem), а также VIN/ICCID/IMEI/MAC/GPS конкретной машины сюда класть нельзя. Значения в
client/fake_tbox.py — синтетические.