20 KiB
Этап 1. Статический анализ — отчёт
- Дата: 2026-05-16
- Аналитик: vladtechno@gmail.com + Claude
- Источники: локальные APK в
recon/artifacts/, jadx 1.5.5 декомпиляция
1. Что разбирали
Два приложения одного вендора NaughtySoft:
| Приложение | APK | Версия | Назначение |
|---|---|---|---|
| «Водитель» | com.naughtysoft.TtcDriver (XAPK split) |
1.2.313 (build 323) | Основное приложение для водителей: балансы, штрафы, платежи, обращения, осмотры |
| «Механик» | com.naughtysoft.ttc |
1.8.13 (build 165) | Приложение для механиков парка: фото машин в 1С |
Оба от одного вендора (com.naughtysoft.*), технологический стек разный (см. §3) — признак того, что вендор находится в процессе технологической миграции и не унифицирует разработку.
2. Метаданные APK
Водитель (com.naughtysoft.TtcDriver)
| Параметр | Значение |
|---|---|
| Version | 1.2.313 (build 323) |
| Target SDK | 36 (Android 16) |
| Min SDK | 24 (Android 7) |
| Main Activity | com.naughtysoft.TtcDriver.MainActivity |
| Permissions | INTERNET, CAMERA, RECORD_AUDIO, STORAGE, WAKE_LOCK, POST_NOTIFICATIONS, FCM (c2dm.permission.RECEIVE), AD_ID, ACCESS_NETWORK_STATE |
| Permissions, которых НЕТ | GPS (ACCESS_FINE/COARSE_LOCATION), SMS, READ_CONTACTS, READ_PHONE_STATE, BIND_NOTIFICATION_LISTENER |
android:usesCleartextTraffic |
true (приложение умеет HTTP без TLS) |
network_security_config.xml |
отсутствует (TLS pinning через XML не настроен) |
| Распространение | XAPK с App Bundle (base + split: arm64_v8a, en, mdpi, zh) |
| Native ABI | только arm64_v8a (нет x86_64, нет armeabi-v7a) |
Отсутствие GPS-разрешений = приложение пассивное, не трекает позицию водителя. Отсутствие SMS-разрешений = OTP-перехвата автоматического нет, логин классический.
Механик (com.naughtysoft.ttc)
| Параметр | Значение |
|---|---|
| Version | 1.8.13 (build 165) |
| Target SDK | 33 (Android 13) |
| Min SDK | 19 (Android 4.4) |
| Application Label | «Механик» |
| Permissions | INTERNET, CAMERA, RECORD_AUDIO, ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, STORAGE, VIBRATE, NETWORK_STATE, WIFI_STATE |
| Native ABI | только arm64_v8a |
В Механике в отличие от Водителя есть GPS и используется Xamarin (см. §3).
3. Технологические стеки
Водитель — Flutter (Dart AOT)
Подтверждено наличием:
lib/arm64-v8a/libflutter.so— Flutter engine (11 МБ)lib/arm64-v8a/libapp.so— Dart AOT snapshot с бизнес-логикой (9.6 МБ)- Java-пакеты:
io/flutter/,dev/flutter/pigeon,_COROUTINE/, Kotlin runtime - Flutter-плагины:
dev/fluttercommunity/plus,com/baseflow/permissionhandler,com/jrai/flutter_keyboard_visibility,io/scer/pdfx,studio/midoridesign/gal,io/scer/pdf_renderer,dev/fluttered/map_launcher
Что это значит для реверса:
- Бизнес-логика приложения — в Dart, скомпилированном AOT в
libapp.so. jadx-декомпиляция Java/Kotlin даёт только тонкие plugin wrappers, не бизнес-код. - Все строковые константы (URL, paths, ключи) хранятся в
libapp.soкак plain ASCII → извлекаемы черезgrep -a/strings(см. §4). - HTTP-запросы Flutter делает через стандартный Dart
HttpClient→ видимы в mitmproxy как обычный HTTPS (без custom низкоуровневых трюков). - Дальнейший реверс Dart-кода (поведение, проверки) — через reFlutter (требует пересборки snapshot) или динамический анализ через mitmproxy. На статическом этапе ограничиваемся извлечёнными строками.
Механик — Xamarin (.NET/Mono)
Подтверждено наличием:
- Java-пакеты:
xamarin/,mono/,crc64xxxxxxxxxxxxxxxx/(CRC64-обфускация имён классов — характерный паттерн Xamarin) assets/assemblies/assemblies.blob(4 МБ) — упакованный контейнер всех .NET DLLassets/assemblies/assemblies.manifest(1.6 КБ) — манифест содержимого blob
Что это значит для реверса:
- jadx даёт ноль информации о логике Механика (всё в C# DLL).
- Для распаковки нужен pyxamstore (распаковать blob → отдельные DLL) + dnSpy/ILSpy для C# декомпиляции.
- Это отдельный поток работы, отложен. Приоритет — водитель.
Вывод о стеке вендора: Xamarin (Mechanic) сейчас deprecated Microsoft'ом в пользу .NET MAUI. Driver переписан на Flutter — современный стек. Вендор находится в процессе технологической миграции, что согласуется с фактом «прекратил доработки» — ресурсы ушли на перенос/переход, а не на обновление наших фич.
4. Сетевая инфраструктура
Все извлечены из libapp.so водительского приложения через grep -aoE 'https?://...'.
Бэкенды
| Endpoint | Назначение |
|---|---|
https://api.ttcontrol.naughtysoft.ru/api |
Главный API вендора NaughtySoft. Основной канал общения приложения с сервером. |
https://taksi.0nalog.com:1703/ |
Нестандартный порт 1703 — классический паттерн HTTP-сервиса 1С Предприятие. С большой вероятностью это сам сервер 1С PremiumPark, на котором работает конфигурация вендора. Имя 0nalog (вместо nalog) — возможный obscurity-приём. |
https://ttcdriver.firebaseio.com |
Firebase Realtime Database. Помимо FCM-пушей, часть данных может синхронизироваться через Realtime DB. Project ID: ttcdriver. |
Архитектурная гипотеза
Driver App (Flutter)
│
├──► api.ttcontrol.naughtysoft.ru (HTTPS, основное взаимодействие)
│ │
│ └──► (на бэке вендора, вероятно) ──► taksi.0nalog.com:1703 (1С)
│
├──► taksi.0nalog.com:1703 (возможно, прямые запросы — нужно подтвердить на этапе 2)
│
├──► ttcdriver.firebaseio.com (Realtime DB + FCM регистрация)
│
└──► FCM (Firebase Cloud Messaging — push-уведомления о штрафах и пр.)
Подтвердить факт прямых обращений на 0nalog:1703 (или это идёт только через прокси api.ttcontrol) — задача этапа 2 (mitmproxy).
Карта API endpoints
Извлечены пути /v1/... и /v3/... из libapp.so. Префикс домена — api.ttcontrol.naughtysoft.ru/api.
| Endpoint | Назначение (гипотеза) |
|---|---|
/v1/GetBalance |
Остаток баланса водителя |
/v1/GetFines |
Список штрафов |
/v1/GetChecks |
Чеки 54-ФЗ |
/v1/GetPayroll |
Зарплата/начисления v1 |
/v3/GetPayroll |
Зарплата/начисления v3 (новее) |
/v1/CreatePayment |
Инициация платежа |
/v1/GetPaymentDetails/ |
Детали платежа |
/v1/BanksSBP |
Список банков СБП — платежи через Систему Быстрых Платежей |
/v1/ChangeQIWIWalletCardNumber |
QIWI Wallet (legacy — QIWI закрылся в 2024, мёртвый код) |
/v1/CreateAppeal |
Создать обращение/заявку |
/v1/GetAppealHistory |
История обращений водителя |
/v1/AddMessageAppeal |
Добавить сообщение к обращению |
/v1/AddInspection |
Добавить осмотр (фото повреждений) |
/v1/FOTO/ |
Endpoint для фотографий |
/v1/AttractedAdd |
Добавить привлечённого (реферал?) |
/v1/AttractedList |
Список привлечённых |
/v1/OnLine |
Heartbeat / online status |
/v1/GetStatus/ |
Статус (требует расшифровки на этапе 2) |
/v1/Sell |
Продать что-то (требует расшифровки) |
/v1/GetListOfQuestions |
Список вопросов (FAQ / скрининг) |
Аутентификация
Найдены характерные строки в libapp.so:
Authorization— заголовок HTTPBearer— Bearer-токенapiKey— упоминание (вероятно Firebase API key)authorization(lowercase) — тоже встречается
Гипотеза: стандартная Bearer-token аутентификация. Логин → получение access_token → дальше во всех запросах Authorization: Bearer <token>. Подтвердить и понять lifecycle (refresh? expiry?) — задача этапа 3.
5. Защитные механизмы
TLS pinning
- На уровне Android system (network_security_config.xml): отсутствует — конфиг не найден в декомпиле.
- Cleartext traffic разрешён — приложение умеет HTTP без TLS (вероятно для
taksi.0nalog.com:1703). - Программный TLS pinning в Dart-коде: возможен, но в
libapp.soявных маркеров pinning'а (типаsetTrustedRoots, custom SecurityContext с pinned certs) поверхностный grep не выявил. Проверится на этапе 2: если mitmproxy сразу читает трафик — pinning'а нет; если нет — нужен reFlutter/Frida.
Прогноз: на этапе 2 mitmproxy будет работать без обхода TLS pinning. Это сильно упрощает разведку.
Root/Emulator detection
Не сканировалось специально (требует анализа Dart-кода в libapp.so). На этапе 2 проверим эмпирически — запустится ли приложение на эмуляторе. Учитывая что:
- Приложение Flutter (без agressive anti-debug по умолчанию)
- Нет premium-фич, ориентированных на security (банковский класс, антифрод)
— вероятность блокирующего detection'а низкая.
Request signing / HMAC
Маркеров явного HMAC (типа Mac.getInstance("HmacSHA256")) в Kotlin-стороне не найдено (большая часть Java-кода — Flutter engine + plugins, без бизнес-логики). В Dart возможна реализация, но это нестандартный паттерн для Flutter приложений — обычно полагаются на Bearer + TLS.
Прочее
- Play Integrity API — не используется (нет соответствующих библиотек в декомпиле).
- App attestation / SafetyNet — отсутствует.
6. Сторонние SDK и зависимости
Из Kotlin/Java декомпиляции водителя
com.google.firebase.*— Firebase (FCM, аналитика)com.google.android.gms.*— Google Play Servicesdev.flutter.pigeon— Flutter Pigeon (Dart ↔ Native bridges)io.flutter.*— Flutter enginecom.baseflow.permissionhandler— permission_handler plugincom.jrai.flutter_keyboard_visibility— keyboard visibility pluginio.scer.pdfx,io.scer.pdf_renderer— PDF rendering pluginsstudio.midoridesign.gal— gal plugin (сохранение в галерею)dev.fluttercommunity.plus— Flutter community pluginsdev.fluttered.map_launcher— map_launcher plugin (открытие карт)com.github.rmtmckenzie.native_device_orientation— ориентация устройстваcom.softmaestri.notification— кастомные нотификации (другой вендор?)com.getkeepsafe.relinker— динамическая загрузка native librariesorg.apache.commons— Apache Commonsorg.chromium.support_lib_boundary— WebView boundary library
НЕ найдено (важно):
ru.yoomoney.*— нет ЮКассы SDKru.tinkoff.*— нет Tinkoff Acquiring SDKcom.yandex.payments.*— нет Yandex Paycom.sberbank.*— нет Сбер SDKcom.squareup.okhttp3/retrofit2— нет (HTTP идёт через Dart)dagger,hilt— нет DI-фреймворков (Flutter сам по себе их не требует)
Вывод по платежам: на Kotlin-стороне платёжных SDK провайдеров нет. Платёж через /v1/CreatePayment скорее всего возвращает deep link (типа https://qr.nspk.ru/<id> для СБП или URL веб-страницы провайдера), и Flutter открывает его через стандартный intent. Это отличный сценарий для миграции — точно повторяемо в нашем приложении без необходимости лицензировать чьи-то SDK.
7. Промежуточные гипотезы
- Архитектура двухслойная: NaughtySoft API (
api.ttcontrol) — фасад, под капотом стоит 1С (taksi.0nalog:1703). Подтвердить на этапе 2: смотреть, идут ли с приложения прямые запросы на0nalog:1703(тогда оба слоя независимы), или только наapi.ttcontrol(тогда0nalog— публично доступный backend 1С только в декомпиле, а реально приложение его не дёргает). - Платежи через СБП + deep links, без нативных SDK провайдеров → легко повторимо в нашем будущем приложении.
- Фото-функция (
/v1/AddInspection,/v1/FOTO/) уже есть на бэке — она просто не выдана водителям в UI текущего приложения. Если на этапе 2 удастся снять трафик этих endpoints (через Механика или через намеренный вызов в Driver если кнопка где-то есть), мы получим готовый протокол для нашего блока B без дизайна с нуля. - QIWI код мёртв — индикатор того, что подрядчик не зачищает мёртвый функционал. Подтверждает «прекратили доработки».
- TLS pinning слабый или отсутствует → mitmproxy будет работать в этап 2 «из коробки».
8. Open questions для следующих этапов
- Какой формат запроса/ответа на
/v1/CreatePayment? (этап 2) - Использует ли приложение
/api/...префикс или/v1/...идут напрямую от корня? (этап 2) - Использует ли водитель
taksi.0nalog.com:1703напрямую или только через проксиapi.ttcontrol? (этап 2) - Программный TLS pinning в Dart — есть? (этап 2, эмпирически)
- Refresh-токен и его lifecycle — какой? (этап 3)
- Что приходит в FCM-сообщении при штрафе? Полный объект или только триггер «обнови данные»? (этап 5)
- Что лежит в
assemblies.blobМеханика? Те же endpoints или другие? (отдельный поток)
9. Блокеры и риски
| # | Риск | Митигация |
|---|---|---|
| R1 | ARM-only ABI в обоих APK — на x86_64 эмуляторе adb install падает с INSTALL_FAILED_NO_MATCHING_ABIS |
Для этапа 2 либо: (a) создать AVD x86_64 API 33+ с включённой ARM-translation, (b) использовать физическое Android-устройство, (c) использовать ARM-эмулятор (медленный) |
| R2 | Программный TLS pinning в Dart-коде возможен, но не подтверждён на статике | Проверить эмпирически на этапе 2; если есть — обойти через reFlutter |
| R3 | Механик на Xamarin требует отдельного тулинга (pyxamstore + dnSpy) для разбора C# логики | Отдельный поток работы; приоритет — водитель |
| R4 | Часть Dart-логики (валидация, формат запросов) невидима без динамики | Перенесено на этап 2 (mitmproxy) |
10. Артефакты
| Файл | Что |
|---|---|
recon/artifacts/original-driver.xapk |
Исходный XAPK водителя из APKPure |
recon/artifacts/driver-xapk/ |
Распакованный XAPK (base APK + splits + manifest.json) |
recon/artifacts/original-mechanic.apk |
APK Механика |
recon/artifacts/decompiled/driver/ |
jadx-декомпиляция водителя (4331 классов, 25 errors ≈ 0.6%) |
recon/artifacts/decompiled/mechanic/ |
jadx-декомпиляция Механика (даёт только Xamarin glue, не C# логику) |
recon/artifacts/native/driver/lib/arm64-v8a/libapp.so |
Извлечённый Dart AOT snapshot водителя — главный источник URL и endpoints |
11. Следующий шаг (этап 2)
Все необходимые данные для разворачивания этапа 2 готовы:
- Список endpoints для отслеживания — есть
- Базовые URL'ы — есть
- Подтверждено отсутствие XML-pinning'а — mitmproxy будет работать
- Топология FCM известна — этап 5 пойдёт от Firebase project
ttcdriver
Перед этапом 2 нужно решить ARM-блокер (R1): выбрать ARM-эмулятор / x86_64 с ARM translation / физическое устройство. Это открытый пункт для пользователя.
После этого по плану: установка приложения на стенд → mitmproxy + системный CA → проигрыш сценариев S1-S6 (см. §5 design-doc'а) → каталог реальных запросов/ответов.