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

Почему сложно разобрать собственный Win32, но легко разбирать приложение .NET?

Почему процесс дизассемблирования собственного образа Win32 (встроенного в C/С++, например,) намного сложнее, чем дизассемблирование .NET-приложения?

В чем главная причина? Из-за чего?

4b9b3361

Ответ 1

A.net-сборка встроена в Common Intermediate Language. Он не компилируется до тех пор, пока он не будет выполнен, когда CLR скомпилирует его для запуска в соответствующей системе. CIL имеет множество метаданных, поэтому их можно скомпилировать на разных процессорных архитектурах и разных операционных системах (в Linux, используя Mono). Классы и методы остаются в основном неповрежденными.

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

Код C и С++ скомпилирован в выбранную архитектуру процессора и систему при компиляции. Исполняемый файл, скомпилированный для Windows, не будет работать в Linux и наоборот. Результатом компилятора C или С++ является инструкция по сборке. Функции в исходном коде могут не существовать как функции в двоичном формате, а каким-то образом оптимизироваться. У компиляторов также могут быть довольно агрессивные оптимизаторы, которые возьмут логически структурированный код и сделают его похожим. Код будет более эффективным (во времени или в пространстве), но может сделать его более трудным для изменения.

Ответ 2

Благодаря внедрению .NET, позволяющему взаимодействовать между языками, такими как С#, VB и даже C/С++ через CLI и CLR, это означает, что дополнительные метаданные должны быть помещены в объектные файлы для правильной передачи свойств класса и объекта, Это упрощает дизассемблирование, поскольку двоичные объекты все еще содержат эту информацию, тогда как C/С++ может отбросить эту информацию, поскольку она не является необходимой (по крайней мере для выполнения кода, информация по-прежнему требуется во время компиляции).

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

Ответ 3

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

В результате что-то вроде итерации над контейнером будет выглядеть как пара inc/jnc инструкции по сборке для собственного кода по сравнению с вызовами функций со значимыми именами в IL. Результат исполняемого кода может быть одним и тем же (или, по крайней мере, близко), поскольку компилятор JIT будет вызывать некоторые вызовы, похожие на собственный компилятор, но код, который можно посмотреть, гораздо читабельнее на земле CLR.

Ответ 4

Люди упомянули некоторые из причин; Я упомянул еще один, предполагая, что мы говорим о разборке, а не декомпиляции.

Проблема с кодом x86 заключается в том, что различение кода и данных очень сложно и подвержено ошибкам. Дисассемблеры должны полагаться на угадывание, чтобы понять это, и они почти всегда чего-то пропускают; напротив, промежуточные языки предназначены для "дизассемблирования" (так что компилятор JIT может превратить "разборку" в машинный код), поэтому они не содержат двусмысленностей, подобных тому, который вы найдете в машинных кодах. Конечным результатом является то, что разбор кода IL довольно тривиален.

Если вы говорите об декомпиляции, это другое дело; это связано с (главным образом) отсутствием оптимизаций для приложений .NET. Большинство оптимизаций выполняется компилятором JIT, а не С#/VB.NET/etc. компилятор, поэтому код сборки почти соответствует 1:1 исходному коду, так что выяснение оригинала вполне возможно. Но для собственного кода существует миллион различных способов перевода нескольких исходных строк (черт, даже не-ops имеют gazillion различные способы написания с различными характеристиками производительности!), Поэтому довольно сложно понять, что такое оригинал.

Ответ 5

В общем случае нет никакой разницы между дизассемблированием кода С++ и .NET. Из-за С++ сложнее разобрать, потому что он делает больше оптимизаций и тому подобное, но это не основная проблема.

Основная проблема заключается в именах. В разобранном С++-коде есть все, что называется A, B, C, D,... A1 и т.д. Если вы не смогли распознать алгоритм в таком формате, вы не сможете извлечь из дизассемблированного бинарного файла С++ информацию.

Библиотека .NET с другой стороны содержит в себе имена методов, параметров метода, имена классов и имена полей класса. Это значительно упрощает понимание дизассемблированного кода. Все остальные вещи являются второстепенными.

Ответ 6

Кроме того, что-то о метаданных, отладочная информация и все технические причины указывают на другие ответы; о чем я думал:

Основная причина, по которой вам кажется, что дизассемблирование win32 более сложна, чем программы .Net, связано с перспективой человека.

С точки зрения машины, собственный код намного более прозрачен, даже при обработке обратной инженерии.

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

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

  • адресация
  • доступ к памяти/регистрации
  • аппаратная связь
  • Технология уровня ОС (обработка, обмен, подкачка и т.д.)

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

  • производительность исполнения
  • оптимизация кода
  • ремонтопригодность
  • соображения стоимости

Если вы прочитали код OpCodes структуры .Net, и вы понимаете, что существуют более сложные концепции уровня языка и ООП. Например, с помощью Reflection.Emit вы можете испустить код операции вызова конструктора, метода или виртуального метода. Да, он основан на MSIL(CIL) и работает под CLR; но это не значит, что его легче разобрать; это может быть сделано запутанным образом и становится намного сложнее изменить исходный код; как и в психическом мире, всегда более непроницаем, чем физический мир.