РЕФЕРАТ 3
ВВЕДЕНИЕ 5
ОСНОВНЫЕ ОПРЕДЕЛЕНИЯ 7
1 Постановка задачи 8
2 Этапы декомпиляции 9
2.1 Каскадный анализ 9
2.2 Загрузка файла 10
2.2.1 Структура файла с байткодом 10
2.2.2 Анализ структуры файла 10
2.3 Промежуточное представление 12
2.4 Анализ графа потока исполнения 13
2.4.1 Построение CFG 14
2.4.2 Шаблоны управляющих конструкций 14
2.4.3 Схлопывание последовательностей узлов 15
2.4.4 Дублирование терминирующего узла 16
2.5 Работа с переменными 17
2.5.1 Удаление неиспользуемых переменных 17
2.5.2 Подстановка переменной 18
3 Программная реализация 19
ЗАКЛЮЧЕНИЕ 20
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ И ЛИТЕРАТУРЫ 20
ПРИЛОЖЕНИЕ А Таблицы трансляции инструкций 22
Основная задача специалиста по обратной разработке (англ. reverse engineering) заключается в том, чтобы по данному ему бинарному файлу исследовать логику работы приложения, реализованного в этом файле, а одним из самых главных инструментов статического анализа (анализа без фактического исполнения кода) является декомпилятор. В большинстве случаев подобные файлы содержат машинный код в том или ином формате, полученный в результате компиляции кода, записанном на некотором языке программирования.
Однако существуют языки, для которых конечным результатом компиляции является не машинный код, а байткод, используемый интерпретатором или виртуальной машиной программного окружения (англ. runtime) языка. Одним из таких языков является LuaJIT [1], представляющий собой модифицированную версию популярного языка программирования Lua. Хотя LuaJIT имеет дополнительную возможность компилировать исходный код непосредственно в машинный код посредством JIT-компиляции, в данной работе будет рассматриваться именно работа с байткодом.
Поскольку байткод является более высокоуровневым представлением в отличии от традиционного машинного кода, часто его возможно декомпилировать обратно в исходный код, ввиду чего существующие декомпиляторы LuaJIT-байткода [2] построены на идее полного однозначного восстановления исходного кода. Это может быть выполнимым, если в ходе генерации выходного бинарного файла был использован стандартный компилятор, в таком случае задача декомпилятора сводится к обращению описанных механизмов и алгоритмов используемых при компиляции. Однако в случае, когда выходной бинарный файл изначально был получен иным способом (например скомпилирован не исходный код на языке LuaJIT, а набор инструкций транслирован напрямую в байткод), или в процессе компиляции к байткоду были применены нестандартные механизмы оптимизации или защиты, процесс восстановления псевдокода из такого файла уже отличается, а алгоритмы и идеи, заложенные в существующих декомпиляторах, не работают в полной мере, либо не работают совсем, исходя из чего задача разработки и внедрения более универсальных методов декомпиляции становится актуальной.
Целью данной работы является разработка и реализация декомпилятора байткода LuaJIT, использующего принципы и подходы позволяющие обеспечить более гибкий анализ, а также устраняющие недостатки существующих аналогов.
В первой главе работы описываются недостатки подходов существующих декомпиляторов байткода LuaJIT и возможные пути их исправления.
Во второй главе описывается архитектура декомпилятора, с краткими описаниями предлагаемых подходов и модулей, выполняющих этапы анализа и декомпиляции. Этап загрузки и дизассемблирования: формирования дерева прототипов, применяемые технологии для описания структуры файла, пример вывода дизассемблированного кода. Этап трансляции дизассемблированного кода в промежуточное представление в виде псевдокода LuaJIT. Работа с графом потока исполнения функций: формирования графа, восстановление конструкций передачи управления, применяемые к графу мутации.
В заключении подводятся итоги проделанной работы
В работе предложены архитектура декомпилятора байткода LuaJIT и способы, предотвращающие недостатки существующих декомпиляторов:
1. Архитектура декомпилятора построена в виде нескольких каскадов анализа файла, что позволяет добавить гибкости в предоставлении информации пользователю.
2. Код функции представляется ввиде графа, что позволяет увеличить покрытие возможных обрабатываемых вариантов конструкций передачи управления.
3. Предложены две мутации, используемые при работе с графом потока управления функции, для повышения возможного покрытия кода функции: метод схлопывания последовательностей узлов и метод дублирования терминирующего узла.
Были составлены и предложены пары трансляции инструкций в псевдокод на языке LuaJIT. На языке python реализован прототип декомпилятора с описанной архитектурой, реализация представлена в [9].
1. The LuaJIT project.— URL: https://luajit.org/luajit.html/ — Дата доступа: 14 января 2021.
2. LuaJIT Raw-Bytecode Decompiler (LJD).— URL: https://github.com/DrNewbie/ luajit-decompiler — Дата доступа: 14 января 2021.
3. LuaJIT Raw-Bytecode Decompiler (LJD).— URL: https://gitlab.com/znixian/ luajit-decompiler — Дата доступа: 14 января 2021.
4. Binary Ninja. — URL: https://binary.ninja/ — Дата доступа: 14 января 2021.
5. LuaJIT bytecode dump format.— URL: http://wiki.luajit.org/Bytecode-2.0# luajit-2-0-bytecode-dump-format — Дата доступа: 14 января 2021.
6. Cifuentes C. Reverse Compilation Techniques / C. Cifuentes. — 1994. — С. 126-163.
7. Khaled Yakdan Sebastian Eschweiler E. G.-P. M. S. No More Gotos: Decompilation Using Pattern-Independent Control-Flow Structuring and Semantics-Preserving Transformations.— Электрон. дан.— 2018.— URL: https://www.ndss-symposium.org/ wp-content/uploads/2017/09/11_4_2.pdf — Дата доступа: 14 января 2021.
8. KaiTai Struct framework. — URL: https://kaitai.io/ — Дата доступа: 14 января 2021.
9. LuaJIT decompiler prototype.— URL: https://github.com/mostobriv/luajit_decomp — Дата доступа: 14 января 2021.