Языки: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
neverd mobile восстанавливает читаемый Java из APK, DEX и smali только встроенным движком NeverD. Независимо реализованные средства чтения используют общую типизированную модель Dalvik и генератор Java с ограниченным объёмом работы. Эта экспериментальная функция CLI не обещает полного восстановления произвольных APK. Контейнеры APK и вывод Java недоступны через нативный C SDK, SDK плагинов Python, загрузчик GUI и neverd decompile --language.
Восстановленный Java-код — реконструкция байткода. Исходные комментарии, форматирование, особенности выбора исходного языка и удалённые идентификаторы недоступны; байткод Kotlin также преобразуется в Java. Успешное выполнение не доказывает семантическую эквивалентность и не гарантирует, что каждый метод удастся скомпилировать. Этот процесс не запускает анализируемые приложения.
После сборки NeverD выберите новый каталог вывода:
neverd mobile app.apk -o recovered-app
neverd mobile classes.dex -o recovered-dex
neverd mobile MainActivity.smali -o recovered-class
neverd mobile decoded/smali -o recovered-javaОткройте recovered-app/sources/ для чтения файлов Java и recovered-app/report.json для просмотра перечня входных файлов и ограничений. Если классы smali ссылаются друг на друга, предпочтительнее передать каталог.
| Компонент | Требование | Выбор |
|---|---|---|
| NeverD | Соберите цель neverd инструментами с поддержкой C++20. Мобильный процесс встроен в нативный CLI и не вызывает интерпретатор Python. Распространяйте исполняемый файл вместе с нативными библиотеками, необходимыми вашей сборке. |
build/bin/neverd / PATH |
Восстановление Android использует только встроенный движок NeverD на C++20 и не требует среды выполнения Python или Java. Поддерживаются представимые обычные объявления и операции DEX 035, 037–040 и smali. DEX 041, динамические вызовы вроде invoke-custom, некоторые пути инициализации, неизвестные семантические аннотации или операции и идентификаторы, непредставимые в Java, приводят к явной ошибке. Приём формата файла не означает поддержку всех его инструкций и объявлений.
Разрешение имён Java различает заголовок и тело класса и учитывает известные случаи затенения имён в одном пакете; если без объявлений внешних суперклассов или интерфейсов нельзя однозначно определить типы либо ссылки в сгенерированном вспомогательном Java-коде, восстановление явно отклоняется, причём только для java.lang.Object без доступного объявления предполагается отсутствие наследуемых типов-членов. Литералы smali с плавающей точкой округляются непосредственно до целевой одинарной или двойной точности с сохранением полученного битового представления.
Встроенный движок C++ сохраняет и проверяет поддерживаемые метаданные Signature классов, полей и методов: переменные типов, массивы, подстановочные типы, ограничения и затенение имён на уровне метода. Результат стирания типов должен соответствовать идентичности исходного объявления DEX. Throws сохраняется; неподтверждённая иерархия исключений отклоняется. Обобщённое наследование или подстановка типов членов, повторное создание мостовых методов, вызовы обобщённых методов, параметризованные внутренние типы и сопоставление скрытых параметров конструкторов по-прежнему явно не поддерживаются без необходимых доказательств.
Пустая, видимая во время выполнения аннотация-маркер Java 8 @java.lang.Deprecated сохраняется у классов, полей, методов и конструкторов. Аннотации параметров, аннотированные статические инициализаторы, другие уровни видимости и значения элементов, например since или forRemoval, отклоняются. После повторной компиляции CI отдельно сравнивает атрибут Deprecated файла класса и аннотации времени выполнения, включая контрольные объявления без аннотаций и сгенерированные вспомогательные методы; вспомогательные методы не входят в число исходных методов.
DEX и smali также сохраняют платформенную аннотацию @android.annotation.SuppressLint у классов, полей, методов и конструкторов. Требуются видимость build и единственный массив строк value; сохраняются пустые массивы и строки, повторяющиеся значения, порядок и экранированные символы. Аннотации параметров и статических инициализаторов по-прежнему не поддерживаются. CI использует Android SDK, проверяет аннотации с политикой CLASS после повторной компиляции и сравнивает поведение программы.
Встроенный движок также сохраняет видимые во время выполнения @Retention, @Target, @Documented и @Inherited у поддерживаемых объявлений аннотаций и создаёт настоящее объявление @interface. Этот поднабор не допускает полей, методов, параметров типов или вложенных объявлений. Поддерживаются аннотации верхнего уровня и статические аннотации-члены с подтверждёнными именами, доступностью и принадлежностью внешнему типу; локальные и анонимные области видимости отклоняются.
Применение пустого маркера к объявлению класса, интерфейса или аннотации требует соответствующего доступного определения маркера в том же наборе анализируемых классов. Retention и Target должны разрешать конкретное применение. Объявление SOURCE можно восстановить, но его применение, сохранившееся во входных данных, отклоняется; для CLASS или отсутствующего Retention нужна видимость DEX build (0), для RUNTIME — runtime (1). Отсутствующий Retention отличается от явно заданного CLASS, а отсутствующий Target — от пустого массива; порядок массива Target сохраняется. Значения Target ограничены Java 8; более новые значения, например MODULE и RECORD_COMPONENT, отклоняются. @Inherited сохраняется без копирования унаследованных применений в прямые аннотации подклассов.
Объявления элементов аннотаций и их значения по умолчанию, непустые пользовательские применения, пользовательские маркеры у полей/методов/параметров, внешние определения аннотаций, контейнеры повторяемых аннотаций и kotlin.Metadata по-прежнему не поддерживаются. Определение маркера выводится как объявление класса без методов элементов или вспомогательных методов и не увеличивает число восстановленных методов.
CI обрабатывает собственные примеры Java 8 через D8 и NeverD, повторно компилирует весь сгенерированный Java-код и сравнивает полные метаданные Signature, результаты рефлексии и поведение. Эти проверки не подтверждают полное восстановление реальных приложений.
cmake --build build --target neverd
./build/bin/neverd mobile app.apk -o recovered-appПути с пробелами заключайте в кавычки.
& .\build\bin\neverd.exe mobile .\app.apk -o .\recovered-appПри сборке с несколькими конфигурациями исполняемый файл может находиться в build/bin/Release/. Соблюдайте обычные требования этой сборки к распространению нативных библиотек.
Таблица входов ниже описывает восстановление. Описанные далее режимы запросов используют более узкие границы проверки.
| Вход | Поведение | Существенное ограничение |
|---|---|---|
.apk |
Проверяется весь ZIP, затем совместно анализируются корневые classes.dex, classes2.dex и последующие пронумерованные DEX-файлы |
Только код; ресурсы и манифест не декодируются |
.dex |
Проверка и разбор DEX 035 или 037–040 встроенным средством чтения | DEX 041 и неподдерживаемые объявления или операции вызывают ошибку; переименование или обрезание файла не создаёт корректный байткод |
.smali |
Анализируется переданный класс | Соседние классы, на которые он ссылается, не загружаются неявно |
| Каталог smali | Файлы .smali собираются рекурсивно и анализируются совместно |
Включите вложенные классы и зависимые корневые каталоги smali в каталог входа |
Для анализа распакованного дерева APK с каталогами smali/ и smali_classes2/ передайте их общий каталог. Бэкенд получает только файлы .smali, но сначала проверяется и копируется всё переданное дерево; крупные посторонние ресурсы тоже учитываются в лимитах входа. Небольшой каталог только с нужными корневыми каталогами smali сокращает объём работы.
Разделённые APK обрабатываются как отдельные входы. Каждый APK с DEX можно обработать независимо, но команда не объединяет комплект APK; части только с ресурсами завершаются ошибкой из-за отсутствия корневого DEX. .aab, .apks, .xapk, .odex, .oat и .vdex не принимаются как мобильные входы.
Ресурсы APK, AndroidManifest.xml, файлы assets, JNI/нативные библиотеки и код, загружаемый во время выполнения, не восстанавливаются как Java. Извлеките нативную библиотеку .so отдельно и используйте neverd decompile library.so -o library.c. Для этого статического процесса зашифрованный или упакованный код должен уже быть доступен в виде обычного DEX/smali; распаковка защищённого кода, подключение к устройствам и обход защиты не выполняются.
neverd mobile app.apk --list-classes
neverd mobile classes.dex --list-classes --class-prefix com.example
neverd mobile app.apk --list-classes --class-prefix Lcom/example/ --json
neverd mobile app.apk --list-classes -o classes.txtЭтот запрос читает идентификаторы классов без декодирования тел методов и
генерации Java. Промежуточный каталог и внешняя среда выполнения не нужны.
Текстовый вывод содержит по одному точному дескриптору DEX на строку, сначала в
порядке каталога ZIP, затем в порядке определений DEX. --class-prefix принимает
префикс дескриптора или имя пакета с точками; выполняется буквальное сопоставление
префикса без проверки границы пакета. Дублирующиеся определения классов, в том
числе между DEX-файлами, явно вызывают ошибку. Идентификаторы классов, которые
невозможно без потерь представить в UTF-8, также вызывают ошибку.
Без -o запрос пишет в stdout. В режиме запроса -o задаёт новый файл, а не
каталог восстановления. Существующие файлы остаются нетронутыми. Результаты
накапливаются до успешной проверки всех выбранных DEX: некорректный DEX,
обнаруженный позже, предотвращает публикацию частичного инвентаря. --json
включает число совпавших классов, общее число классов, число DEX и
validation_scope: "dex-envelope-and-class-identities".
Проверяются все имена ZIP, заголовки, диапазоны и заявленные ограничения ресурсов.
Распаковываются и проверяются по CRC только корневой classes.dex и
пронумерованные classesN.dex; целостность содержимого посторонних ресурсов не
проверяется. Для каждого DEX сохраняются проверки заголовка, SHA-1, Adler-32,
границ карты и используемых метаданных классов. Тела методов и неиспользуемые
метаданные не проверяются. Это запрос инвентаризации, а не проверка целостности
всего архива и не доказательство восстановления Java. DEX 041, разделы
method-handle и custom-call-site по-прежнему не поддерживаются. Обычный путь
полного восстановления по-прежнему проверяет содержимое всех записей архива.
Действуют --timeout, --max-files и --max-bytes. Лимит классов учитывает все
определения во всех DEX до фильтрации; невыбранные записи ZIP также учитываются
в ограничениях архива. Операция инвентаризации не принимает параметры
iOS и входы smali.
Поиск прямых операндов инструкций без генерации Java:
neverd mobile app.apk --find-refs string --query 'login failed' --json
neverd mobile classes.dex --find-refs type --query 'Lcom/example/Service;' --exact
neverd mobile app.apk --find-refs method --query '->connect(' --owner 'Lcom/example/Client;'
neverd mobile app.apk --find-refs field --query 'Lcom/example/State;->ready:Z' --exact -o refs.jsonlПоиск выполняет буквальное сопоставление подстрок с учётом регистра. --exact
сопоставляет цель целиком: содержимое строки, дескриптор типа, идентификатор
метода вида Lpkg/Type;->name(I)V или поля вида Lpkg/Type;->name:I.
--owner ограничивает владельца целевого метода или поля точным дескриптором.
Он не фильтрует метод, содержащий ссылку. Пустые запросы, входы smali и сочетание
параметров запроса с параметрами восстановления не поддерживаются; метасимволы
регулярных выражений считаются обычными буквальными символами.
По умолчанию выводится один компактный объект JSON на вхождение (JSON Lines).
--json возвращает отчёт со счётчиками и массивом references. Каждая строка
содержит dex_entry, полный идентификатор содержащего метода method,
pc_code_units (16-битные единицы от первой инструкции метода), opcode, kind,
target_index (локальный для DEX) и target. Сохраняются все вхождения, включая
общий код, отнесённый к нескольким определениям методов. Строковые записи также
содержат точные единицы target_utf16; target равен null, если одиночный
суррогат не позволяет опубликовать UTF-8 без потерь. Остальные идентификаторы
должны допускать представление в UTF-8.
Сканер использует границы инструкций, проверки операндов, обработку payload и
проверки потока управления средства чтения для восстановления. Непосредственные
значения и данные payload переключателей/массивов не могут стать ссылками.
Перед установкой code_scan_complete: true проверяется каждое определённое тело
кода, даже если нет совпавших целей. defined_method_count включает нативные и
абстрактные объявления; scanned_method_count считает определения с телами, а
scanned_code_item_count — различные физические тела в каждом DEX.
matching_pool_entries считает совпавшие цели, в том числе неиспользуемые.
validation_scope: "dex-code-references" охватывает оболочку DEX, таблицы
идентификаторов, принадлежность классов и членов, а также прочитанные данные
кода, исключений и отладки. Аннотации, статические кодированные значения,
использования только в объявлениях и неиспользуемые метаданные находятся вне
границ запроса. Это не восстановление Java и не верификатор ART. Неподдерживаемый
код и некорректные прочитанные данные явно вызывают ошибку. Проверка APK имеет
ту же границу выбранного содержимого, что и инвентаризация классов; целостность
содержимого невыбранных ресурсов не проверяется.
Результаты накапливаются по всем выбранным DEX до публикации, включая проверку
дублирующихся классов между DEX. Необязательный -o задаёт новый файл.
--max-files отдельно ограничивает суммарные количества определений классов,
определений методов и вхождений в результатах, а также записи ZIP.
--max-bytes ограничивает вход, сохраняемые данные запроса, рабочую память
каждого тела и вывод (с консервативным запасом на расширение JSON). Это бюджеты
операции; RSS процесса также включает входные буферы, накладные расходы
аллокатора и среду выполнения. Обычные бюджеты времени и работы тоже действуют.
neverd mobile app.apk -o recovered-app --platform=android \
--timeout=600 --max-files=30000 --max-bytes=4294967296 --json| Параметр | По умолчанию | Значение |
|---|---|---|
-o PATH |
Обязателен для восстановления | Новый каталог восстановления; в обоих режимах запроса — необязательный новый файл вывода |
--list-classes |
Выключен | Запрос идентификаторов классов APK/DEX без восстановления Java |
--class-prefix PREFIX |
Все классы | Буквальный префикс дескриптора или имени с точками; требует --list-classes |
--find-refs KIND |
Выключен | Запрос прямых ссылок инструкций на строки, типы, методы или поля |
--query TEXT |
Обязателен для ссылок | Буквальная подстрока идентификатора цели |
--exact |
Выключен | Сопоставить полный идентификатор цели ссылки |
--owner DESCRIPTOR |
Любой владелец | Точный владелец цели для запросов методов/полей |
--platform=auto|android |
auto |
Явно выбрать Android или определить платформу по входу |
--timeout N |
300 |
Положительный бюджет времени анализа в секундах |
--max-files N |
20000 |
Положительный лимит записей, включая создаваемые каталоги |
--max-bytes N |
2147483648 |
Положительный лимит байтов для входа, извлечённых данных и итогового вывода |
--json |
Выключен | Вывести отчёт в JSON; иначе запросы ссылок выводят JSON Lines |
Нестандартное значение --arch, --artifact, --metadata-only и ненулевой --max-func предназначены для iOS и отклоняются для Android; явный --arch=auto допустим.
Для входов, распакованных данных и конечного вывода сохраняются ограничения на число файлов и байты. Встроенные средства чтения и генератор также проверяют объём работы и прошедшее время. Увеличение одного лимита не отключает остальные.
recovered-app/
sources/ восстановленные пакеты и классы Java
metadata/android-methods.json покрытие методов встроенным движком
report.json версионированный перечень и ограничения
Временные входы удаляются. Вложенные классы могут использовать файл внешнего класса, поэтому число файлов Java не равно числу классов DEX. Созданные методы могут использовать диспетчерский цикл Java; они не выполняют исходный DEX и не вызывают его через мост времени выполнения.
Встроенный отчёт содержит android_method_recovery; те же данные записываются в metadata/android-methods.json, и каждый исходный метод остаётся в перечне. Выполняется method_count = recovered_method_count + projected_method_count + declaration_only_method_count + unrecovered_method_count; отсутствующий projected_method_count означает ноль, а unrecovered_method_count до публикации остаётся нулевым. Исходные методы native и abstract получают статус declaration-only и не учитываются как восстановленные тела.
Ограниченное подмножество именованных локальных классов без захвата переменных можно вывести внутри точно установленного внешнего статического метода. Требуются обычный метод со скалярными типами, класс без полей с непосредственным предком Object, настоящий конструктор без аргументов, скалярные методы экземпляра и проверенное использование объектов без выхода за поддерживаемую область. Анонимные классы, захват переменных, неподдерживаемые модификаторы и недоказанные способы использования по-прежнему вызывают явную ошибку.
Локальные методы и внешний метод получают source-projected с projection_kind: "named-method-local"; покрытие остаётся partial, даже если общий отчёт сообщает success. Двоичные имена и флаги доступа после повторной компиляции остаются непроверенными. Компилятор Java может выбрать другое двоичное имя, поэтому class_source_bindings сохраняет исходный класс, точный внешний метод, путь исходника и локальное имя с binary_name_status: "unverified".
generated_source_helpers перечисляет дополнительные методы с точными типами throw-helper, constant-helper, default-constructor и field-initializer. Последний обозначает дополнительно сгенерированный <clinit>, которого не было в исходном списке методов. Эти методы не входят в исходное общее количество. Успешная компиляция или разовое совпадение имён не доказывают полного восстановления. Следующий сокращённый пример не содержит проецируемых методов:
{
"schema_version": 1,
"status": "success",
"platform": "android",
"source": "app.apk",
"input_kind": "apk",
"backend": {
"name": "neverd",
"version": "1",
"execution": "builtin"
},
"input_code_files": [
"classes.dex",
"classes2.dex"
],
"dex_count": 2,
"smali_count": 0,
"java_source_count": 2,
"java_sources": [
"sources/example/Main.java",
"sources/example/Peer.java"
],
"logs": [],
"android_method_recovery": {
"schema_version": 1,
"status": "recovered",
"class_count": 2,
"method_count": 6,
"recovered_method_count": 5,
"declaration_only_method_count": 1,
"unrecovered_method_count": 0
}
}input_kind принимает значение apk, dex, smali или smali-directory. input_code_files перечисляет имена входного байткода или пути smali, а java_sources и logs задаются относительно корня вывода. source — базовое имя входного файла или каталога. Реальные отчёты содержат дополнительные ограничения реконструкции; сохраняйте их при передаче результатов другим инструментам.
При автоматизации проверяйте код завершения процесса, прежде чем использовать status, и при перенаправлении stdout сохраняйте отчёт вне нового каталога вывода:
neverd mobile app.apk -o recovered-app --json > recovery-result.jsonНативный CLI возвращает ноль при успехе и ненулевой код при ошибке восстановления. С --json обработанные ошибки содержат schema_version, status: "error" и error. Ошибки разбора аргументов, запуска нативного файла или библиотек и прерывания могут сообщаться только через stderr. Сначала проверяйте код завершения.
Публикация транзакционна: существующий вывод сохраняется, а временные результаты неудачного запуска удаляются. Неподдерживаемые операции, неразрешённые потоки регистров, непредставимые объявления, некорректная обработка исключений и исчерпанные бюджеты завершают встроенный анализ ошибкой вместо публикации пропущенных тел методов. Успешное восстановление не доказывает семантическую эквивалентность.
| Симптом | Действие |
|---|---|
| DEX, инструкция, объявление или инициализация не поддерживаются | Прочитать диагностику и проверить поддерживаемое подмножество |
| Некорректный вход или дублирующийся класс | Исправить байткод или набор классов; неподдерживаемые тела не пропускаются молча |
| Превышен срок или бюджет | Сократить вход или изменить --timeout, --max-files и --max-bytes с учётом доступных ресурсов |
| Вывод уже существует | Выбрать новый выходной каталог |
Python используется только в приведённых ниже тестовых сценариях разработки; встроенное мобильное восстановление выполняется нативным CLI на C++20.
cmake --build build --target check-neverd-mobile
ctest --test-dir build -L NeverDMobileTests --output-on-failure
python3 scripts/test_mobile_android_internal.py --d8 PATH --neverd build/bin/neverdТесты компонентов и CLI проверяют разбор, контракты вывода и очистку после сбоев. Внутренний исполнитель сравнений использует JDK (java и javac) и D8 для создания независимых примеров DEX/APK, затем компилирует и запускает восстановленный Java. Это зависимости тестирования, а не требования встроенного восстановления. Запустите его с текущей сборкой и изучите результаты, прежде чем считать случай проверенным. Успех примеров не доказывает полного восстановления произвольных приложений.
Связанный процесс для iOS описан в обзоре мобильных приложений.