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

  • На форуме работает ручное одобрение пользователей. Это значит, что, если Ваша причина регистрации не соответствует тематике форума, а также Вы используете временную почту, Ваша учётная запись будет отклонена без возможности повторной регистрации. В дальнейшем - пожизненная блокировка обоих аккаунтов за создание мультиаккаунта.
  • Мы обновили Tor зеркало до v3!
    Для входа используйте следующий url: darkv3nw2...bzad.onion/
  • Мы вновь вернули telegram чат форуму, вступайте, общайтесь, задавайте любые вопросы как администрации, так и пользователям!
    Ссылка: https://t.me/chat_dark_time

AnGel

Администратор
Команда форума

AnGel

Администратор
Команда форума
27 Авг 2015
3,411
2,025
CALL и RET

Я намеренно оставил эти 2 инструкции напоследок. На данном этапе у Вас уже достаточно базовых знаний, чтобы вплотную подойти к изучению CALL и RET.

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

Итак, давайте снова загрузим крэкми CrueHead'а в OllyDbg.

Правая кнопка мыши на любой строке листинга - "Go to" - Expression".

ca63f00bbdf390b1280cce75a25d154e.png

Во всплывшем диалоге вводим 401245

7e7d07e23efb905ab56ab3242c6ca7ca.png

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

89961f7c307719e14fba67ddacd1c1d1.png

Чтобы выполнить этот CALL, подсвечиваю строчку - правая кнопка мыши - "New origin here". Теперь значение EIP указывает на 401245, а это значит, что следующей инструкцией выполнится наш CALL.

91b3aa4276870a4d2108d6f8a564bf10.png

Тут можем проследить как изменяется значение EIP.

c866677084463aeca22557125fc85604.png

Вернёмся же к нашему CALL.

542531a50627a01549af0b12573f2b92.png

Инструкция CALL передаёт управление заданной процедуре (или просто подпрограмме), адрес которой указан в операнде. Например:

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

В данном случае, после завершения процедуры 401362, управление вернётся на адрес 40124A.

Когда мы имеем дело с инструкцией CALL, OllyDbg предоставляет несколько полезных механизмов трассирования. Если нам интересно продолжить трассировку по внутренностям данной процедуры, можно "нырнуть" в неё клавишей F7. Если же нам хочется просто глянуть на содержимое процедуры, чтобы далее принять решение трассировать её или нет, можно воспользоваться следующей опцией: правая кнопка мыши - "Follow". Наконец, если мы не хотим трассировать данную процедуру, т.к. её содержимое не сулит ничего интересного, можем выполнить её на одном дыхании клавишей F8 и продолжить трассировку по адресу следующей за CALL инструкции.

Итак, чтобы посмотреть содержимое процедуры, не переводя на неё управление, подсвечиваем CALL - правая кнопка мыши - "Follow":

0c9d7e00be77e716a1b539a664a967a0.png

Как видите, значение EIP по-прежнему 401245, т.к. FOLLOW просто подгружает в дизассемблер код по заданному адресу и ждёт дальнейших распоряжений, но ничего при этом не выполняет.

ad4e0003310a3d75d8c8c667c051d50e.png

Итак, перед нами код процедуры, которая, естественно, начинается по адресу 401362, в соответствии со значением операнда CALL. Где же конец процедуры? - В данном случае конец приходится на следующую инструкцию RET, которая находится чуть ниже. OllyDbg использует мнемонику RETN вместо RET, что одно и тоже. Эта инструкция завершает процедуру, возвращая управление на адрес 40124A, где находится следующая за CALL инструкция.

Мы теперь знаем как подсмотреть код процедуры не переводя на неё управление. Вернёмся же назад - для этого достаточно нажать на клавишу (-), т.е. "минус". Эта клавиша всегда возвращает нас на один шаг назад в цепочке FOLLOW, ничего при этом не выполняя.

Вот мы снова видим изначальный CALL:

a159b9f217761cc59ad7a9012587bc11.png

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

b46aba5aed6fc0ab44393081cc799ce3.png

Предыдущая картинка отображает содержимое стека на моей машине. Возможно, у Вас будут другие значения, но это несущественно.

Нажимаю на F7:

31749aea95a92a12219185125b9121b9.png

"Погружаемся" в процедуру и видим, что на этот раз, в отличие от предыдущих манипуляций с FOLLOW, значение EIP изменилось - теперь оно равно 401362 и это значит, что мы действительно начали выполнять код процедуры.

Давайте обратим внимание на стек

e65fad34fafa88fa63da6039868c3160.png

На предыдущей картинке я выделил жёлтым состояние стека до входа в процедуру. Как видите, в стеке появилась новая ячейка, т.е. при выполнении инструкции CALL в стек добавляется адрес возврата, чтобы RET знала, куда вернуть управление при завершении процедуры.

Значение этой новой ячейки - 40124A - соответствует адресу следующей после CALL инструкции. Если Вы вдруг успели забыть об этом, следующая картинка освежит вашу память:

86f3b03154f8253abb191268cc6f35cc.png

Причём Olly заботливо добавляет к этой ячейке некоторую дополнительную информацию.

28459a6a7e6f7501c362ff589916bf14.png

Сообщает нам, что это адрес возврата из 401262 в 40124A.

Т.е. Olly ещё не в курсе, где находится RET, но точно знает, что процедура начинается по адресу 401362 и заканчивается по какому-нибудь адресу, где вероятнее всего находится инструкция RET, которая вернёт управление на адрес 40124A.

Давайте ещё раз нажмём на F7, чтобы выполнить PUSH 0, и посмотрим, как этот ноль добавится в стек, сместив вниз наш адрес возврата.

f0a6125346225bf00a32ec0c8a5d6df5.png

Процедура может содержать тысячи стековых операций (PUSH, POP и др.), добавляя и удаляя из стека различные значения, но при завершении процедуры в верхушке стека должен снова оказаться адрес возврата. Давайте продолжим выполнять процедуру в пошаговом режиме, нажимая F8, чтобы не трассировать код внутри процедур, пока не доберёмся до инструкции RET.

b67e72dded5b75072c8f3ad1781be527.png

Вот мы и добрались до завершающей инструкции RET, и в верхушке стека снова находится адрес возврата.

4908df6ca58beb040c9aa783f2b5b9c5.png

Таким образом, можно заключить, что RET обычно является прямой противоположностью CALL, т.е., если CALL вызывает процедуру, RET возвращает управление обратно. RET также удаляет за ненадобностью из стека адрес возврата.

Жмём F7.

120178045fcdbbdf05305ae2eb58aedf.png

и возвращаемся к адресу 40124A. При этом состояние стека становится таким, каким оно было до вызова процедуры.

5aa514f74946444ab148a69a90d63df4.png

Стоит добавить, что RET можно использовать не только для завершения текущего CALL, например:

PUSH 401256
RET
Сначала PUSH затолкнёт в стек значение 401256. Далее, следующий за ним RET воспримет это значение как адрес возврата из текущего CALL, хотя это вовсе не так, и передаст управление на адрес 401256. Таким образом, этот код отработает точно также как JMP 401256.

Далее мы рассмотрим ещё один пример использования CALL/RET. Перезагружаем крэкми CrueHead'а. "Go to" - "Expression" - вводим 401364.

9480d5b5f2d11c2443898531e7734400.png

По этому адресу нажимаем F2, устанавливая таким образом точку останова (об этом мы подробно поговорим позже). Olly прервёт исполнение как только начнёт выполняться инструкция по адресу, на который установлена точка останова.

98b4c4485443e0e80058b3d717fc2d87.png

Адрес выделяется красным цветом и это значит, что точка останова (BREAKPOINT) установлена. Нажимаем на F9 (RUN), чтобы приложение запустилось...

e8c2757ad95c453455fda02479e0258d.png

Появилось окно крэкми. Если Вы его не видите, поищите хорошенько через Alt+Tab :)

Приложение ещё не выполнило код, на который мы поставили точку останова. В окне крэкми вызовите меню и выберите опцию "Help" - "Register".

6117c41c70662a75b0d9093b6cd98609.png

Появилось окно, через которое нужно ввести имя и серийник.

e21fdfb2aca3ec2850284b6ffa48f942.png

Пишем что угодно.

db379af2ae7bc18636b40167d2195e22.png

Нажимаем OK.

5bff51ec3b7bf12aa8f33e8ba176f642.png

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

efefcec41a13e256bd63deef09df58a5.png

Мы находимся прямо посреди выполнения кода программы, но кое-какую базовую информацию можем почерпнуть например из стека:

aec0ea327b680f615e0f5fac25282b71.png

Видим сразу несколько RETURN TO... Видимо, мы находимся внутри процедуры и самый верхний RETURN TO содержит адрес возврата, на который передаст управление ближайший RET. Этот адрес, вероятно, окажется на верхушке стека, перед выполнением RET и управление будет передано на адрес 40124A.

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

Жмём на F8, чтобы добраться до RET, как и раньше. Только в этот раз при выполнении последнего CALL сразу перед RET должно появиться сообщение, которое нужно закрыть, чтобы продолжить трассирование.

b30beb605b69e797322b3128a94f0452.png

Закрываем его.

fb16d641a78b87b01bb57f08f1d73961.png

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

17180d47c267dc6ccbf5fae4dc95570c.png

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

161d46cb1d154d60526e3cabb10c783c.png

7968f888f9f59ff89cad8fe7f7eac70f.png

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

Рассмотрим ещё один закрепляющий пример. Для этого нужно перезагрузить крэкми в отладчике, сразу нажать на клавишу пробела и ввести следующую инструкцию: CALL 401245.

3f12acfa065f30f0322694bb69941d49.png

a4aa8ad17737209a5aa72a92896c8052.png

Готово. Далее воспользуемся опцией FOLLOW, чтобы попасть во внутрь функции.

291011889772d228da68b49d393fa3c2.png

Процедура начинается по адресу 401245 и заканчивается по адресу 401288 (где находится инструкция RETN 10, которая немного отличается от знакомого нам уже RET, но об этом позже). Обратите внимание на вложенный CALL (первая инструкция процедуры является вызовом другой процедуры).

Нажмите МИНУС, чтобы выйти из FOLLOW. Теперь нажмите F7, чтобы войти в процедуру, фактически передав на неё управление.

fba406f6dcdcf4381a3f7335d2039893.png

Вот мы и внутри, о чём свидетельствует значение EIP: оно указывает на адрес 401245.

416e3e103948ff8fe3ec1178d13a5fe0.png

В верхушке стека хранится адрес возврата к следующей за нашим свежевписанным CALL инструкции.

634d1148ee2c62eee2a8e34adb4cd5c8.png

Адрес возврата имеется, но OllyDbg не выделил его как RETURN TO 401005. Чтобы понять почему он в этот раз не проявил сообразительность, нужно принять во внимание, что мы вписали CALL после того, как OllyDbg выполнил предварительный анализ кода. Представьте себе, что OllyDbg - это Чапаев, пересекающий реку верхом :) Прямо посреди реки мы модифицируем его скакуна, чем, мягко говоря, дезориентируем Василия Ивановича. Точно также в растерянность впадает и OllyDbg. (В общем, OllyDbg в воде не тонет, но пословица тут не при чём - прим. переводчика ;-) )

Чтоб исправить ситуацию, достаточно нажать на правую кнопку мыши в любом месте окна дизассемблера - "Analysis" - "Analyse code". Таким образом, отладчик снова пробежится по коду анализатором и скорректирует подсказки в стековом кадре.

bbbcd5fbc56346c26aef0ac253866532.png

Теперь адрес возврата в верхушке стека корректно распознан анализатором:

0a62e31e532fe361c1f8720e86a5deb5.png

Адрес возврата в данном случае обозначен относительно стартовой точки программы: MODULE ENTRY POINT + 5 = 401000 + 5 = 4010005.

В общем, не забывайте запускать анализатор после внесения изменений в код программы (это касается также изменений кода самой программой). Иногда анализ кода может оказаться ошибочным и его следует сбросить опцией "Analysis" - "Remove analysis from module".

Давайте вернёмся к нашему примеру.

cb6c8b0ad2e06226f978d044137ba397.png

Жмём F7 и "погружаемся" в следующий CALL.

fc3569203e605f55614a71a71a62cadb.png

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

Если Вы попадёте в подобную ситуацию, например, в результате срабатывания точки останова, не имея представления о ходе выполнения программы до останова, взглянув на стек можете сориентироваться:

Ниже в окне дизассемблера виден RET, что наталкивает на мысль о том, что мы находимся внутри процедуры.

ed32236cc650c95bb7e4b3b49d8232aa.png

Смотрим в стек, чтобы удостовериться:

62d5b29b91fb3a2d906943e5dbe87445.png

Сверху вниз первый RETURN TO указывает на возможный адрес возврата из текущей процедуры. В данном конкретном случае - это адрес 40124A.

0ba3dd872a2441a3878a19cd4eb124b1.png

К тому же, ниже есть ещё один RETURN TO, а это может значить, что текущая процедура была вызвана из другой процедуры.

89511ce5467712bb5f7c50621fc54136.png

Второй RETURN TO говорит нам, что в конечном итоге программа должна вернуться к адресу 401005 и непосредственно над этим адресом находится CALL, который инициировал всю эту цепочку вызовов. Кстати, вот он:

4cf1e50f577aa7c80ca44ed9d628cd7e.png

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

Если что-то непонятно - спрашивайте. До следующей главы!
 

О нас

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

    Dark-Time 2015 - 2022

    При поддержке: XenForo.Info

Быстрая навигация

Меню пользователя