Декомпилятор - Decompiler

Декомпилятор это компьютерная программа , которая транслирует исполняемый файл на высоком уровне исходного файл , который может быть перекомпилированным успешно. Следовательно, это противоположность компилятору , который переводит исходный файл в исполняемый файл. Декомпиляторы обычно не могут полностью восстановить исходный код, поэтому часто создают запутанный код . Тем не менее, декомпиляторов остаются важным инструментом в обратной инженерии от компьютерного программного обеспечения .

Вступление

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

Декомпиляция - это действие с использованием декомпилятора, хотя этот термин также может относиться к выходным данным декомпилятора. Его можно использовать для восстановления утерянного исходного кода, а также в некоторых случаях полезно для компьютерной безопасности , взаимодействия и исправления ошибок . Успех декомпиляции зависит от количества информации, содержащейся в декомпилируемом коде, и сложности анализа, выполняемого над ним. Форматы байт-кода, используемые многими виртуальными машинами (такими как Java Virtual Machine или .NET Framework Common Language Runtime ), часто включают обширные метаданные и высокоуровневые функции, которые делают декомпиляцию вполне возможной. Применение отладочных данных , то есть отладочных символов, может позволить воспроизвести оригинальные имена переменных и структур и даже номера строк. Машинный язык без таких метаданных или отладочных данных декомпилировать намного сложнее.

Некоторые компиляторы и инструменты посткомпиляции создают запутанный код (то есть они пытаются произвести вывод, который очень трудно декомпилировать или который декомпилируется до запутанного вывода). Это сделано для того, чтобы затруднить обратное проектирование исполняемого файла.

Хотя декомпиляторы обычно используются для (воссоздания) исходного кода из двоичных исполняемых файлов, существуют также декомпиляторы для превращения определенных файлов двоичных данных в читаемые и редактируемые источники.

Дизайн

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

Загрузчик

На первом этапе декомпиляции загружается и анализируется входной машинный код или формат двоичного файла программы на промежуточном языке . Он должен быть в состоянии обнаружить основные факты о программе ввода, такие как архитектура (Pentium, PowerPC и т. Д.) И точка входа. Во многих случаях он должен быть в состоянии найти эквивалент функции программы C , которая является началом написанного пользователем кода. Это исключает код инициализации среды выполнения, который по возможности не следует декомпилировать. Если доступно, также загружаются таблицы символов и данные отладки. Внешний интерфейс может идентифицировать используемые библиотеки, даже если они связаны с кодом, это предоставит библиотечные интерфейсы. Если он может определить используемый компилятор или компиляторы, он может предоставить полезную информацию для определения идиом кода. main

Разборка

Следующим логическим этапом является разборка инструкций машинного кода в машинно-независимое промежуточное представление (IR). Например, машинная инструкция Pentium

mov    eax, [ebx+0x04]

может быть переведен на IR

eax  := m[ebx+4];

Идиомы

Последовательности идиоматического машинного кода - это последовательности кода, комбинированная семантика которых не сразу очевидна из индивидуальной семантики инструкций. Либо как часть фазы разборки, либо как часть более позднего анализа, эти идиоматические последовательности необходимо транслировать в известный эквивалентный IR. Например, ассемблерный код x86 :

    cdq    eax             ; edx is set to the sign-extension≠edi,edi +(tex)push
    xor    eax, edx
    sub    eax, edx

можно перевести на

eax  := abs(eax);

Некоторые идиоматические последовательности не зависят от машины; некоторые включают только одну инструкцию. Например, очищает регистр (обнуляет). Это может быть реализовано с помощью машинно-независимого правила упрощения, например . xor eax, eaxeaxa = 0

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

Особенно важно распознавать идиомы компилятора для вызовов подпрограмм , обработки исключений и операторов переключения . Некоторые языки также имеют обширную поддержку строк или длинных целых чисел .

Программный анализ

К IR можно применить различные программные анализы. В частности, распространение выражений объединяет семантику нескольких инструкций в более сложные выражения. Например,

    mov   eax,[ebx+0x04]
    add   eax,[ebx+0x08]
    sub   [ebx+0x0C],eax

может привести к следующему IR после распространения выражения:

m[ebx+12]  := m[ebx+12] - (m[ebx+4] + m[ebx+8]);

Результирующее выражение больше похоже на язык высокого уровня, а также исключило использование машинного регистра eax. Более поздние анализы могут исключить ebxрегистр.

Анализ потока данных

Места, в которых определяется и используется содержимое регистров, необходимо отслеживать с помощью анализа потока данных . Такой же анализ можно применить к местоположениям, которые используются для временных и локальных данных. Затем для каждого такого связанного набора определений значений и использования может быть сформировано другое имя. Возможно, что одно и то же местоположение локальной переменной использовалось для нескольких переменных в разных частях исходной программы. Еще хуже то, что анализ потока данных может определить путь, по которому значение может перемещаться между двумя такими использованиями, даже если на самом деле это никогда не произойдет или не будет иметь значения в действительности. В плохих случаях это может привести к необходимости определять местоположение как объединение типов. Декомпилятор может позволить пользователю явно нарушить такие неестественные зависимости, что приведет к более ясному коду. Это, конечно, означает, что переменная потенциально может использоваться без инициализации, что указывает на проблему в исходной программе.

Типовой анализ

Хороший декомпилятор машинного кода выполнит анализ типов. Здесь способ использования регистров или ячеек памяти приводит к ограничениям на возможный тип местоположения. Например, andинструкция подразумевает, что операнд является целым числом; программы не используют такую ​​операцию для значений с плавающей запятой (кроме кода специальной библиотеки) или указателей . An addРезультаты команд в трех ограничений, поскольку операндами могут быть как целым числом, или одно целое , а один указатель (с целыми и указателей результатов соответственно, третье ограничение происходит от упорядочения двух операндов , когда типы различны).

Можно распознать различные выражения высокого уровня, запускающие распознавание структур или массивов. Однако трудно различить многие возможности из-за свободы, которую предоставляет машинный код или даже некоторые языки высокого уровня, такие как C, с приведениями и арифметикой указателей.

Пример из предыдущего раздела может привести к следующему высокоуровневому коду:

struct T1 *ebx;
    struct T1 {
        int v0004;
        int v0008;
        int v000C;
    };
ebx->v000C -= ebx->v0004 + ebx->v0008;

Структурирование

Предпоследняя фаза декомпиляции включает в себя структурирование IR в конструкции более высокого уровня, такие как whileциклы и if/then/elseусловные операторы. Например, машинный код

    xor eax, eax
l0002:
    or  ebx, ebx
    jge l0003
    add eax,[ebx]
    mov ebx,[ebx+0x4]
    jmp l0002
l0003:
    mov [0x10040000],eax

можно перевести на:

eax = 0;
while (ebx < 0) {
    eax += ebx->v0000;
    ebx = ebx->v0004;
}
v10040000 = eax;

Неструктурированный код сложнее перевести в структурированный код, чем уже структурированный код. Решения включают репликацию некоторого кода или добавление логических переменных.

Генерация кода

Заключительный этап - это генерация кода высокого уровня в задней части декомпилятора. Подобно тому, как компилятор может иметь несколько внутренних интерфейсов для генерации машинного кода для разных архитектур, декомпилятор может иметь несколько внутренних интерфейсов для генерации кода высокого уровня на разных языках высокого уровня.

Непосредственно перед генерацией кода может быть желательно разрешить интерактивное редактирование IR, возможно, с использованием некоторой формы графического пользовательского интерфейса . Это позволит пользователю вводить комментарии и неуниверсальные имена переменных и функций. Однако их почти так же легко ввести при редактировании после декомпиляции. Пользователь может захотеть изменить структурные аспекты, например преобразовать whileцикл в forцикл. Их труднее изменить с помощью простого текстового редактора, хотя инструменты рефакторинга исходного кода могут помочь в этом процессе. Пользователю может потребоваться ввести информацию, которую не удалось идентифицировать на этапе анализа типа, например, изменение выражения памяти на выражение массива или структуры. Наконец, может потребоваться исправить неправильный IR или внести изменения, чтобы выходной код был более читабельным.

Законность

На большинство компьютерных программ распространяются законы об авторском праве . Хотя точный объем того, что покрывается авторским правом, отличается от региона к региону, закон об авторском праве обычно предоставляет автору (программисту (программистам) или работодателю) совокупность исключительных прав на программу. Эти права включают право делать копии, в том числе копии, сделанные в ОЗУ компьютера (если создание такой копии не является необходимым для использования программы). Поскольку процесс декомпиляции включает создание нескольких таких копий, это обычно запрещено без разрешения правообладателя. Однако, поскольку декомпиляция часто является необходимым шагом в достижении функциональной совместимости программного обеспечения , законы об авторском праве как в Соединенных Штатах, так и в Европе разрешают декомпиляцию в ограниченной степени.

В Соединенных Штатах защита авторских прав на основе добросовестного использования успешно применяется в случаях декомпиляции. Например, в деле Sega v. Accolade суд постановил, что Accolade может на законных основаниях участвовать в декомпиляции, чтобы обойти механизм блокировки программного обеспечения, используемый игровыми консолями Sega. Кроме того, Закон об авторском праве в цифровую эпоху (ПУБЛИЧНЫЙ ЗАКОН 105–304) имеет соответствующие исключения как для тестирования безопасности, так и для оценки в §1201 (i), а также для обратного проектирования в §1201 (f).

В Европе Директива по программному обеспечению 1991 г. прямо предусматривает право на декомпиляцию для достижения функциональной совместимости. В результате жарких дебатов между, с одной стороны, сторонниками протекционизма, а с другой - учеными, а также независимыми разработчиками программного обеспечения, статья 6 разрешает декомпиляцию только при соблюдении ряда условий:

  • Во-первых, физическое или юридическое лицо должно иметь лицензию на использование декомпилируемой программы.
  • Во-вторых, декомпиляция должна быть необходима для обеспечения взаимодействия с целевой программой или другими программами. Поэтому информация о совместимости не должна быть легко доступна, например, в руководствах или документации API . Это важное ограничение. Необходимость должна быть доказана декомпилятором. Целью этого важного ограничения является прежде всего стимул для разработчиков документировать и раскрывать информацию о совместимости своих продуктов.
  • В-третьих, процесс декомпиляции должен, по возможности, ограничиваться частями целевой программы, имеющими отношение к взаимодействию. Поскольку одной из целей декомпиляции является понимание структуры программы, это третье ограничение может быть трудновыполнимым. Опять же, бремя доказательства лежит на декомпиляторе.

Кроме того, статья 6 предписывает, что информация, полученная путем декомпиляции, не может использоваться для других целей и не может быть передана другим лицам.

В целом, право на декомпиляцию, предусмотренное статьей 6, кодифицирует то, что считается обычной практикой в ​​индустрии программного обеспечения. Известно, что некоторые европейские иски возникли из-за права на декомпиляцию. Это можно интерпретировать как означающее одно из трех:

  1. ) право на декомпиляцию используется нечасто, поэтому право на декомпиляцию могло быть ненужным,
  2. ) право на декомпиляцию функционирует хорошо и обеспечивает достаточную правовую определенность, чтобы не вызывать юридических споров или
  3. ) незаконная декомпиляция в основном остается незамеченной.

В отчете 2000 года о реализации Директивы по программному обеспечению европейскими государствами-членами Европейская комиссия, похоже, поддержала вторую интерпретацию.

Инструменты

Декомпиляторы обычно нацелены на определенный двоичный формат. Некоторые из них представляют собой собственные наборы инструкций (например, Intel x86, ARM, MIPS), другие представляют собой байт-код для виртуальных машин (Dalvik, файлы классов Java, WebAssembly, Ethereum).

Из-за потери информации во время компиляции декомпиляция почти никогда не бывает идеальной, и не все декомпиляторы работают одинаково хорошо для данного двоичного формата. Есть исследования, сравнивающие производительность разных декомпиляторов.

Смотрите также

Декомпиляторы Java

Другие декомпиляторы

использованная литература

внешние ссылки