Глава 53
Хорошо, победитель обоих частей конкурса из прошлой части - это Ularteck, который прислал оба скрипта [[ссылка]](.gitbook/assets/files/53/scripts%20(53).7z) и объяснение первой части в виде туториала, который мы используем здесь. Вот скрипт-победитель первой части, который предназначается для починки украденных байтов. Объяснение идёт ниже.
Теперь продолжаем объяснение, как всё это работает, написанное Ularteck'ом.
Часть 1: Ищем OEP и чиним украденные байты (написано Ularteck'ом)
Введение:
Хотя всё уже было объяснено в туториале №4 курса C97, написанного marciano, поясню некоторые вещи, чтобы пояснить как выполняется скрипт.
Marciano говорил об ошибке из-за антиотладочного OpenProcess'а, хотя у меня она не возникала.
Вот конфигурация используемых плагинов.
И с помощью этого крэкми у меня запустилось превосходно.
Метод, использованный для нахождения OEP для этой программы, мне понравился, так как часть встречается в новых версиях Asprotect, таких как 2.1 SKE , 2.2 SKE и 2.3 SKE, в которых есть украденный код.
Конфигурируем исключения.
Запускаем программу, и как только она начинается выполняться, идём в Log, чтобы посмотреть, каким было последнее исключение.
В моём случае это 0046D36B, что и будем использовать для скрипта, однако сейчас выполним ручную процедуру по поиску OEP.
Перезапускаем.
Нажимаем ALT+O.
И конфигурируем согласно картинке выше.
Запускаем программу с помощью F9 и проходим исключения с помощью SHIFT + F9, пока не окажемся в исключении по адресу 0046D36B, то есть в последнем
Теперь, находясь на последнем исключении, нажимаем ALT + M.
И устанавливаем точку останова на доступ к памяти в секции кода.
И нажимаем SHIFT + F9, чтобы пройти исключение и оказаться здесь.
Но это не OEP. Если посмотрим на стек.
Видим, что это исполняемый код. Выполненный код находится по адресу 8B0EA4 в секции 8B0000.
Так что перезапускаем. Делаем ту же процедуру, но точку останова устанавливаем теперь на доступ к этой секции. Начинаем делать скрипт для автоматизации задач.
После этого перезапускаем Olly и запускаем скрипт.
Нам показывается диалоговое окно, в котором нужно указать последнее исключение. В моём случае это 46d36b.
Нажимаем OK.
И когда оказываемся в этом исключении – выполнение останавливается.
Нажимаем ALT + M.
И вместо установки BP на доступ к секции кода, делаем это на секцию где бы выполненный код. Нажимаем SHIFT + F9 и оказываемся в украденном коде.
Если нажмём клавишу "-", то увидим, что находимся в последнем исключении.
И ниже находится переход, о котором нам говорил marciano, что он тот, который ожидаемо переносит нас на OEP, и как только мы его нашли, сразу модифицируем скрипт, чтобы когда, в последнем исключении, найдётся этот переход, на него, среди опкодов FFEO, была установлена точка останова, и когда находимся на переходе, мы нажимаем F7, чтобы перейти на украденный код.
В секции переменных скрипта добавляем ещё одну:
dir_JMP, в которой будет храниться адрес перехода JMP EAX.
И в метке ultima добавляем следующее:
Пробуем.
И готово — мы в OEP. Здесь дело усложняется, как сказал marciano, если скопируем этот код в точку входа:
Просто скопируем кусок украденного кода, как минимум прямой вызов, который находится в 008B0E9F.
И вставляем в точку входа:
Здесь у нас немного вставленного когда, видимо, что это косвенный вызов, который находится по адресу 0046b067. Он испорчен, посмотрим, куда он должен указывать. Идём туда, где находится OEP.
В оригинальном украденном коде находится CALL 004293A0.
Значит этот вызов нам нужно починить, чтобы, когда скопируем его в другую часть, он указывал в то же место. Как это сделать?
Идём по этому вызову в DUMP.
Не берём в расчёт E8 и используем четыре следующих байта.
FF B7 84 FC -> назовём их "OPCODES".
Берём адрес из Call 008B0E9F -> назовём его DIR_CALL.
Теперь этот вызов становится CALL 004293A0, а адрес получили следующим образом:
УКАЗАТЕЛЬ = OPCODES + DIR_CALL + 5
УКАЗАТЕЛЬ = FFB784FC + 008B0E9F + 5 = 004293A0
Хорошо, теперь у нас есть адрес, куда должен указывать вызов, чиним его с помощью новых опкодов и эти новые опкоды мы рассчитываем (не уверен, что правильно понял его здесь — прим. пер.) таким образом.
УКАЗАТЕЛЬ – НОВЫЙ_УКАЗАТЕЛЬ – 5 = НОВЫЕ ОПКОДЫ
НОВЫЙ_УКАЗАТЕЛЬ: рассчитываем таким образом.
Например:
Если скопируем первые строки украденного кода и скопируем их в точку входа.
Видим, что первый прямой вызов будет находится по этому адресу в точке входа 0046B057. Поэтому этот адрес рассчитываем так:
ИСХОДНЫЙ АДРЕС – АДРЕС OEP (УКРАДЕННЫЙ КОД) + АДРЕС ТОЧКИ ВХОДА, где ИСХОДНЫЙ АДРЕС = 008B09EF
АДРЕС OEP (УКРАДЕННЫЙ КОД) = 008B0E48
И АДРЕС ТОЧКИ ВХОДА : 0046b010
8B0E9F - 8B0E48 + 46B010= 0046B067 , по этому адресу будет находится наш починенный адрес.
Получается, что НОВЫЙ АДРЕС = 0046B067
Теперь у нас есть данные, чтобы рассчитать НОВЫЕ ОПКОДЫ.
004293A0 - 0046B067 – 5 = FFFBE334
У нас есть новые опкоды. Теперь осталось их отредактировать вручную. Идём в первый прямой вызов в украденном коде.
В дампе нам показывают вот что:
Не берём E8 и модифицируем следующие за ним четыре байта:
И заменяем их новыми опкодами.
Видим, что наш вызов стал ужасен, но если скопируем этот кусок кода и вставим в точку входа.
Остаётся починенный код, как раз такой, какой должен быть.
Теперь нужно сделать это для других прямых вызовов, но мы оставляем это скрипту, к которому добавляем следующее:
А потом ещё вот это:
Запускаем скрипт.
Видим, что починились все нужные вызовы.
Выбираем весь украденный код: делаем Binary Copy.
И вставляем в точку входа.
Находясь в точке входа, меняем EIP.
Говорим, что "Да" и делаем дамп.
Marciano говорил, что нужно поменять это
На
Ставим флажок "Rebuild Import" и нажимаем "Dump".
Вот наш сдампленный файл, в котором проблема с IAT.
Если попробуем открыть его с помощью Олли:
Попробуем починить его с помощью PE Editor'а [ссылка].
И готово.
В плагине HideOD есть ошибки, которые приводят к тому, что некоторые программы не запускаются, поэтому лучше заменить его на HideDebugger и OllyAdvanced [ссылка].
Ок, на этом можно завершить объяснение скрипта из первой части и перейти к скрипту из второй.
Метод для починки IAT – классический, который был хорошо объяснён в туториале marciano. Устанавливаем BPM ON WRITE на одном из плохих элементов IAT, который нашли ранее, находясь в OEP, а затем пытаемся попасть в место, где сохраняется плохое значение. К сожалению во время выполнения функции VirtualProtect стирает BPM ON WRITE, так что нужно установить BP на этой функции, чтобы произвести восстановление вышеуказанного BPM, когда она отработает.
Здесь видим изображение, взятое из туториала marciano, когда происходит останов на VirtualProtect, чтобы изменить права доступа к области IAT, так что в этот момент должны дойти до RET и снова установить BPM ON WRITE, чтобы продолжить с помощью "RUN", пока не остановимся на месте, где плохое значение сохраняется в элементе.
Останавливаемся здесь, где происходит сохранение. Плохое значение находится в EAX, а EBP указывает на элемент IAT, за которым мы наблюдали.
Затем устанавливаем BP несколькими строками выше, и когда останавливаемся, то трассируя видим следующее:
Заменяем правильный адрес функции на плохое значение, которое сохраняется несколькими строками ниже, так что работа сводится к тому, чтобы заNOPить эту строку, так как правильный адрес будет сохраняться чуть позже.
Так что скрипт толже установить местонахождение этой инструкции и заNOPить её.
Таким образом, первое что делает скрипт — это ищет адрес API-функции VirtualAlloc, так как первый раз, когда останавливается на ней, получает адрес области, где находится инструкцию, которую нужно заNOPить. Этот адрес может быть разным на разных машинах, поэтому его необходимо искать.
Здесь сохраняем адрес вышеуказанной функции в переменную var dir_VirtualAlloc, потом делаем то же самое с функцией VirtualProtect.
В переменной var dir_VirtualProtect сохраняем адрес соответствующей API-функции.
Затем нужно установить BP на VirtuallAlloc, делаем RUN и с помощью eob останавливаемся на указанном bp, сохраняем в переменной base адрес, где программа собирается создать область, в которой будет находится адрес для NOPинга.
Также устанавливаем BP на API-функции VirtualProtect.
Когда останавливаемся на BP, сравниваем равен ли ESI 460000, что является началом IAT, так как VirtualProtect использует его как аргумент функции для защиты указанной зоны и стирает BPMы, которые там находится, и если равна, то идём в метку retornar.
Где стирается BP на функции VirtualProtect, ищется значение, что будет находится в [esp] и устанавливается BP, чтобы остановиться, как только выйдем из API-функции.
Когда возвращаемся из API-функции, то уже можно искать инструкцию, которую будем NOPить. Искать надо по достаточно большой последовательности байтов, чтобы не встретить левые инструкции: 897C24188B4424.
После того, как нашли, делаем переход на процедуру заNOPления.
Вот и всё объяснение и спасибо Ularteck'у за хорошую работу и marciano за его прекрасный туториал, из которого мы взяли изображения для объяснения 2-ой части скрипта.
Думаю, что это поможет вам освоить технику создания скриптов, думаю, что она очень мощная и в ней нет ничего трудного, есть приложить немного упорства.
В 54-ой части мы начнём новую тему.
[C] Рикардо Нарваха, пер. Aquila
Last updated