Подтвердить что ты не робот

Почему так легко декомпилировать код .NET IL?

Почему так легко декомпилировать .NET IL-код в исходный код, по сравнению с декомпиляцией собственных двоичных файлов x86? (Reflector производит довольно хороший исходный код большую часть времени, а декомпиляция вывода компилятора С++ практически невозможна.)

Это потому, что IL содержит много метаданных? Или это потому, что IL - это более высокая абстракция, чем инструкции x86? Я провел некоторое исследование и нашел следующие две полезные статьи, но ни один из них не ответил на мой вопрос.

4b9b3361

Ответ 1

Я думаю, что у вас уже есть самые важные биты.

  • Как вы говорите, есть больше доступных метаданных. Я не знаю подробностей того, что испускается компилятором C или С++, но я подозреваю, что в IL больше всего имен и подобной информации. Просто посмотрите, что декомпилятор знает о том, что в конкретном стеке стека, например - насколько это касается x86, вы знаете только, как используется стек; в IL вы знаете, что представляет собой содержимое стека (или, по крайней мере, тип - не смысловое значение!)
  • Опять же, как вы уже упоминали, IL - это абстракция более высокого уровня, чем x86. x86 не знает, что такое метод или вызов функции, или событие или свойство и т.д. IL имеет всю эту информацию, все еще находящуюся в ней.
  • Обычно компиляторы C и С++ оптимизируют гораздо больше, чем (скажем) компилятор С#. Это связано с тем, что компилятор С# предполагает, что большая часть оптимизации может быть выполнена позже - JIT. В некотором смысле для компилятора С# имеет смысл не пытаться делать большую оптимизацию, так как существуют различные биты информации, которые доступны JIT, но не компилятору С#. Оптимизированный код сложнее декомпилировать, поскольку он еще дальше от естественного представления исходного исходного кода.
  • IL был разработан для JIT-компиляции; x86 был разработан для выполнения изначально (по общему признанию через микрокод). Информация, необходимая компилятору JIT, похожа на ту, что захочет декомпилятор, поэтому декомпилятор имеет более легкое время с IL. В некотором смысле это действительно просто повторение второго пункта.

Ответ 2

Есть несколько вещей, которые делают обратную инженерию il довольно легкой.

  • Введите информацию. Это массивный. В ассемблере x86 вы должны вывести типы переменных в зависимости от того, как они используются.

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

  • имена. Знание имен вещей может быть полезно.

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

Ответ 3

С# и IL почти сопоставляются друг с другом. (Это в меньшей степени относится к некоторым новым функциям С# 3.0.) Близость отображения (и отсутствие оптимизатора в компиляторе С#) делает вещи "обратимыми".

Ответ 4

Расширение правильного ответа Брайана

Если вы считаете, что все IL легко декомпилируемы, я предлагаю написать нетривиальную программу F # и попытаться декомпилировать этот код. F # выполняет множество преобразований кода и, следовательно, имеет очень плохое отображение из фактического испускаемого ИЛ и исходной базы кода. ИМХО, значительно сложнее рассмотреть декомпилированный код F # и вернуть исходную программу, чем для С# или VB.Net.