← Все гайды AI Release
EN Подписаться на @ai_release1

Как проверить APK-файл на скрытый код: разбор DEX и smali через SQL-запросы

Обновлено: 15.10.2026 · AI Release · @ai_release1
Как проверить APK-файл на скрытый код: разбор DEX и smali через SQL-запросы

Проверка APK на скрытый код — это поиск в установочном пакете Android логики, о которой вам не сказали: догрузки сторонних модулей из интернета, тихой отправки данных или чтения идентификаторов устройства. Гайд для стажёра, который уже умеет распаковывать архивы и запускать команды в терминале, но никогда не разбирал DEX (скомпилированный байт-код приложения) и smali (читаемый текстовый аналог этого байт-кода). Подходит для APK с Android 5.0 и выше (minSdk 21+), проверяется на ПК с Windows, macOS или Linux — root на телефоне не нужен.

Требования и подготовка

Иллюстрация к разделу «Требования и подготовка»
Иллюстрация к разделу «Требования и подготовка»

Пошаговая инструкция

Иллюстрация к разделу «Пошаговая инструкция»
Иллюстрация к разделу «Пошаговая инструкция»

1. Создайте рабочую папку и скопируйте туда APK. Сразу считайте хеш файла: `sha256sum app.apk` (на Windows — `certutil -hashfile app.apk SHA256`).

**Пример:** вывод вида `3f2a...9c1b app.apk`. Запишите его: если хеш изменится, значит файл подменили, и все выводы ниже недействительны.

Почему это работает: хеш — отпечаток содержимого, любая правка байтов его меняет.

2. Распакуйте APK: `apktool d -f -o out app.apk`.

**Пример:** в папке `out` появляются `AndroidManifest.xml`, `res/`, `smali/`, `smali_classes2/`. Если приложение многомодульное, папок `smali_classesN` будет несколько.

Ожидаемый результат: `apktool` пишет «I: Copying assets and libs…» и завершается без ошибок.

3. Сделайте вторую копию — в Java: `jadx -d out_java app.apk`.

**Пример:** в `out_java/sources/com/example/app/` лежат `.java`-файлы с читаемыми именами методов.

Почему это работает: jadx умеет восстанавливать имена классов из отладочной информации, а smali — нет. Два представления дополняют друг друга.

4. Соберите индекс smali в SQLite — скрипт ниже кладёт каждую строку каждого `.smali` в таблицу.

import pathlib, sqlite3
db = sqlite3.connect("smali.db")
db.execute("CREATE TABLE IF NOT EXISTS lines (file TEXT, line INTEGER, code TEXT)")
for path in pathlib.Path("out").rglob("*.smali"):
    for i, text in enumerate(path.read_text(encoding="utf-8", errors="replace").splitlines(), 1):
        db.execute("INSERT INTO lines VALUES (?,?,?)", (str(path), i, text))
db.commit()

**Пример:** запуск `python index_smali.py`, на выходе — файл `smali.db` размером в десятки мегабайт.

Почему это работает: SQLite превращает поиск по тысячам файлов в один запрос, а главное — позволяет группировать и считать совпадения.

5. Ищите динамическую загрузку кода — главный признак скрытой логики.

SELECT file, line, code FROM lines WHERE code LIKE '%DexClassLoader%';
SELECT file, line, code FROM lines WHERE code LIKE '%Ljava/lang/reflect/Method%';

**Пример:** строка вида `invoke-virtual {v0, v1}, Ljava/lang/DexClassLoader;->loadClass:(...)`. Если рядом есть `const-string` с `http`, приложение скачивает и исполняет чужой код.

Ожидаемый результат: пустая выборка — признак нормы; 5–20 совпадений в одном классе — повод читать его целиком.

6. Проверьте сбор данных об устройстве.

SELECT file, COUNT(*) AS hits FROM lines
WHERE code LIKE '%getDeviceId%' OR code LIKE '%getImei%' OR code LIKE '%getSubscriberId%'
GROUP BY file ORDER BY hits DESC LIMIT 10;

**Пример:** вывод `smali/com/example/app/Analytics.smali | 7` означает, что класс собирает идентификаторы семь раз. Смотрите, куда эти значения передаются дальше.

Почему это работает: `GROUP BY` и `ORDER BY` показывают самый «жадный» до данных класс, а не разбросанные одиночные вызовы.

7. Отдельно посмотрите сетевые строки в smali и в Java-выгрузке.

SELECT DISTINCT file FROM lines WHERE code LIKE '%const-string%http%';

**Пример:** `smali/com/example/app/net/Uploader.smali` — откройте файл и посмотрите, какой URL собирается.

Ожидаемый результат: список доменов, которые приложение реально использует.

8. Разберите `AndroidManifest.xml`: разрешения (`INTERNET`, `READ_PHONE_STATE`, `READ_SMS`), экспортированные компоненты (`android:exported="true"`), список `activity`, `service`, `receiver`.

**Пример:** приложение-фонарик с разрешением `READ_SMS` — явное несоответствие заявленной функции.

Почему это важно: скрытый код начинается с лишнего права.

9. Проверьте встроенные базы приложения — они тоже SQL, и в них часто лежат конфиги, endpoints и токены. Файлы ищите в `out/assets/` и `out/res/raw/` с расширениями `.db`, `.sqlite`, `.sqlite3`.

sqlite3 out/assets/config.db ".tables"
sqlite3 out/assets/config.db "SELECT * FROM settings LIMIT 5;"

**Пример:** вывод `.tables` — `settings telemetry_queue`. Вторая таблица с очередью отправки — сигнал о фоновой передаче данных.

10. Прогоните строки на подозрительный контент: `strings app.apk | grep -iE "base64|exec|su |chmod"`.

**Пример:** длинная base64-строка внутри smali, которая декодируется в `.dex`. Это классическая упаковка скрытого модуля.

Ожидаемый результат: короткий список, каждую строку проверяете вручную в smali.

11. Сведите выводы в чек-лист и решайте по нему:

Возможные проблемы и решения

Иллюстрация к разделу «Возможные проблемы и решения»
Иллюстрация к разделу «Возможные проблемы и решения»

Симптом: `apktool` падает с ошибкой вида `Invalid resource` или «java.lang.OutOfMemoryError».У больших APK не хватает памяти. Решение: закройте браузер и IDE, увеличьте лимит — `java -Xmx4g -jar apktool.jar d app.apk`; если ошибка осталась, ищите более новую версию apktool. Проверьте, что распаковка дошла до конца: папка `smali` должна существовать, иначе шаг 4 проиндексирует пустоту.

Симптом: скрипт из шага 4 падает с `sqlite3.OperationalError: no such table` или создаёт базу на 0 строк.Вы запустили его не из той папки. Решение: команда `pwd` (или `cd`) должна показать папку, где лежит `out`; путь `pathlib.Path("out")` относительный. Проверьте, что индекс реально наполнился: `sqlite3 smali.db "SELECT COUNT(*) FROM lines;"` — счётчик должен быть в сотнях тысяч.

Симптом: SQL-запрос возвращает сотни совпадений, но среди них нет ничего опасного.Слишком широкий шаблон: `LIKE '%http%'` ловит и строки лицензий, и подсказки интерфейса. Решение: сужайте до конкретного класса — добавьте `AND file LIKE '%net%'`, или ищите пары: строка с `http` должна встречаться в одном файле с `invoke` на сетевой метод. Так вы отсекаете шум. Если задача — понять, ведёт ли найденный код себя автономно, пригодится отдельная методика проверки: [тесты автономности ИИ-агента и риски](https://ai-release.org/guides/kak-proverit-mozhet-li-ii-agent-rabotat-avtonomno-i-bez.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide).

FAQ

Что такое smali простыми словами?

Это текстовое представление байт-кода: каждая строка — одна инструкция виртуальной машины Android. Читается тяжелее Java, но именно в smali видно всё, что реально исполняется, включая код без исходников.

Можно ли проверить APK без root и без установки приложения?

Да. Всё в гайде делается на ПК над файлом: распаковка, декомпиляция и SQL-запросы root не требуют. Root нужен только если вы хотите снять дамп памяти уже запущенного приложения.

Заменяет ли такой разбор антивирус?

Нет. Антивирус ищет известные сигнатуры, а SQL-запросы по smali показывают поведение: загрузку кода, сбор идентификаторов, лишние права. Это разные слои проверки, и их стоит совмещать.

Почему SQL, если DEX — не база данных?

Мы сами переносим строки smali в SQLite на шаге 4. Дальше SQL даёт то, чего не даёт обычный поиск по файлам: `GROUP BY` по классам, счётчики совпадений и сортировку `ORDER BY` — то есть приоритет для ручного чтения.

🤖 Кратко для ИИ