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

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


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


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




Очень удобно, когда все крэкерские инструменты, книги и статьи в одном месте. Используйте сборник от EXELAB - вот тут.

Распаковка 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


ЧАСТЬ II. Nanomites


В первой часть мы получили дамп игры. Поправив первые два байта на OEP. Нужно также прописать этот адрес в файл OEP 99B988-400000=59B988. Теперь попробуем запустить его в OllyDbg, остановимся здесь:

Safedisc Nanomites

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

Примечание: В моём случае не больше 8, вот соответствующий код (в EAX количество INT3):

Safedisc Nanomites

Но я не уверен, что это так всегда. Хотя я и 8 не когда не видел в коде, в основном большая масса это 2 и 3 .

Самая первая проблема, которую скрывают в себе наномиты это их поиск в коде. К примеру, вот это уже не наномиты:

Safedisc Nanomites

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

Для восстановления наномитов очень удобно использовать два отладчика. Теперь, чтобы нам начать изучать код ответственный за работу наномитов, мы должны дойти до OEP в SoftIce зациклить его (EB FE). Свернуть SoftIce (Ctrl+D) запустить OllyDbg и приатачиться к отладчику SafeDisk - ~e5.0001. За обработку прерываний (INT3) ответственна API функция WaitForDebugEvent. (Перейдём к ней в OllyDbg Ctrl+G и впишем название). Поставим брекпоинт не на начало, а на RET и запустим приложение (F9). Теперь перейдём опять в SoftIce и восстановим наши байты EB FE на 6A 74. Это мы делали, чтобы программа не выполняла свой код, пока мы переходили в OllyDbg. Сворачиваем SoftIce и мы сразу остановимся на нашем брекпоинте. Выходим из этой функции и попадаем на следующий код:

Safedisc Nanomites

Условный переход по адресу 66705509 зависит от значения EAX. Если функция завершилась успешно, то возвращаемое значение 1. Это одна из наших контрольных точек.

Теперь нужно сказать о самом методе восстановления наномитов. На мой взгляд, есть только три метода восстановления. Самый сложный из них это найти зашифрованные таблицы и расшифровать их. Но в SafeDisk используется достаточно запутанный и сложный алгоритм. Поэтому этот метод я отверг сразу. Второй метод это поиск условного перехода в отладчике, который отвечает за запись наномитов в программу. И если занопить его то наномиты будут записываться сразу, а не после второго исполнения. Далее нужно очень тщательно играть в игру, т.е. стараться использовать всё, что только возможно. И конце концов сдампить. Этим мы добьёмся, что большая часть наномитов будет восстановлена. Но я замечу, что только часть. Существует потенциальный процент не восстановленных наномитов и если к этому прибавить ещё и сложность с их поиском, то этот метод лучше не использовать. Я выбрал третий метод, это можно сказать синтез двух предыдущих. Мы исследуем отладчик, находим все контрольные точки и переделываем его так, чтобы он сам всё сам расшифровал и записал в программу. Это оказалась не так уж и сложно, как мне показалась сначала его изучения. Но, исследовав отладчик, у меня родилась мысль, что если взять и вместо WaitForDebugEvent вписать свой код который бы подавал адреса всей секции, т.е. с 401000 до B4DFFF (в моём случае). И тут я задался вопросом, а есть ли условный переход, который отвечает за «правильность наномита». И такой нашёлся. Что нам очень помогает. Хотя если просто подумать логически то такой должен быть, который в случае несовпадения адреса с базой вёл бы на выход или окно с ошибкой. Теперь нужно всю эту задумку реализовать.

Стоя на том же самом адресе, и взглянув в стек, то мы видим, что адрес наномита записывается по адресу ESP+24 (в моём случае):

Safedisc Nanomites

Теперь поставим брекпоинт на API функцию WriteProcessMemory, именно не далеко от ней идут интересующие нас проверки. Но до этого поставим ещё брекпоинт на наше сравнение по адресу 66705509, оно нам ещё пригодится так как, чтобы понять всю эту работу нам придётся достаточно много раз подавать различные значения, как правильные, так и нет. Запустив программу и остановившись на нашем брекпоинте, выйдем из функции (Ctrl + F9). Далее попадём на код, проверяющий успешно ли завершилась функция:

Safedisc Nanomites

Опять выходим от сюда и попадем на ветку программы, где и происходит всё самое интересное. Интересующий нас переход, который отвечает за запись наномита со второго раза, должен находится где-то выше того места, откуда мы вышли. Начинаем смотреть, и находим его:

Safedisc Nanomites

Чтобы проверить, правы мы или нет, можно поставить на него брекпоинт и посмотреть на его работу. Если наномит выполнился один раз то в AX 0, второй 1. Вот и ещё одна контрольная точка. Также неплохо бы запоминать адреса настоящих наномитов, которые появляются после вызова WaitForDebugEvent, так как нам они ещё понадобятся для тестирования нашего кода. Исследуя эту достаточно большую процедуру можно найти ещё один достаточно интересный переход, который определяет «правильный наномит» или нет. Он находится ещё выше сравнения 6673С6F6. Его найти не сложно, нужно поставить брекпоинт на самое начало процедуры:

Safedisc Nanomites

Но перед тем нужно изменить значение в ESP+24 (Примечание: это нужно делать сразу после вызова WaitForDebugEvent) на неправильное, т.е. на тот адрес, где нет наномита в программе. И после того как мы вписали «левый» адрес начинаем трассировать код в той самой процедуре с самого начала и смотря где мы переходим через запись наномита в программу. У меня это переход по адресу 6673BF92. Именно он определяет «правильность наномита»:

Safedisc Nanomites

Также ещё есть один условный переход, который завершает наше приложение. Это связано с вызовом API функции ContinueDebugEvent (Примечание: ContinueDebugEvent продолжает работу основной программы. А так как мы больше не будем использовать WaitForDebugEvent то, и продолжать нам не чего). Если мы будем её обрабатывать то возвращаемое значение у нас 0, а должно быть 1 в случае успеха. Вот именно из-за этого мы и идём на выход. Поэтому поставим на неё брекпоинт и когда он сработает, выходим из функции и попадаем на следующий код:

Safedisc Nanomites

Здесь нет проверки, но она будет чуть-чуть дальше. Выходим через RET и попадаем сюда:

Safedisc Nanomites

Опять выходим через RET и вот тут уже, и есть проверка:

Safedisc Nanomites

Если этот условный переход срабатывает (667054BC),то мы переходим через WaitForDebugEvent, а этого не должно быть. Поэтому нам нужно занопить этот условный переход или сам вызов ContinueDebugEvent со всеми его PUSH не забыв поставить в EAX единицу, что лучше всего. Теперь рассмотрим место вызова API функции WaitForDebugEvent. Если код по адресу 66705514 не получит исполнения то мы можем прямо суда вписать наш патч не опасаясь за нехватку места.




Впишем достаточно легкий и понятный код:


 			66705500 			  MOV 			EAX,1 			 			
 			66705505 			  CMP 			DWORD 			PTR 			SS:[ESP+24],0B4DFFF 			;сравнение, не закончилась ли секция
 			6670550D 			  JE 			SHORT 			~df394b.66705518 			;если да то переходим
 			6670550F 			  INC 			DWORD 			PTR 			SS:[ESP+24] 			;если нет, то увеличиваем адрес на единицу
 			66705513 			  JMP ~df394b.66705472 ;идём далее на выполнение кода
 			66705518 			  NOP ;суда мы переходим, если закончилась секция. Поэтому нужно 			суда поставить брекпоинт.
 			 


Перед запуском нашего кода нужно изменить значение в ESP +24 на 401000, т.е. самое начало. Сначала я трассировал просто вручную (F8) и смотрел, как восстанавливаются наномиты, всё было нормально. Но когда я запустил программу (F9) убрав все брекпоинты кроме адреса 66705518. И после того как наш код пробежал через всю секцию в поисках наномитов. Я перешёл в SoftIce (addr civilization4) и посмотря на секцию кода и увидел, что восстановилось всего несколько наномитов, которые были в самом начале. А остальные нет. Прежде всего, я подумал, что существует какая то проверка на количество наномитов, но всё оказалась значительно проще. Исходя из того, что существует условный переход, который отвечает за запись наномитов со второго раза. Все эти адреса, должны, где-то хранится, а как позднее я выяснил, туда записываются все значения даже неправильные. И вот именно из-за того, что мы начинаем подавать 1000 адресов, и происходит какая то нестыковка. Так как даже если сейчас прописать правильный адрес наномита, то он определяет его как неверный. За это отвечает ранее найденный переход по адресу 6673BF92. Перезапустим игру и посмотрим на сравнение 6673BF8A. Если в EBP-1C - 00A0F498 ноль, то мы идём на восстановление наномита, если единица, то нет. Ставим опять брекпоинт на самое начало этой процедуры - 6673BCE8. И когда сработает, смотрим, где идёт запись по адресу 00A0F498. Сначала всегда записывается единица:

Safedisc Nanomites

У меня адрес наномита был правильный, поэтому трассируем дальше и смотрим в дамп, где идёт запись нуля:

Safedisc Nanomites

После прохождения CALL 6673BF10 в EAX ноль, где он далее и записывается. Теперь надо изучить процедуру 6673BF10. Где в самом конце и идёт решение о записи единицы или нуля:

Safedisc Nanomites


Если условный переход по адресу 66787015 не срабатывает, то возвращаемое значение EAX ноль. Если наномит правильный, то в EAX и EBX по адресу 66787013 единица. Нужно теперь исследовать CALL - 6678700E. Зайдя в него, посмотрим на работу условных переходов:

Safedisc Nanomites

Тут можно увидеть, что если условный переход 667059F7 не срабатывает то наномит правильный. Сначала увеличивается EBX на единицу - 66705A01, а потом EBX ложится в EAX. Теперь посмотрим на условие перехода - 667059F3. Если EDI не равен ESI+8, то переход не срабатывает. Здесь очень важно посмотреть на сами значения:

Safedisc Nanomites

Меня здесь заинтересовали адреса A2XXXX. Перейдя в дампе на какой-нибудь из этих двух можно увидеть, что там находятся какие-то таблицы. Это и есть место, куда мы записываем наши адреса, также можно ещё сказать, что именно там, и находятся зашифрованные наномиты, но это мы узнаем чуть позднее. Теперь поставив брекпоинт на адрес 667059F3. Я изменил адрес настоящего наномита на любой другой неправильный. И посмотрел на новые значения:

Safedisc Nanomites

Значит значение в ESI+8 постоянное, а изменяется только EDI. А в EDI значение ложится по адресу 667059EC. Поставим брекпоинт на него и посмотрим, что за адрес у нас в EAX:

Safedisc Nanomites

Теперь найдём место, где происходит запись по адресу A0F068. А она происходит выше в CALL - 667059E4. Теперь попробуем изучить и его:

Safedisc Nanomites

Запись по адресу A0F068 происходит в 6671CBC0 из адреса EAX - 6671CBBA. Посмотрим, что это за адрес, поставив брекпоинт на 6671CBBA и запустив программу остановившись, увидим следующие:

Safedisc Nanomites

Запись идёт с адреса A0F03C, посмотрим, откуда записываются данные в A0F03C. А они туда записываются по адресу 6671CBB4. Посмотрев за работой этого кода можно сказать, что если условный переход по адресу 6671CBAD срабатывает то у нас неправильный адрес наномита, если нет то правильный адрес. Посмотрим на условие - 6671СBAA:

Safedisc Nanomites

Это при условии, что адрес наномита правильный, вот здесь и проходит сравнение нашего зашифрованного адреса наномита с базой. Перейдём в дампе на адрес A2B8B4 (d 0A2B8B4). И увидим следующие:

Safedisc Nanomites

Здесь у нас имеется таблица, где для каждого наномита выделено 32 байта с зашифрованными данными. Теперь что бы узнать, где у нас кэшируются адреса, сделаем бинарный поиск. Поставив в самое начало секции указатель и Ctrl+B:

Safedisc Nanomites

Где впишем любой адрес, который мы подавали стоя на WaitForDebugEvent, к примеру, я взял 40866E. Находим сначала такое место:

Safedisc Nanomites

Здесь нет нечего интересного, для нас, сюда мы просто записываем текущий адрес наномита. Ищем дальше (Ctrl + L):

Safedisc Nanomites

Вот это уже интересно, отсюда видно, что я запускал код четыре раза с одним и тем же адресом 40866E. Продолжаем искать:

Safedisc Nanomites

Здесь почему-то только два адреса. Делаем поиск дальше:

Safedisc Nanomites

Вот сюда мы пишем только, когда мы подаем на разные адреса. Именно это нам и надо исправить. Так как, исправляя это, мы исправим и все другие до записи в секцию. Теперь нам остается найти место в программе, где происходит запись этих значений. Для этого устанавливаем опять брекпоинт на самое начало процедуры по адресу - 6673BCE8. Мы его уже не однократно использовали в процессе изучения.

Примечание: Я не стал использовать брекпоинт на запись так как, слишком много лишних срабатываний. Быстрее получится найти трассировкой.

Я решил сначала найти место, откуда идёт запись по адресу A2BFDC. Для этого нужно указать другой адрес в WaitForDebugEvent. И когда остановимся на нашем брекпоинте. Начинать трассировать код, попутно смотря в дамп на предмет записи нового значения. Если такое происходит, то ставим на соответствующий CALL брекпоинт и в следующий раз уже входить в него, не забыв перед этим сменить адрес наномита. У меня первый адрес - это 6673C858 зайдя в который, увидим следующие:

Safedisc Nanomites

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

Примечание: Запись идёт не непосредственно на этот адрес, а каждый раз чуть ниже. Но это легко заметить.

Следующий адрес это 6673С894. Зайдя в который увидим следующее:

Safedisc Nanomites

Видим аналогичную ситуацию, если заменить условный переход по адресу 6673B603 на NOP, а по адресу 6673B60A на JMP. То этим мы перестаём записывать в секцию наши адреса. Теперь попробуем полностью пропатчить отладчик, и запустить и проверить всё ли мы сделали правильно. Оказывается ещё не все, так как наномиты не восстановились. Переходим в дампе на наши зашифрованные таблицы и посмотрим, что опять не так. Прокручиваем в самый конец и видим следующие:

Safedisc Nanomites

Этих значений нет в начальной таблице. По исследовав опять процедуру по адресу 6673BCE8. Я нашёл следующие место в программе, откуда идёт запись:

Safedisc Nanomites

Пройдя CALL - 6673BF10, где программа и записывает не нужный для нас код. Зайдём в него:

Safedisc Nanomites

Здесь нет, не чего интересного, поэтому двигаемся дальше в CALL по адресу 6678700E:

Safedisc Nanomites

Здесь запись происходит в CALL 66705A3D и если мы переправим условный переход 66705A27 c JNZ на JMP, то у нас всё должно быть нормально. Перезапускаем программу, вместо восстановления первоначальных байтов на OEP впишем CC CC. Чтобы снять впоследствии дамп на OEP. Как остановимся в OllyDbg на выходе WaitForDebugEvent. Патчим отладчик и запускаем его на выполнение.


ВЫВОД - Чтобы восстановить наномиты мы должны сделать следующие:


  1. Вместо WaitForDebugEvent вписать наш патч.

  2. Занопить вызов ContinueDebugEvent со всеми PUSH и вписав вместо них - MOV EAX, 1.

  3. Исправить переход, отвечающий за запись наномита со второго раза - 6673С6F6.

  4. Переправить условные переходы, отвечающие за кэширование адресов - 6673B603, 6673B60A, 66705A27, 6673B9C6.

После завершения работы кода, проверяем в SoftIce и видим, что все наномиты восстановилось. Восстанавливаем первые два байта на OEP CC CC - 6A 74. И дампим в PETools или любой другой программой.




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



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


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