Введение в крэкинг с нуля, используя OllyDbg - Глава 53

Дата публикации 22 окт 2010

Введение в крэкинг с нуля, используя OllyDbg - Глава 53 — Архив WASM.RU



Хорошо, победитель обоих частей конкурса из прошлой части - это Ularteck, который прислал оба скрипта и объяснение первой части в виде туториала, который мы используем здесь. Вот скрпит-победитель первой часчти, который предназначается для починки украденных байтов. Объяснение идёт ниже.

Код (Text):
  1.  
  2. /*
  3. ###############################################################################################################
  4.  
  5.                                              CracksLatinoS - 2006
  6.  
  7.     Скрипт создан: Ulaterck.
  8.  
  9.     Описание: Скрипт создан для курса «Введение в крэкинг с нуля, используя OllyDbg» Рикардо Нарвахи, главы 52-ой. Он предназначается для первой части: ищет OEP и чинит украденные байты.
  10.  
  11.     Цель: UnPackMe_TPPpack.exe
  12.     Требования: ODBGScript 1.48 , HideDebugger 1.24 , HideOD,  остановленные на точке входа,  снять все случаи исключений меньше KERNEL32, так как используемый метод – это метод исключений Рикардо.
  13.  
  14.                 - Перед запуском скрпита отметьте последнее найденное исключение, так как скрипт этого ожидает.
  15.  
  16. В любом случае смотрите объяснение, которое находится ниже.
  17.     ###############################################################################################################
  18. */
  19.  
  20. var dir_excep
  21. var Newoep
  22. var dir_JMP
  23.  
  24. var dir_CALL
  25. var oep
  26. var StartScan
  27. var Opcodes  
  28. var temp
  29. var temp2
  30. var temp3
  31.  
  32. Datos:
  33. mov Newoep, eip             // Сохраняем адрес точки входа
  34.                         // Отмечаем последний адрес скрипта                   
  35. ask "Введите последнее исключение"    //  Выводить окно для ввода данных.
  36. cmp $RESULT,0   // Сравниваем, был ли введён какой-либо адрес.
  37. je aviso                                 // Если нет, то прыгаем на метку aviso
  38. mov dir_excep, $RESULT  // Если ввели адрес сохраняем его в dir_excep.
  39. jmp Inicio          // Прыгаем на метку Inicio, чтобы продолжить.
  40.  
  41. aviso:
  42. msg "Выполняем скрипт заново – введите правильный адрес"
  43. jmp final
  44.  
  45. Inicio:
  46. run             // Запускаем программу
  47. eoe verifica            // Если происходит исключение, переходим на метку verifica.
  48.  
  49. verifica:
  50. cmp eip,dir_excep       // как только мы здесь из-за исключения, смотрим, последнее ли оно.
  51. je ultima           // Если да, то переходим на метку ultima.
  52. esto                // Если нет, то запускаем SHIFT + F9, чтобы пройти исключение.
  53. jmp Inicio:         // И переходи на метку Inicio для поиска других исключений
  54.  
  55. ultima:
  56. findop eip,#FFE0#       // Попав на последнее исключение, ищем переход  JMP EAX  на украденный код.
  57. mov dir_JMP,$RESULT // Как нашли – сохраняем адрес в dir_JMP
  58. bp dir_JMP          // Устанавливаем точку останов (F2) на переход JMP EAX.
  59. esto                // Проходим исключениес помощью SHIFT + F9, чтобы оказаться в точке останова
  60. bc dir_JMP          // Снимаем точку останова с перехода JMP EAX.
  61. sti             // Запускаем F7, чтобы оказаться в украденном коде.
  62.  
  63. mov oep,eip         // Сохраняем адрес украденного OEP.
  64. mov StartScan,eip       // Сохраняем адрес OEP также в StartScan, который используется для поиска Call'ов.
  65.  
  66. BuscarCall:         // Также начинаем искать прямые вызовы, чтобы починить их, когда будет создаваться двоичная копия, и прикрепить их к новому OEP.
  67.                 // Посмотрите MIniTuto.PARTE1.doc
  68.  
  69.  findop StartScan,#E8#  // Ищем прямые вызовы, которые начинаются с опкода E8
  70.  cmp $RESULT, 0     // Когда их больше нет, $RESULT равен 0 и переходим на    
  71.                 // метку final.
  72.  je final
  73.  mov dir_CALL, $RESULT  // Сохраняем адрес первого встреченного call (вызова).
  74.  mov StartScan, $RESULT // Задаём, откуда ходим искать вызовы, в данном случае – оттуда, где был встречен первый вызов.
  75.  add dir_CALL,1     // К адресу встреченного вызова прибавляем 1, чтобы миновать опкод E8.
  76.  mov Opcodes, [dir_CALL]    // Перемещаем опкоды из этого адреса в Opcodes.
  77.  add Opcodes,StartScan  // К этим опкодам прибавляем адрес вызова.
  78.  add Opcodes,5      // К результату прибавляем 5 и получаем адрес, который указывает на вызов.
  79.                 // Для пояснений смотрите MiniTuto.PARTE1.doc.
  80.  
  81.  
  82. // Теперь назначаем встреченному вызову новый опкод, чтобы при создании двоичного
  83. // файла и вставке нового OEP этот вызов был починен.
  84.  
  85. mov temp, StartScan        // Помещаем адрес, содержащий StartScan, во временную переменную. ( StartScan = адрес встреченного вызова.)
  86. sub temp, oep         // От адреса вызова на починку отнимаем адрес OEP украденного кода.   
  87. mov temp2, Newoep         // Перемещаем адрес нового OEP (EntryPoint) в вторую временную переменную.
  88. add temp,temp2        // К ней прибавляем значение первой.
  89. sub Opcodes, temp       // Теперь отнимаем результат от адреса, на который указывает CALL.
  90. sub Opcodes, 5      // И от того, что остаётся, отнимаем (в оригинале – прибавляем, но это не соответствует команде) 5, получая необходимые для починки этого вызова опкоды.
  91.  
  92. Editar:         // Начинаем редактировать вызов с помощью новых опкодов.
  93. mov temp3, StartScan    // Помещаем в третью временную переменную адрес вызова для починки, содержащийся в StartScan.
  94. add temp3,1         // К адресу вызова прибавляем 1, чтобы не учитывать опкод E8.
  95. mov [temp3], Opcodes    // Чиним вызов новыми опкодами.
  96. jmp BuscarCall
  97.  
  98. final:
  99. ret

Теперь продолжаем объяснение, как всё это работает, написанное 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.

Y al momento termina cuando está en la ultima excepción. Presionamos ALT + M

И когда оказываемся в этом исключении – выполнение останавливается.

Нажимаем ALT + M.

И вместо установки BP на доступ к секции кода, делаем это на секцию где бы выполненный код. Нажимаем SHIFT + F9 и оказываемся в украденном коде.

Si presionamos la tecla (-) vemos que nos sitúa en la ultima excepción

Если нажмём клавишу «-», то увидим, что находимся в последнем исключении.

И ниже находится переход, о котором нам говорил 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

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

PUNTERO - DIRECCIÓN_NUEVA – 5 = NUEVOS OPCODES

УКАЗАТЕЛЬ – НОВЫЙ_УКАЗАТЕЛЬ – 5 = НОВЫЕ ОПКОДЫ ----------------------------------------------------------------------------------------------------

DIRECCIÓN _NUEVA: рассчитываем таким образом.

Например:

Если скопируем первые строки украденного кода и скопируем их в точку входа.

Видим, что первый прямой вызов будет находится по этому адресу в точке входа 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.

Ок, на этом можно завершить объяснение скрипта из первой части и перейти к скрипту из второй.

*/

Код (Text):
  1.  
  2. var base
  3. var dir_VirtualAlloc
  4. var dir_VirtualProtect
  5. VAR dir_mov
  6. Inicio:
  7.  
  8. gpa "VirtualAlloc", "kernel32.dll"    // Buscamos la dirección de la Funcion VirtualAlloc
  9. mov dir_VirtualAlloc, $RESULT
  10. log dir_VirtualAlloc
  11.  
  12. gpa "VirtualProtect", "kernel32.dll"
  13. mov dir_VirtualProtect, $RESULT
  14.  
  15. bp dir_VirtualAlloc
  16. run
  17. eob info
  18.  
  19. info:
  20.  
  21. mov base, eax
  22. log base
  23. bc dir_VirtualAlloc
  24. bp dir_VirtualProtect
  25.  
  26. Zona:
  27. eob seccion
  28. run
  29.  
  30. seccion:
  31. cmp esi, 00460000
  32. je retornar
  33. jmp Zona
  34.  
  35. retornar:
  36. bc dir_VirtualProtect
  37. mov Reg_esp, [esp]
  38. bp Reg_esp
  39.  
  40. eob zona_1
  41. run
  42.  
  43. zona_1:
  44. bc Reg_esp
  45. find base, #897C24188B4424#
  46. mov dir_mov, $RESULT
  47. log dir_mov
  48. jmp nopear
  49.  
  50. nopear:
  51. bp dir_mov
  52. eob nopear2
  53. run
  54. nopear2:
  55. bc dir_mov
  56. fill dir_mov, 4, 90
  57.  
  58. final:
  59.  
  60. msg "Mov nopeado, al terminar el script presiona run (F9)."
  61.  
  62. ret

Метод для починки 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ить. Этот адрес может быть разным на разных машинах, поэтому его необходимо искать.

Код (Text):
  1.  
  2. var base
  3. var dir_VirtualAlloc
  4. var dir_VirtualProtect
  5. VAR dir_mov
  6. Inicio:
  7.  
  8. gpa "VirtualAlloc", "kernel32.dll"    // Buscamos la dirección de la Funcion VirtualAlloc
  9. mov dir_VirtualAlloc, $RESULT
  10. log dir_VirtualAlloc
Здесь сохраняем адрес вышеуказанной функции в переменную var dir_VirtualAlloc, потом делаем то же самое с функцией VirtualProtect.

Код (Text):
  1.  
  2. gpa "VirtualProtect", "kernel32.dll"
  3. mov dir_VirtualProtect, $RESULT

В переменной var dir_VirtualProtect сохраняем адрес соответствующей API-функции.

Код (Text):
  1.  
  2. bp dir_VirtualAlloc
  3. run
  4. eob info
  5.  
  6. info:
  7.  
  8. mov base, eax
  9. log base
  10. bc dir_VirtualAlloc
  11. bp dir_VirtualProtect

Затем нужно установить BP на VirtuallAlloc, делаем RUN и с помощью eob останавливаемся на указанном bp, сохраняем в переменной base адрес, где программа собирается создать область, в которой будет находится адрес для NOPинга.

Также устанавливаем BP на API-функции VirtualProtect.

Код (Text):
  1.  
  2. Zona:
  3. eob seccion
  4. run
  5.  
  6. seccion:
  7. cmp esi, 00460000
  8. je retornar
  9. jmp Zona
Когда останавливаемся на BP, сравниваем равен ли ESI 460000, что является началом IAT, так как VirtualProtect использует его как аргумент функции для защиты указанной зоны и стирает BPMы, которые там находится, и если равна, то идём в метку retornar.

Код (Text):
  1.  
  2. retornar:
  3. bc dir_VirtualProtect
  4. mov Reg_esp, [esp]
  5. bp Reg_esp

Где стирается BP на функции VirtualProtect, ищется значение, что будет находится в [esp] и устанавливается BP, чтобы остановиться, как только выйдем из API-функции.

Код (Text):
  1.  
  2. zona_1:
  3. bc Reg_esp
  4. find base, #897C24188B4424#
  5. mov dir_mov, $RESULT
  6. log dir_mov
  7. jmp nopear

Когда возвращаемся из API-функции, то уже можно искать инструкцию, которую будем NOPить. Искать надо по достаточно большой последовательности байтов, чтобы не встретить левые инструкции: 897C24188B4424.

После того, как нашли, делаем переход на процедуру заNOPления.

Код (Text):
  1.  
  2. nopear:
  3. bp dir_mov
  4. eob nopear2
  5. run
  6. nopear2:
  7. bc dir_mov
  8. fill dir_mov, 4, 90
  9.  
  10. final:
  11.  
  12. msg "Mov nopeado, al terminar el script presiona run (F9)."
  13.  
  14. ret

Вот и всё объяснение и спасибо Ularteck'у за хорошую работу и marciano за его прекрасный туториал, из которого мы взяли изображения для объяснения 2-ой части скрипта.

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

В 54-ой части мы начнём новую тему.

© Рикардо Нарваха, пер. Aquila


1 5.358
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532

Комментарии


      1. virtuha 23 май 2017
        Оглавление
        Введение в крэкинг с нуля, используя OllyDbg - Глава 1
        Введение в крэкинг с нуля, используя OllyDbg - Глава 2
        Введение в крэкинг с нуля, используя OllyDbg - Глава 3
        Введение в крэкинг с нуля, используя OllyDbg - Глава 4
        Введение в крэкинг с нуля, используя OllyDbg - Глава 5
        Введение в крэкинг с нуля, используя OllyDbg - Глава 6
        Введение в крэкинг с нуля, используя OllyDbg - Глава 7
        Введение в крэкинг с нуля, используя OllyDbg - Глава 8
        Введение в крэкинг с нуля, используя OllyDbg - Глава 9
        Введение в крэкинг с нуля, используя OllyDbg - Глава 10
        Введение в крэкинг с нуля, используя OllyDbg - Глава 11
        Введение в крэкинг с нуля, используя OllyDbg - Глава 12
        Введение в крэкинг с нуля, используя OllyDbg - Глава 13
        Введение в крэкинг с нуля, используя OllyDbg - Глава 14
        Введение в крэкинг с нуля, используя OllyDbg - Глава 15
        Введение в крэкинг с нуля, используя OllyDbg - Глава 16
        Введение в крэкинг с нуля, используя OllyDbg - Глава 17
        Введение в крэкинг с нуля, используя OllyDbg - Глава 18
        Введение в крэкинг с нуля, используя OllyDbg - Глава 19
        Введение в крэкинг с нуля, используя OllyDbg - Глава 20
        Введение в крэкинг с нуля, используя OllyDbg - Глава 21
        Введение в крэкинг с нуля, используя OllyDbg - Глава 22
        Введение в крэкинг с нуля, используя OllyDbg - Глава 23
        Введение в крэкинг с нуля, используя OllyDbg - Глава 24
        Введение в крэкинг с нуля, используя OllyDbg - Глава 25
        Введение в крэкинг с нуля, используя OllyDbg - Глава 26
        Введение в крэкинг с нуля, используя OllyDbg - Глава 27
        Введение в крэкинг с нуля, используя OllyDbg - Глава 28
        Введение в крэкинг с нуля, используя OllyDbg - Глава 29
        Введение в крэкинг с нуля, используя OllyDbg - Глава 30
        Введение в крэкинг с нуля, используя OllyDbg - Глава 31
        Введение в крэкинг с нуля, используя OllyDbg - Глава 32
        Введение в крэкинг с нуля, используя OllyDbg - Глава 33
        Введение в крэкинг с нуля, используя OllyDbg - Глава 34
        Введение в крэкинг с нуля, используя OllyDbg - Глава 35
        Введение в крэкинг с нуля, используя OllyDbg - Глава 36
        Введение в крэкинг с нуля, используя OllyDbg - Глава 37
        Введение в крэкинг с нуля, используя OllyDbg - Глава 38
        Введение в крэкинг с нуля, используя OllyDbg - Глава 39
        Введение в крэкинг с нуля, используя OllyDbg - Глава 40
        Введение в крэкинг с нуля, используя OllyDbg - Глава 41
        Введение в крэкинг с нуля, используя OllyDbg - Глава 42
        Введение в крэкинг с нуля, используя OllyDbg - Глава 43
        Введение в крэкинг с нуля, используя OllyDbg - Глава 44
        Введение в крэкинг с нуля, используя OllyDbg - Глава 45
        Дополнение к 45-ой главе «Введения в крэкинг, используя OllyDbg»
        Введение в крэкинг с нуля, используя OllyDbg - Глава 46
        Введение в крэкинг с нуля, используя OllyDbg - Глава 47
        Введение в крэкинг с нуля, используя OllyDbg - Глава 48
        Введение в крэкинг с нуля, используя OllyDbg - Глава 49
        Введение в крэкинг с нуля, используя OllyDbg - Глава 50
        Введение в крэкинг с нуля, используя OllyDbg - Глава 51
        Введение в крэкинг с нуля, используя OllyDbg - Глава 52
        Введение в крэкинг с нуля, используя OllyDbg - Глава 53