Вопрос

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

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

Это было полезно?

Решение

Я думаю, вы уже поняли самое важное.

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

Другие советы

Есть ряд вещей, которые делают реверс-инжиниринг довольно простым.

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top