Введение


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


Предмет исследования


В качестве примера будем изучать код Atomineer Pro Documentation (далее APD). Это плагин для Visual Studio предназначенный для автоматической генерации комментариев в исходных кодах. Для начала установим данный плагин и проверим его работу. Бесплатная версия имеет триальный период и ряд ограничений использования в течении этого времени. Так при добавлении комментариев в файл пользователю выдается сообщение о том, что в течении дня можно обработать только 10 файлов

Сообщение 1
image

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

Сообщение 2
image

Приступим


Для начала посмотрим в каталог установленного расширения и найдем там только одну динамическую библиотеку. Она нам и нужна. Первое что мы сделаем это загрузим ее в декомпилятор dotPeek от компании JetBrains.

dotPeek
image

Как видно на скрине по библиотеке прошлись обфускатором, переменные и методы переименованы и имеют названия вида a, b, c, d… это нам и нужно. Мы же искали именно это. Давайте посмотрим, что же можно сделать.

Часть 1


Первое что приходит на ум это отыскать строчку, но ввиду того что функционала поиска нет в dotPeek, пойдем по другому пути. Декомпилируем библиотеку штатным ildasm.exe из состава Microsoft SDKs. На выходе получим только один текстовый файл. В нем и поищем текст сообщения «Trial Mode. Please note that your…»

текстовый файл
image

Нашли метод
.method family hidebysig static bool e() cil managed
Который принадлежит классу CmdDocThisScope. Теперь возвращаемся в dotPeek.

Заголовок спойлера
image

Итак, что мы имеем. Мы нашли метод, который выводит сообщение о триальности APD и в зависимости от условия возвращает true или false. Найдем все места из которых вызывается этот метод

поиск вызова
image

Нашлось только 2 места вызова и это методы CmdDocThisFile::c и CmdDocThisScope::c.

нашли
image

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

В начале метода CmdDocThisScope::e увеличивается переменная f. Откроем окно «IL View» и найдем код команды:

IL View


В википедии есть статья с описанием этих инструкций.

Далее найдем этот метод в файле библиотеки APD. Делать это будем с использованием инструмента IDA. В окне с функциями найдем наш метод, и увидим уже знакомый код.

IDA


Выделив инструкцию ldsfld найдем ее бинарное представление в окне Hex View

Hex View


Описание команды подтверждает то, что мы нашли нужное место.

Wiki


Дальнейший анализ кода этого метода и последующие шаги выходят за рамки данной статьи.

Часть 2


Теперь, как уже опытные исследователи, найдем вызов диалога с сообщением "The 'Document all in Project' command is only available in the full version…”. Вот этот метод CmdDocThisProject::c

CmdDocThisProject::c


Класс CmdDocThisProject отвечает за команду «Обработать проект», метод «с» виртуальный. И он содержит только одно – это вызов диалога с сообщением. Ни условий, ни проверок. Поиск по исходному коду приводит нас к методу CmdDocThisProject::i, в котором есть то что мы ожидаем в CmdDocThisProject::c. Теперь в IDA мы с легкостью найдем нужные методы и сможем изучить CIL инструкции


Заключение


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

Комментарии (10)


  1. msin
    23.03.2019 08:46

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


    1. gdt
      23.03.2019 09:42

      Ваш Eazfuscator.Net точно так же распаковывается одним щелчком пальцев. Если хотите что-то, что не снимается за 5 минут — посмотрите в сторону ConfuserEx (https://github.com/XenocodeRCE/neo-ConfuserEx). Его применение требует определённых усилий, зато в наших тестах он оказался самым устойчивым к распаковке.


      1. msin
        23.03.2019 10:31

        Ваш Eazfuscator.Net точно так же распаковывается одним щелчком пальцев

        Позволю себе усомниться в этом утверждении…
        Скачал de4dot-net35.zip, он без проблем «очистил файл»:
        Detected Eazfuscator.NET 5.2-5.8 (EazDemo.exe)
        Cleaning EazDemo.exe
        Renaming all obfuscated symbols
        Saving EazDemo-cleaned.exe

        Вроде бы полный успех… только программа перестала работать:
        EazDemo-cleaned.exe
        Необработанное исключение: System.NullReferenceException: Ссылка на объект не указывает на экземпляр объекта.
           в Class97.method_249(Int32 int_0, Class24 class24_0)
           в Class97.method_48(Int32 int_0)
           в Class97.method_99(Class61 class61_2)
           в Class97.method_174()
           в Class97.method_72()
        --- Конец трассировка стека из предыдущего расположения, где возникло исключение ---
           в Class97.method_254(Object object_3)
           в Class97.method_279()
           в Class97.method_292(Object object_3, UInt32 uint_0)
           в Class97.method_72()
           в Class97.method_136()
           в Class97.method_97()
        --- Конец трассировка стека из предыдущего расположения, где возникло исключение ---
           в Class97.method_254(Object object_3)
           в Class97.method_279()
           в Class97.method_292(Object object_3, UInt32 uint_0)
           в Class97.method_97()
           в Class97.method_207(Object[] object_3, Type[] type_9, Type[] type_10, Object[] object_4)
           в Class45.Main(String[] args)

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


        1. gdt
          23.03.2019 10:44

          Заметьте, я ничего не говорил про запуск. Далеко не всегда для анализа нужно запускать исполнимый файл, тем более вы специально выбрали crackme. Возьмите лучше рабочий софт, с рефлексией, WCF, COM interop, упакуйте его так, чтобы он продолжал работать, тогда и поговорим ;)


          1. msin
            23.03.2019 11:32

            Вы, видимо, шутите… деобфускатор восстанавливает исходный код, но при этом не может сделать валидную исполняемую сборку?
            Запустил dotPeek, смотрю на восстановленный код:

            internal sealed class Class35
            {
              internal void method_0()
              {
                Class26.smethod_0().method_256(Class26.smethod_2(), "\"U#&7\"TZ.9", new object[1]
                {
                  (object) this
                });
              }
            }
            

            Очень содержательно… знаете, что значит "\«U#&7\»TZ.9"?
            Это название ресурса, где находится исполняемый код для виртуальной машины со случайной системой команд (каждая обфускация создаёт новую VM с уникальной системой команд).
            Более того, этот кусок кода выглядит точно так же до деобфускации с единственной разницей, что классам и методам присвоены печатные идентификаторы.
            В общем, признайте, вы погорячились насчет щелчка пальцев.
            И рефлексия, WCF, COM interop и т.д. здесь не причем. Виртуализация кода работает везде одинаково и если нет возможности увидеть 4 строки простейшего кода, то смысла ковырять что-то сложнее нет никакого.
            Понятно, что все имеет свою цену и нельзя виртуализировать весь код целиком, он будет с неприемлемой производительностью. Но найти 3-5 самых важных методов для виртуализации труда не составит.


            1. gdt
              23.03.2019 12:03

              Окей, в данном конкретном случае я и правда погорячился насчёт «щелчка пальцев», виртуализация и правда очень хорошо защищает код от реверсинга (можно вспомнить хотя бы VMProtect). Но мне такое не встречалось в боевом софте (я про .Net), буду очень признателен, если дадите ссылку на что-то подобное, чтобы поковырять своими руками. А чтобы сломать рефлексию зачастую достаточно банального переименования типов, не то что виртуальной машины :)


              1. msin
                23.03.2019 12:53

                Нет, к сожалению я не знаю, кто использует виртуализацию в коммерческих продуктах.
                На самом деле виртуализация защищает от статического анализа и не является абсолютной защитой… потому что аппаратно код выполняется достаточно прозрачно и нельзя отобрать доступ к оперативной памяти и процессору у реверсера.
                Для полноценной защиты нужно хотя бы частично выполнять код на защищенной аппаратной платформе, например, в ключе Guardant Code.
                Это тоже не абсолютная защита, но послойное сканирование ячеек памяти ключа это крайне дорогая операция (слышал, что ценник начинается от $100к).
                Кстати, Guardant так же предлагает виртуализацию кода (.NET и нативного).
                На самом деле можно пойти еще дальше и скомбинировать виртуализацию и защищенное выполнение — написать простенькую VM, записать её в ключ и заменить куски кода в исходной программе на вызовы интерпретатора в ключе. Куски байткода в исходной программе можно зашифровать и подписать ECDS подписью (это совершенно здоровая паранойя :-)


  1. gdt
    23.03.2019 09:18

    На самом деле большинство популярных обфускаторов для .Net (особенно dotnet reactor, который я вижу в каждой первой софтине) снимает не напрягаясь замечательная утилита под названием de4dot. Она консольная, но если использовать для декомпиляции Telerik JustDecompile — можно в пару кликов установить плагин de4dot и делать всё полностью через ui.


  1. kekekeks
    23.03.2019 09:50

    Хотите нормально убрать IL-код — собирайте с CoreRT. Только для этого UI-тулкит должен быть соответствующим (сейчас только AvaloniaUI умеет).


  1. IgorPie
    23.03.2019 20:55

    Это не реверс-инжиниринг, а достаточно банальный кряк, представленный методом "как срисовать сову"