Введение

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

Перед тем как начать, я настоятельно рекомендую Вам выделить несколько часов  для самообучения, по меньшей мере одному из .NET языков (Visual Basic .NET или C#). Может показаться, что реверсирование .NET программ происходит легче, нежели реверсирование «традиционных» программ, что не всегда правильно с одной точки зрения.

Саму концепцию .NET можно с легкостью сравнить с сущностью JAVA и Виртуальной Машины Java, хотя бы в том случае, когда говорим о компиляции. В отличие от большинства традиционных языков программирования, таких как C/C++, приложения, разработанные с использование .NET фреймворков и скомпилированные в Common Intermediate Language (CIL или Microsoft Intermediate Language MSIL), сравнимого с байткодом (bytecode) в Java программах, вместо того, чтобы быть преобразованными напрямую в нативный машинный исполняемый код, .NET Common Language Runtime (CLR) будет транслировать CIL в машинный код во время выполнения.

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

Рисунок 1. Обзор Common Language Infrastructure (CLI)

Что это означает для реверс инженера?

В принципе любое скомпилированное приложение .NET есть не более чем его представление в Common Intermediate Language, всё еще имеющее все pre-coded идентификаторы в том виде, в котором ввел программист.

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

Когда говорим о приложениях .NET, мы говорим о рефлексии («reflection»), а не о  декомпиляции («decompilation»). Эта техника позволяет нам исследовать информацию о классах или сборке во время выполнения, таким образом, мы можем получить  все свойства, методы, функции и т.д. со всеми параметрами и аргументами, а также все интерфейсы, структуры и прочее.

В настоящее время существует множество инструментов, которые позволяют  “рефлексировать” исходный код готового приложения .NET. Широкое распространение получила утилита «Reflector», позволяющая просматривать классы, декомпилировать и анализировать программы и компоненты .NET. Она позволяет выполнять просмотр и поиск CIL инструкций, ресурсов и XML документации, хранимой в сборке .NET. Но это не единственная утилита, которая нам понадобится для реверса .NET приложений. Постараюсь представить остальные утилиты по мере публикации новых постов.

Что Вы изучите в данной статье?!

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

Немного практики

Наша программа Crack-ME представляет собой простое однооконное приложение .NET, запрашивающее пользователя ввести пароль. Написано оно для показа основ  реверс инжиниринга .NET. Обычно исследователи начинают с анализа объекта и его поведения. Давайте взглянем с чем имеем дело!

 

Далее показано окно с ошибкой при вводе неправильного пароля:

 

Давайте посмотрим, почему скомпилированный код выдает сообщение “Invalid password”. Открываем Reflector, теперь нам надо настроить Reflector через выпадающий список на главной панели путем выбора любого языка программирования, с которым Вы знакомы. В качестве примера я выберу Visual Basic.

Рисунок 2. Выбор основного языка

Загрузите Crack-ME (File > Open menu) и поищите что-нибудь, что сможет привлечь наше внимание.  Crack-ME проанализирован и помещен в древовидную структуру. Мы продолжим исследовать узлы, которые нас интересуют:

Рисунок 3. Наш Crack-ME загружен

Вы можете развернуть объект путем нажатия значка “+”:

 

Продолжите раскрывать дерево и увидите, что находится внутри Crack-ME:

 

Теперь мы можем увидить, что наш Crack-ME состоит из References, Code и Resources.

  1. Code: данная часть содержит интересные вещи и всё, что нам понадобится, находится внутри InfoSecInstitute_dotNET_Reversing (являющийся Namespace).
  2. References: схоже с секциями “imports”, “includes” для PE файлов.
  3. Resources: на данный момент нас мало волнуют, но похожи на остальные ресурсы, что используются в других программах Windows.

Разворачивая исходный узел мы увидим следующее дерево:

 

Reflector определяет единственную форму Form1 со всеми переменными, процедурами, функциями и элементами графического интерфейса пользователя. Также, как сказано выше, он выводит исходные имена, что облегчает нам работу и позволяет предугадать, что каждый элемент предположительно делает. Для примера, функция btn_Chk_Click(Object, EventArgs), по-видимому, должна срабатывать, когда кнопка “btn_Chk” нажата, а btn_About_Click(Object, EventArgs) вызывается, когда кнопка “btn_About” нажата…

Поскольку это приложение для лабораторной работы, оно не содержит множества форм и функций, дающих понимание о работе программы. В данном случае нас может заинтересовать функция btn_Chk_Click (). Мы хотим узнать, что наша программа Crack-ME действительно делает при нажатии btn_Chk. Всё это может быть оттранслировано в выбранный ранее язык программирования (см. Рисунок 2).

Чтобы увидеть текущий исходный код, дважды щелкните по имени функции. Reflector показывает нам декомпилированный исходный код на выбранном языке, в нашем случае мы получим:

 

Никакой магии, мы действительно видим, что данная функция делает. Любой человек с разным уровнем подготовки в произвольном языке программирования сможет понять, что эта функция\процедура проверяет, если набран пароль “p@55w0rd!”. После нажатия мы получим:

Все очень легко! Развивая тему, мы сможем исследовать больше возможностей, например, создание патча для Crack-ME, позволяющего принимать любые пароли на ввод (рассмотрим в следующих постах).

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

Ссылки

1 комментарий для “Основы .NET Реверс-инжиниринга

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *