
Статический набор инструментов для деобфускации скомпилированного байткода V8 JavaScript, ориентированный на полезные нагрузки JSCeal. Предоставляет фильтры на основе шаблонов, восстановление линейного потока управления, реконструкцию строк и опциональное переименование функций с помощью LLM для анализа.
Этот инструмент предназначен для статической деобфускации скомпилированного байт-кода V8 JavaScript, защищённого с помощью javascript-obfuscator.
Он работает с псевдокодом, созданным View8, а не с исходным кодом JavaScript. Проект разрабатывался и тестировался на нагрузках JSCeal.
Фильтры основаны на шаблонах и предназначены в первую очередь как исследовательский инструментарий и эталонная реализация. Инструмент не является универсальным деобфускатором JavaScript, не восстанавливает исходный код и не создаёт исполняемый JavaScript. Его выходные данные остаются псевдокодом View8, предназначенным для статического анализа, поиска, сравнения и экспорта дерева функций.
pickle. Загрузка вредоносного или недоверенного файла .pkl может привести к выполнению кода. Загружайте только те сериализованные файлы, которые вы создали локально с помощью View8.requirements.txt;brotli для пакетного процесса распаковки в Linux;Создайте изолированное окружение Python:
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt
Серверная часть OpenAI в deobf_ai.py дополнительно требует пакет OpenAI для Python:
python3 -m pip install openai
Серверная часть Anthropic использует HTTP API через requests. Серверная часть Ollama ожидает доступный сервер Ollama.
Исходная нагрузка JSCeal app.jsc сжата Brotli. В Linux её можно распаковать с помощью утилиты brotli:
brotli -d app.jsc -o app.decompressed.jsc
Пакетный процесс в scripts/ выполняет этот шаг с помощью scripts/unpack_all.sh.
В Windows или когда утилита командной строки brotli недоступна, можно использовать включённый помощник Node.js как запасной вариант. Он только распаковывает входные данные и не выполняет их:
node Utils/decompress-jsc.js app.jsc
Он записывает:
app.jsc.decompressed.jsc
Кэш кода V8 зависит от версии. Используйте дизассемблер, собранный для той же версии V8, что и нагрузка.
Образцы JSCeal, использованные при разработке, были основаны на V8 10.2.154.26. Дизассемблеры по умолчанию из несвязанной сборки V8 работать не будут.
Дерево исходников содержит исходный код дизассемблера и необходимые патчи V8 в:
Utils/disasm/v8dasm.cpp
Utils/disasm/patches/
Предварительно собранный бинарный файл для Linux распространяется с релизом проекта, а дерево исходников содержит исходный код и патчи, необходимые для его пересборки. Подробное описание доступно в Wiki проекта. После получения или сборки соответствующего v8dasm выполните:
/path/to/v8dasm app.decompressed.jsc > app.jsc.disasm.txt
Передайте дизассемблированный файл в view8.py и создайте как сериализованный вывод для дальнейшей обработки, так и читаемый псевдокод:
mkdir -p decompiled
python3 View8/view8.py \
--input_format disassembled \
--inp app.jsc.disasm.txt \
--normalize \
--out decompiled/app.dec.txt \
--export_format decompiled serialized
Это создаёт:
decompiled/app.dec.txt
decompiled/app.dec.pkl
Опция --normalize делает сгенерированные идентификаторы функций воспроизводимыми при повторных запусках дизассемблирования и декомпиляции.
Существуют отдельные фильтры для отдельных слоёв обфускации. Их можно применить вместе к сериализованному выводу View8 с помощью deobf_all.py:
mkdir -p deobfuscated
python3 deobf_all.py \
--inp decompiled/app.dec.pkl \
--out deobfuscated/app.deobf.txt \
--export_format decompiled serialized
Фильтр строк по умолчанию — вариант 2, используемый большинством проанализированных нагрузок JSCeal. Чтобы явно выбрать более простую схему строк, добавьте:
--str_deobf 1
Типичные выходные данные:
deobfuscated/app.deobf.txt
deobfuscated/app.deobf.pkl
deobfuscated/app.deobf.txt.strings.txt
decompiled/app.dec.resolved_funcs.csv
CSV-файл разрешённых функций — это кэш, специфичный для образца. Когда он отсутствует, этап строк восстанавливает необходимую конфигурацию декодера, записывает CSV и продолжает деобфускацию строк в том же запуске. Последующие запуски повторно используют кэш и обычно выполняются быстрее.
Не используйте повторно CSV-файл разрешённых функций с другой декомпилированной нагрузкой.
После применения всех структурных фильтров деобфускации deobf_ai.py может предложить имена, описывающие поведение функций. Он поддерживает серверные части Anthropic, OpenAI и Ollama.
Передавайте модель явно, чтобы запуски оставались воспроизводимыми.
export ANTHROPIC_API_KEY='...'
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--llm_backend anthropic \
--model '<model-id>' \
--export_format decompiled serialized
export OPENAI_API_KEY='...'
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--llm_backend openai \
--model '<model-id>' \
--export_format decompiled serialized
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--llm_backend ollama \
--model '<local-model>' \
--ollama_url http://localhost:11434 \
--export_format decompiled serialized
В режиме по умолчанию переименователь строит дерево прямых вызовов, начиная с входной функции, и переименовывает только функции, достигнутые через вызовы. Добавьте --greedy, чтобы включить все видимые ссылки на функции, включая обратные вызовы и назначенные обработчики.
Сгенерированный двухколоночный CSV-файл действует как кэш и позволяет продолжить прерванный запуск. Выберите существующий кэш явно с помощью --csv:
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--csv deobfuscated/app.deobf.renamed_funcs.greedy.example-model.csv \
--llm_backend anthropic \
--model '<model-id>' \
--greedy \
--export_format decompiled serialized
В обычном режиме CSV-файл рассматривается как потенциально частичный кэш. Кэшированные имена применяются первыми, функции, уже покрытые кэшем, удаляются из выбранного дерева вызовов или ссылок, и LLM вызывается только для функций, которые остались неразрешёнными. Если CSV-файл полностью покрывает это дерево, ключ API или соединение с LLM не требуются. Если он покрывает только часть дерева, выбранная серверная часть инициализируется, и вновь сгенерированные сопоставления добавляются в тот же CSV-файл.
Используйте тот же режим дерева, который использовался при создании CSV-файла. CSV-файл, созданный из запуска --greedy, обычно снова требует --greedy, если цель — продолжить этот запуск, а не только повторно использовать подмножество прямых вызовов.