Как работает декомпиляция?

Я слышал термин "декомпиляция", используемый несколько раз раньше, и мне становится очень любопытно, как это работает.

У меня есть очень общее представление о том, как это работает; обратная инженерия приложения, чтобы увидеть, какие функции он использует, но я не знаю много дальше этого.

Я также слышал термин"дизассемблер", в чем разница между дизассемблером и декомпилятором?

Итак, чтобы подвести итог моему вопросу (- ам): Что именно участвует в процессе декомпиляции чего-либо? Как это обычно делается? Насколько сложны / легки процессы? может ли он произвести точный код? И в чем разница между декомпилятор и дизассемблер?

2 ответов


Ilfak Guilfanov, автор Декомпилятор Шестигранных Лучей, произнес речь о внутренней работе своего декомпилятора на каком-то con, и вот белая бумага и презентация. Это описывает хороший обзор в том, что все трудности в создании декомпилятора и как заставить все это работать.

кроме того, есть некоторые довольно старые бумаги, например классическая Кандидатская диссертация Кристины Сифуэнтес!--7-->.

Что касается сложности, все "декомпилирование" зависит от языка и времени выполнения двоичного файла. Например, декомпиляция .NET и Java считается "выполненной", так как есть доступные бесплатные декомпиляторы, которые имеют очень высокий коэффициент успеха (они производят исходный источник). Но это вызвано очень специфическим характером виртуальных машин, которые используют эти среды выполнения.

Что касается действительно скомпилированных языков, таких как C, C++, Obj-C, Delphi, Pascal, ... задача становится намного сложнее. Прочитайте вышеуказанные статьи для деталей.

в чем разница между дизассемблер и декомпилятор?

когда у вас есть двоичная программа (исполняемый файл, библиотека DLL, ...), состоит из инструкций процессора. Язык этих инструкций называется сборка (или ассемблер). В двоичном коде эти инструкции кодируются двоично, так что процессор может непосредственно выполнять их. Ля дизассемблер принимает этот двоичный код и переводит его в текстовое представление. Этот перевод обычно 1-к-1, Что означает, что одна инструкция отображается как одна строка текста. Эта задача сложная, но простая, программа просто должна знать все различные инструкции и как они представлены в двоичном файле.

С другой стороны, a декомпилятор гораздо сложнее. Он принимает либо двоичный код, либо вывод дизассемблера (который в основном то же самое, потому что это 1-к-1) и производит код высокого уровня. Позвольте мне привести вам пример. Скажем, у нас есть эта функция с:

int twotimes(int a) {
    return a * 2;
}

при компиляции, компилятор сначала генерирует файл сборки для этой функции, он может выглядеть примерно так:

_twotimes:
    SHL EAX, 1
    RET

(первая строка - это просто метка, а не настоящая инструкция,SHL выполняет операцию сдвига влево, которая быстро умножается на два,RET означает, что функция сделанный.) В результате двоичный файл выглядит так:

08 6A CF 45 37 1A

(Я это придумал, а не реальные двоичные инструкции). Теперь вы знаете, что A дизассемблер переносит вас из двоичной формы в форму сборки. А декомпилятор переносит вас из формы сборки в код C (или какой-либо другой язык более высокого уровня).


Декомпиляция по существу является обратной компиляцией. То есть-взять объектный код (двоичный) и попытаться воссоздать исходный код из него.

Декомпиляция зависит от артефактов, оставленных в объектном коде, которые могут быть использованы для определения структуры исходного кода.

с C / C++ не так много осталось, чтобы помочь процессу декомпиляции, поэтому это очень сложно. Однако с Java и C# и другими языками, которые нацелены на виртуальные машины, это может быть легче декомпилировать, потому что язык оставляет гораздо больше подсказок в объектном коде.