Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 2 сентября!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь


Распаковка протектора SafeDisk 4.6, часть 4




Хорошая подборка видеоуроков, инструментов крэкера, книг и статей - здесь.

Распаковка SafeDisk 4.6 на примере

Launch Sid Meier's Civilization 4


ИНСТРУМЕНТЫ: SoftIce v4.3.2+IceExt v0.67, OllyDbg+plugun Olly advanced v1.26 beta 9,

PETools v1.5, ImpREC v1.6

VaZeR



ЧАСТЬ IV. SDAPI v2


Теперь нам осталось самое трудное, SDAPI. Которые представляют из себя не ссылки на какую-нибудь API, а самостоятельные вычислительные функции. Которые прочно вшиты в программу. И правильная их работа, необходима приложению, так как возвращаемые значения используются дальше в коде в других вычислениях. Из этого следует, что восстановить их можно, только если понять механизм их работы. А значит, нам нужно будет исследовать достаточно много кода, в котором ещё достаточно много мусора и разных ненужных вычислений. Но для начала посмотрим на сам вызов SDAPI:

Распаковка взлом SafeDisk

Но перед этим как попасть на этот код, мы кладём в стек два параметра. Из-за которых и зависит возвращаемое значение. К примеру, если я ложу в стек два нуля то получаю результат - 1С592D76, далее ложу две единицы и получаю совершенно другой результат - 9F0BAC47. Только одно это может говорить нам, что мы не можем просто взять и вписать строгое число. Да и самих этих вызовов в секции кода просто огромное количество. Теперь начнём разбирать этот код. В начале (4068С4) мы кладём в ESI адрес, куда мы попадаем через CALL ESI. У меня там всегда одно и тоже число - 6673003B (в секцию протектора), а это значит, что функция генерации значений только одна. Также можно сразу сказать, что в данной игре отладочные обработчики остались целы, это тот код, который начинается с адреса - 40690С.

Примечание: Зачем вообще разработчики протектора сделали эти отладочные обработчики, мне так до сих пор и не понятно, вероятно, они нужны на этапе внедрения SDAPI в программу, хотя тоже не понятно чем. Есть некоторые версии протектора, где нет отладочных обработчиков. И в таком случае придётся полностью переписывать функцию протектора. Но вроде бы в новых версиях протектора они есть всегда.

На этот адрес мы не попадаем из-за того, что у нас значение ESI равно 6673003B, а не нулю. Но переправка условных переходов нам не поможет, так как массивы необходимые в вычислениях были затёрты. А это значит, что нам нужно восстановить эти массивы, которые, к слову сказать, также вычисляются в коде протектора т.к. они играют ключевую роль в вычислении конечных значений. Здесь нам нужно обязательно посмотреть в дампе на адрес С76AD4, где увидим некую структуру, которая располагается в секции данных файла (SD_struct):

Распаковка взлом SafeDisk

Если посмотреть на ближайшие данные то можно, сказать, что это таблица, которая начинается с байтов 67231341 и заканчивается байтами 78822408. Если посмотреть выше или ниже, то мы увидим ещё такие же таблицы. Всего у меня их 40h. Следующее число, которое идет за началом, это 184 это размер этой таблицы, третье число всегда 1. А вот предпоследнее 15 это номер таблицы, они нумеруются, начиная с нуля. Вот именно эту таблицу нам и надо будет восстановить в последствии.

Теперь начнём двигаться дальше по коду, по адресу 4068DE мы кладём в EDI ещё одно интересное число 16ABDF4. Перейдя в дампе на него увидим следующее:

Распаковка взлом SafeDisk

Это структура (назовём её Initial table) совпадает с нашей, тоже начало - 67231341, тот же номер - 15. Но это не те значения, которые должны быть в нашей SD_struct. Здесь находятся значения, благодаря которым в последствии будут получены нужные нам величины.

Также в изучение механизма SDAPI очень помогает сравнение кода в отладочном обработчике с нашим в протекторе. Например, чтобы понять какие величины нужны отладочному обработчику, нужно исследовать его начало:

Распаковка взлом SafeDisk

В CALL по адресу 406B9B, мы кладём указатель на нашу SD_struct и в CALL - 406BAB мы считываем нужные значения из таблицы:

Распаковка взлом SafeDisk

Здесь мы считываем 9 dword из нашей таблицы, а это число совпадает с тем, что у нас есть в Initial table (данные после 01 15 01).

Примечание: Также просматривая, эти отладочные обработчики, можно встретить ещё один парадокс:

Распаковка взлом SafeDisk

Это просто мусор, к тому же не характерный для SafeDisk (слишком уж простой). Так и хочется спросить, зачем он нужен здесь. В отладке этих самых SDAPI он точно не зачем.

Теперь, когда разобрались с нашими таблицами, вернёмся опять к коду, который подготавливает данные для функции протектора. У нас остался здесь ещё один интересный момент CALL 406AD0. Зайдя в который увидим процедуру, которая шифрует данные, передаваемые протектору, т.е. два наших параметра, которые мы положили в стек перед заходов процедуру SDAPI, также из стека берутся ещё два значения, те, что были над стеком в момент захода в SDAPI, но они не влияют на результат вычисления, а добавлены только для веса:

Распаковка взлом SafeDisk

В процессе шифровки используются две случайные величины - адреса 406AE1 и 406AE8, которые зависят от EDX, например я беру от туда значения - EF00900B, B30C900B. ниже еще есть три константы - 48201245, 48201241, 48201245. Благодаря всем этим числам, в конце концов, мы получаем ещё один массив (CryptData), который расположен по адресу EAX, число туда ложится ещё до захода в эту процедуру:

Распаковка взлом SafeDisk

Первое значение в этом массиве соответствует нашему второму числу, которое мы брали случайным образом. А вот первое затирается. В последствии мы расшифруем этот массив обратно.

Теперь нам нужно начать изучать код самого протектора. Сначала мы должны найти основную ветку этой функции. Найти её не сложно, первая же длинная процедура, она и есть. Начинается она вот так:

Распаковка взлом SafeDisk

Также перед изучение этой процедуры, надо сказать о представлении данных в протекторе. А именно то, что все важные данные находятся не в искомой форме, а зашифрованы и расшифровываются непосредственно перед вычислением, где результат опять зашифровывается. Причем ключ для зашифровки, каждый раз генерируется новый. А значит и все данные в коде, постоянно меняются, что не очень удобно для нас. Поэтому сначала решим эту проблему. Процедуру шифрования найти не сложно, так как она, наверное, самая здесь распространенная. Например, войдём в CALL 66717EFC, в которой найдём следующею процедуру:

Распаковка взлом SafeDisk

Её легко узнать по множеству команд XOR. После прохода CALL 667154AE у нас в EAX и будет, находится ключ для зашифровки данных. Поэтому войдём в него:

Распаковка взлом SafeDisk

Этот самый ключ мы кладём в ECX с адреса 667ADB10. Попробуем отследить, где происходит запись по этому адресу, поставим на этот адрес аппаратный брекпоинт на запись двойного слова и перезапустим программу. Остановимся здесь:

Распаковка взлом SafeDisk

Именно здесь мы и записываем наш ключ, поэтому каждый раз при запуске будем менять значение в ECX на одно и тоже, само число можно взять любое, кроме нуля.

Теперь вернёмся опять к изучению нашего кода. Исследуя код отладочного обработчика, можно придти к выводу, что главными элементами в этом коде являются как раз отсутствующий массив SD_struct. Исходя из этого можно предположить, что и вначале кода протектора мы должны получить этот массив из нашего Initial table т.к. больше у нас не чего нет, похожего на таблицу SD_struct. Поэтому, чтобы найти такое место нужно просто трассировать код и смотреть, когда у нас будут использоваться значения из Initial table. Ну а когда найдём такое место, то начинаем изучать этот код более основательно. Таким образом, я нашёл такое место:

Распаковка взлом SafeDisk

Эта процедура и вычисляет нужный нам массив. Сама эта процедура находится, как и предполагалось во втором же CALL в главной ветке. Через который мы попадаем на ещё одну процедуру, которая то же играет достаточно большое значение в вычислениях и на которую мы попадаем несколько раз в процессе работы SDAPI. Так вот опять же в начале этой процедуры (третий CALL) мы и попадаем на нужное место. Эту процедуру нам нужно разобрать более подробно.

Примечание: Во всех этих процедурах SDAPI столько много кода, что изучение всех их может занять целую вечность. Поэтому лучше всего при изучении руководствоваться принципом, что всё самое важное находится в конце процедур. А буквально это последний или в большинстве случаев предпоследний CALL.

Если посмотреть за работой этой процедуры, то можно сказать, что нужный нам массив создаётся только один раз. Например, если один раз создать массив для SD_struct 15, то при следующих вызовах он уже не будет создаваться заново, а будет использоваться уже готовый. Для этого имеется соответствующая проверка:

Распаковка взлом SafeDisk

Если массив был создан, то мы кладём по адресу EBP-4 число FFFFFFFF, которое выполняет роль метки. Таких проверок тут две.

Далее следует одна процедура (66729431), которая даёт нам информацию о том, что в тех девяти двойных слов в массиве, на самом деле восемь значений и одно заголовочное, оно находятся самым первым по счёту. И для всех массивов будет одинаковым - 5CAC5AC5. Это можно проверить, если зайти в эту процедуру. Сам вызов, которой не строгий, а с помощью значения регистра:

Распаковка взлом SafeDisk

Зайдя в которую переёдём в конец процедуры:

Распаковка взлом SafeDisk

Теперь зайдём в предпоследний CALL:

Распаковка взлом SafeDisk

Эта процедура используется для получения каждого элемента массива. Можно посмотреть на работу каждой имеющейся здесь процедуры. Но конечный результат вычисляется в CALL 66726627. Перейдя туда попадём в процедуру, где результат вычисления получается в процедуре - 667183D2:

Распаковка взлом SafeDisk

Сама эта процедура может выполнять несколько различных вычислений XOR, AND, OR. Где тип операции зависит от передаваемого параметра, в данном случае 24С3E94D, что соответствует XOR. Перейдя в эту процедуру, мы и увидим три наших сравнения:

Распаковка взлом SafeDisk


Распаковка взлом SafeDisk

Распаковка взлом SafeDisk

Конечный результат получается опять в предпоследнем CALL. После исполнения, которого в EAX и будет заголовок массива SD_struct - 5CAC5AC5.

Далее вернёмся к главной ветке вычисления восьми значений. Где имеется цикл, который за одну итерацию вычисляет одно значение массива. Процедуры вычисления те же самые, что и при вычислении заголовка массива. Сам вход в вычисления выглядит так:

Распаковка взлом SafeDisk

Этот CALL ведёт в ту же самую процедуру 66729431, где мы и вычисляли значения заголовка. Результат, получается, по тому же адресу.

Вот собственно и всё, что нам нужно знать о SDAPI. Так как есть отладочные обработчики, то можно остановится и на этом. Теперь нам нужно восстановить все массивы и правильно настроить SD_struct. Для восстановления массивов я решил написать скрипт для OllyDbg:


 			 			var startscan
 			 			var startdata
 			 			var addr_SDAPI
 			 			var order
 			 			var struct
 			 			var massiv
 			 			mov startscan, 401000
 			mov 			startdata, C74AC8 ; Начало 			таблиц 			SD_struct
 			find 			startscan,#83EC20568B35# ; 			Ищем 			вызов 			SDAPI
 			mov 			addr_SDAPI, $RESULT ; Кладём 			в 			переменную 			адрес 			SDAPI
 			 			
VIZOV: cmp struct, 40 ; Проверяем не закончили ли SD_struct je END inc struct add addr_SDAPI, 20 ; Переходим к команде, которая кладёт в EDI адрес Initial table add startdata, C0 ; Переходим в SD_struct к адресу Initial table mov [addr_SDAPI],startdata ; Заменяем в нашем вызове SDAPI адрес на Initial table sub addr_SDAPI, 20 sub startdata, C0 mov eip, addr_SDAPI add startdata,18
CIKL: bp 667269CC run cmp eip, 667269CC jne ERROL ; Эти проверки очень помогают при отладке скрипта inc order bp 66729519 run cmp eip, 66729519 jne ERROL bp 66726F36 bc 66729519 run cmp eip, 66726F36 jne ERROL bp 6672666C bc 66726F36 run cmp eip, 6672666C jne ERROL bp 667184DE bc 6672666C run cmp eip, 667184DE jne ERROL mov [startdata], eax ; Как найдём правильный адрес кладём его в SD_struct bc 66729519 bc 667184DE add startdata,4 cmp order,8 ; Проверка не закончились ли наши значения в массиве je KORECTIROVKA jmp CIKL
KORECTIROVKA: bc 667269CC sub startdata, 38 ; Возвращаемся на начало SD_struct add startdata, 184 ; Переходим к следующей структуре mov order,0 jmp VIZOV
ERROL: mov eax, FFFFFFFF ; В случае ошибки положим в EAX метку о том, что у нас есть ошибка
END: ret

Примечание: Так как многие процедуры при вычислении используются ещё достаточно много в других вычислениях, то поэтому я использовал в скрипте тот же путь, что и при ручном поиске нужного значения. С помощью установки брекпоинтов.


После того как мы восстановили все значения, т.е. восемь двойных слов в каждой структуре. Нам нужно ещё правильно скорректировать эти самые структуры. Во-первых, мы должны добавить те первые значения, которые отсутствуют в начале таблице, их можно взять из Initial table. Также добавить заголовок массива - 5CAC5AC5. Ну и на последок убрать все значения в середине массива, которые больше не нужны, такие как адрес Initial table, адрес процедуры протектора. Для этого я решил написать ещё один скрипт:


 			 			var startdata
 			 			var temp
 			 			var order
 			 			var massiv
 			 			var nomer
 			 			mov startdata, C74AC8
 			 			
COPY: add startdata,C0 mov massiv, [startdata] ; Положим адрес Initial table sub startdata,C0
CIKL: mov temp, [massiv] ; Этот цикл копирует первые значения до заголовка с Initial table mov [startdata],temp add startdata,4 add massiv,4 inc order cmp order,5 je HEAD jmp CIKL
HEAD: mov [startdata],5CAC5AC5 ; Кладём заголовок mov order,0 add startdata,24 ; Переходим на адрес который находится после 8 dword
CLEAN: mov [startdata],0 ; Очищаем все не нужные значения в структуре inc order cmp order,2C ; Я очищаю сразу блок размером 2C je DALEE add startdata,4 jmp CLEAN
DALEE: sub startdata,E4 ; Возвращаем адрес на начало SD_struct add startdata,184 ; Переходим к следующей структуре inc nomer cmp nomer,40 je END mov order,0 jmp COPY
END: ret


После запуска этих двух скриптов скопируем все 64 таблицы в наш файл с восстановленным импортом. После чего игра работает, без никаких проблем.

Но это ещё не всё, осталось найти Trigger table. Которые тоже затёрты в секции данных. Найти эти таблицы можно по следующей команде:

mov dword ptr[esp+XX], адрес в секции данных

Можно искать через бинарный поиск (С74424) или можно написать скрипт. Просмотрев всю секцию, я нашёл только одну такую команду. Где в секции данных находятся нули. Были, конечно, и другие команды, где тоже в секции данных находятся нули, но нам нужен только тот случай, где рядом есть «известный байт»:

Распаковка взлом SafeDisk

В моём случае «известный байт» равен 2, но он может быть любым. Также ниже этой команды должны быть вызовы SDAPI:

Распаковка взлом SafeDisk

Сначала мы вызываем SDAPI по адресу 410D70 и получаем в EAX значение, которое будет использоваться в следующем вызове 410AE0.

Начнем теперь трассировать код этой SDAPI, пойдём пока только по главной ветки функции до первого оператора switch … case, который выглядит следующим образом:

Распаковка взлом SafeDisk

Этот оператор определяет ход дальнейших вычислений конечных значений. Таких операторов тут два, этот первого порядка, а дальше будет ещё и второго порядка. Нас с вами интересует второй вариант, где происходит обработка структур. Двигаемся дальше и находим второй оператор switch … case:

Распаковка взлом SafeDisk

За обработку структурных данных отвечает CASE под номером 16. И у нас как раз в EAX число 16. Это значит, что всё идёт правильно. Теперь нужно найти код обрабатывающий структурные данные:

Распаковка взлом SafeDisk

В данном месте кода у нас в EAX ложится указатель на адреса структур в выделенной памяти:

Распаковка взлом SafeDisk

Так как у меня только одна структура, то поэтому все три адреса одинаковые. Теперь перейдём на этот адрес и увидим следующее:

Распаковка взлом SafeDisk

Здесь восьмое двойное слово это наш «известный байт». Теперь наша задача преобразовать эту таблицу к нормальному виду. Структура состоит, в общем, из двадцати трёх двойных слов. Первые четыре двойных слова мы пропускаем, вместо числа 01688F58 ставим маркер начала 41162469. Далее всё оставляем, как есть, только заменив FFFFFFFF на 0. Начиная с числа 13, в моём случае мы должны пронумеровать восемь двойных слов, начиная с единицы. И в конце поставить маркер конца 73872468. В общем, должно получится так:

Распаковка взлом SafeDisk


Напоследок можно сказать, что SafeDisk очень интересный протектор, на изучение которого не жалко потраченного времени и сил. Также отдельно хочется поблагодарить DillerInc за его помощь. С помощью него было исправлено достаточно большое количество ошибок и недочётов.



Распаковка взлом SafeDisk








Скачать статью "Распаковка протектора SafeDisk 4.6, часть 4" в авторском оформление + файлы.
пароль архива на картинке



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS