chore: initial commit — recon artifacts + design spec + Phase 1 plan
This commit is contained in:
@@ -0,0 +1,283 @@
|
||||
# Этап 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 DLL
|
||||
- `assets/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` — заголовок HTTP
|
||||
- `Bearer` — 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 Services
|
||||
- `dev.flutter.pigeon` — Flutter Pigeon (Dart ↔ Native bridges)
|
||||
- `io.flutter.*` — Flutter engine
|
||||
- `com.baseflow.permissionhandler` — permission_handler plugin
|
||||
- `com.jrai.flutter_keyboard_visibility` — keyboard visibility plugin
|
||||
- `io.scer.pdfx`, `io.scer.pdf_renderer` — PDF rendering plugins
|
||||
- `studio.midoridesign.gal` — gal plugin (сохранение в галерею)
|
||||
- `dev.fluttercommunity.plus` — Flutter community plugins
|
||||
- `dev.fluttered.map_launcher` — map_launcher plugin (открытие карт)
|
||||
- `com.github.rmtmckenzie.native_device_orientation` — ориентация устройства
|
||||
- `com.softmaestri.notification` — кастомные нотификации (другой вендор?)
|
||||
- `com.getkeepsafe.relinker` — динамическая загрузка native libraries
|
||||
- `org.apache.commons` — Apache Commons
|
||||
- `org.chromium.support_lib_boundary` — WebView boundary library
|
||||
|
||||
### **НЕ найдено** (важно):
|
||||
|
||||
- **`ru.yoomoney.*`** — нет ЮКассы SDK
|
||||
- **`ru.tinkoff.*`** — нет Tinkoff Acquiring SDK
|
||||
- **`com.yandex.payments.*`** — нет Yandex Pay
|
||||
- **`com.sberbank.*`** — нет Сбер SDK
|
||||
- `com.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. Промежуточные гипотезы
|
||||
|
||||
1. **Архитектура двухслойная**: NaughtySoft API (`api.ttcontrol`) — фасад, под капотом стоит 1С (`taksi.0nalog:1703`). Подтвердить на этапе 2: смотреть, идут ли с приложения **прямые** запросы на `0nalog:1703` (тогда оба слоя независимы), или только на `api.ttcontrol` (тогда `0nalog` — публично доступный backend 1С только в декомпиле, а реально приложение его не дёргает).
|
||||
2. **Платежи через СБП + deep links**, без нативных SDK провайдеров → **легко повторимо** в нашем будущем приложении.
|
||||
3. **Фото-функция (`/v1/AddInspection`, `/v1/FOTO/`) уже есть на бэке** — она просто не выдана водителям в UI текущего приложения. Если на этапе 2 удастся снять трафик этих endpoints (через Механика или через намеренный вызов в Driver если кнопка где-то есть), мы получим готовый протокол для нашего блока B без дизайна с нуля.
|
||||
4. **QIWI код мёртв** — индикатор того, что подрядчик не зачищает мёртвый функционал. Подтверждает «прекратили доработки».
|
||||
5. **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'а) → каталог реальных запросов/ответов.
|
||||
Reference in New Issue
Block a user