niksankin / rev_task

Task for 5'th year MIEM course "Protection of Programs and Data"

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Задание №1 по курсу "Защита программ и данных"

Выполнил студент группы СКБ161 Санкин Н.А.

Описание программы

Программа запрашивает у пользователя некоторые данные с максимальной длиной в 256 байт (данные вне этой границы обрезаются). Затем программа преобразовывает введённые данные, выводя результат на экран. Для решения задания от пользователя требуется по предварительно известному результату выполнения программы найти исходные данные, которые были использованы для получения данного результата. Для выполнения задачи требуется дизассемблировать алгоритм преобразвания ввода, защищённый методами антиотладки.

Методы защиты кода от анализа

Модификация кода программы во время выполнения

Данный метод позволяет изменять алгоритм программы во время выполнения, что затрудняет статический анализ (код в скомпилированном файле отличается от кода, на самом деле исполняемым программой). По умолчанию сегмент с кодом программы имеет права доступа "read-only". Смену прав доступа к региону памяти на "read-write" можно выполнить при помощи вызова функции VirtualProtect из состава WinAPI с указанием флага PAGE_EXECUTE_READWRITE. Далее доступ к байтам внутри региона можно производить так же, как и в обычном массиве на стеке.

Vectored Exception Handling (VEH)

В Windows имеется возможность определения пользовательских обработчиков исключений. Добавление нового обработчика можно выполнить при помощи вызова функции AddVectoredExceptionHandler. Данный метод основывается на том, что при выполнении кода отладчиком все исключения в программе будут перехватываться и обрабатываться отладчиком, если явно не передавать их обработку коду программы. Таким образом, отладчик заменит пользовательские обработчики исключений на собственные, и выполнение программы под отладчиком будет отличаться от выполнения без отладчика.

Хеширование/вычисление контрольной суммы от кода программы

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

Выполнение кода при инициализации статических переменных

В С++, вызов конктрукторов статических объектов и инициализация статических переменных происходят до выполнения основного кода программы (функции main()). Это позволяет скрыть часть выполняемого кода, заставляя аналитика искать адреса функций, выполняемых в startup коде программы.

Список простых методов антиотладки

  • Спецификатор функции __declspec(naked) - компилятор сгенерирует код функции без пролога и эпилога. Может заставить дизассемблер решить, что функция является набором байт, а не исполняемым кодом. Вынуждает программиста вручную управлять стековым фреймом функции.
  • IsDebuggerPresent - данная функция проверяет наличие флага BeingDebugged в структуре PEB (Process Environment Block) процесса, что позволяет обнаружить отладчики пользовательского уровня. Так как вызов IsDebuggerPresent может перехватываться плагинами отладчиков или легко находиться в таблице импорта программы, можно заменить вызов этой функции на эквивалентный по функционалу код:
    mov eax, fs:0x18		
    mov eax, [eax + 0x30]
    mov al, [eax + 2]
    
    и проверять равенство значения в регистре al единице.
  • Использование jmp от значения регистра - может заставить отладчик подумать, что jmp является концом функции.
  • Использование инициализации локальных массивов байт на стеке (инициализация вида nies[]{}) - помещает данные в код функции, а не в секции данных. Для строк позволяет избежать поиска по строкам в файле (но не поиска по байтам в коде).
  • "Мусорный" и обфусцированный код - мешает анализу кода аналитиком. В комбинации с другими методами может заставить дизассемблер воспринять часть кода в виде обычного набора байт

Механизм работы программы

Основная функция для анализа

Зашифровка данных выполняется функцией generate_flag(). Она запрашивает данные у пользователя, и затем преобразует их при помощи побайтовой операции xor. Исходная версия функции, выполняющая xor со значением 0x94, призвана запутать аналитика.

Этап выполнения до функции main()

Описанная версия функции generate_flag() будет выполнена только при отладке программы; за модификацию процесса выполнения без отладчика отвечают методы специального класса Hidden:

Hidden()
hidden_prepare_code()
hidden_prepare_data()
hidden_add_veh()

Данные методы будут выполнены до вызова main() в приведённом выше порядке. Конструктор Hidden() устанавливает права read-write на регион памяти внутри кода generate_flag(), после чего метод hidden_prepare_code() заменяет инструкции

mov bl, [eax]
xor bl, 0x94

на код

mov bl, [eax+1]
xor bl, [eax]

, а также заменяет инструкцию xor eax, fs:[0] на xor eax, ds:[0], где последняя будет вызывать программное исключение. Таким образом, мы добиваемся изменения видимого при статическом анализе алгоритма шифрования и вызова исключения, которое будет обработано пользовательским обработчиком vectored_handler(), регистрируемым внутри функции hidden_add_veh(). Оставшаяся функция hidden_prepare_data() циклически заполняет массив flag байтами гаммы и подготавливает адреса начала и конца хешируемого кода. Эти адреса будут использованы внутри обработчика vectored_handler().

Итого, этап выполнения кода до функции main() привносит следующие изменения в код генерации флага:

  • В качестве данных массива flag берутся не исходные данные флага, а данные, над которыми поблочно выполнена операция xor с данными заранее определённой гаммы
  • Вместо выполнения над байтами массива flag операции xor со значением 0x94, выполняется xor каждого байта из flag со следующим за ним по порядку байтом
  • Вместо инструкции, не вызывающей программных исключений, подставляется всегда вызывающая исключение инструкция, внешне напоминающая модифицированную

Последний шаг приводит к возникновению исключения и передаче управления обработчику vectored_handler().

Этап выполнения обработчика VEH

Код обработчика vectored_handler() использует найденные функцией hidden_prepare_data() адреса начала и конца хешируемого кода для вычисления от него хеш суммы SHA256. Данная хеш-сумма используется как ключ для алгоритма rc4, который затем выполняет шифрование массива flag. Данные из получившегося массива затем возвращаются пользователю.

Сборка

Для сборки требуется открыть файл проекта crack.sln и собрать решение с помощью Visual Studio (v142) в конфигурации Debug-x86

About

Task for 5'th year MIEM course "Protection of Programs and Data"


Languages

Language:C++ 96.0%Language:C 4.0%