Введение в реверсинг с нуля используя IDA PRO. Часть 7

Дата публикации 15 май 2017 | Редактировалось 1 июн 2017
ИНСТРУКЦИИ УПРАВЛЕНИЯ ПОТОКОМ ВЫПОЛНЕНИЯ ПРОГРАММЫ

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

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

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

JMP A

A будет адресом памяти, куда мы хотим, чтобы программа совершила безусловный переход.

1.png

JMP SHORT - это короткий переход, который состоит их двух байт и он имеет возможность переходить вперед и назад, направление задаётся значением второго байта, первый байт это ОПКОД перехода, мы не можем переходить очень далеко.

2.png

Если мы установим в IDA опцию, чтобы видеть байты, из которых состоят инструкции, мы увидим опкод 0xEB, который соответствует инструкции JMP и которая будет совершать переход на 5 байт вперед от места, где заканчивается инструкция, адрес назначения может быть рассчитан следующим образом.

3.png

Начальный адрес инструкции + 2, что является количеством байт, которые составляют инструкцию и затем добавляется число 5, что является вторым байтом.

Очевидно, что переход вперёд и назад с использованием одного лишь байта не даст нам большой диапазон для перехода, максимальный положительный переход вперёд будет равен 0x7F байт, мы увидим такой пример.

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

3.png
4.png

Здесь нас спросят имя.

В VIEW->DATABASE SNAPSHOT MANAGER

5.png

Это позволит нам увидеть список всех снимков и дату, когда они были сделаны и с помощью кнопки RESTORE, мы можем вернуться к состоянию, которое мы хотим, которое мы сохранили ранее.

Давайте посмотрим, что произойдёт, если поменять байт 0x05 на 0x7F.

6.png

Используя PATCH BYTE в IDA, я меняю байт 0x05 на 0x7F.

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

7.png

8.png

Мы видим, что всё нормально и этот переход ведёт к адресу 0x4013A5 вперёд, давайте посмотрим, что произойдёт, если мы изменим байт 0x7F на 0x80.

Вернёмся в графический режим с помощью пробела и изменим байт на 0x80.

9.png

Мы видим, что сейчас мы делаем самый большой переход назад, здесь изменяя значение 0x7F на +1, что является самым большим переходом вперёд, мы изменили его на 0x80, что является теперь самым большим переходом назад.

10.png

В этом случае, когда мы возвращаемся назад, чтобы подогнать всё под формулу и только для математических целей, потому что PYTHON не знает, что переходы могут идти вперед или назад от инструкции, мы должны записывать -0x80 в его 16-ное значение, в DWORD, что равно значению 0xFFFFFF80, а затем, как мы видели при выполнении инструкции, применить логическое AND 0xFFFFFFFF к результату, этим мы очищаем все биты, большие, чем необходимо для 32-битного числа, и у нас получается тот же самый адрес 0x4012A6.

Если я использую 0xFF в качестве второго байта, это будет минимальный переход, который равен -1 в 16-том представлении. Я добавляю к нему 0xFFFFFFFF, чтобы соответствовать нужной разрядности. Помните, что мы всегда добавляем размер инструкции, в данном случае, два байта. Это позволит нам совершить переход назад, потому что 2 байта, которые мы добавили, заставляют считать это как начало вычисления. Инструкция будет переходить по адресу 0x401325.

Если мы продолжим переходить назад ещё на одну позицию, то вторым байтом будет 0xFE, что означает переход назад на -2 байта от конца инструкции, в формуле это будет добавление значения 0xFFFFFFFE.

11.png

Этот переход к той же самой начальной инструкции, которая именуется БЕСКОНЕЧНЫМ ЦИКЛОМ, она всегда возвращается к повторению и Вы не можете вырваться из него.

И так далее, переход на -3 где заканчивается инструкция перехода назад, будет равен 0xFD и он совершит переход по адресу 0x401323.

12.png

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

13.png

Здесь, мы видим пару длинных переходов, помните, что loc_ говорит нам, что эта инструкция является локальной(любой, основной) инструкцией.

14.png

Здесь мы видим длинный переход, расстояние между адресами 0x4026AE и 0x4029B3 намного больше, чем мы можем достичь с помощью короткого перехода.

15.png

Здесь мы видим, что расстояние вычисляется с помощью следующей формулы: конечный адрес - начальный адрес – 5. 5 - это размер инструкции. Это даёт мне результат 0x300, что является DWORD и он стоит рядом с опкодом длинного перехода 0xE9.

16.png

17.png

Если с помощью KEYPATCH я изменяю адрес назначения этого перехода на адрес перехода назад, например на адрес 0x400000, мы видим ...

18.png

Хотя он помечен мной в красный, потому что он не является действительным адресом в данный момент, Я буду видеть, смогу ли я сделать формулу в PYTHON, чтобы сделать переход назад.

19.png

Это даёт мне расстояние равное -0x26B3, используя ту же самую предыдущую формулу.

20.png

Эти байты в 16-ном представлении равны 0xFFFFD94D, которые стоят рядом с опкодом 0xE9, но только инвертированы справа налево.

21.png

УСЛОВНЫЕ ПЕРЕХОДЫ

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

Давайте возьмём например инструкцию CMP A, B.

Я могу сделать, чтобы программа сравнила A и B и в соответствии с отношениями между ними, программа сделает что-то, если не сделает что-то другое.

Так что, обычно, после сравнения, которое изменяет важные ФЛАГИ, в зависимости от их состояния, инструкция условного перехода решит что делать.

22.png

Здесь мы видим, пример условного перехода JZ, он также совершает переход, если флаг Z или нуль активирован. Это происходит, когда в предыдущем примере CMP EAX и EBX равны, так как CMP похожа на инструкцию SUB, но она без сохранения результата.

CMP вычитает оба регистра и если они равны, результатом будет 0, что активирует ФЛАГ Z или нуль, который проверяет переход JZ, чтобы совершить переход, если ФЛАГ Z активирован, программа переходит к зелёной стрелке и если нет, то переходит к красной стрелке, если они разные.

При использовании отладчика, мы будем видеть несколько примеров срабатывания флагов. К настоящему моменту важно знать, что если есть сравнение после него, вы можете увидеть эти разные переходы.

23.png

Хорошо, за исключением JMP и NOP, которые не принадлежат таблице, остальные - это условные переходы, которые оценивают различные состояния сравнения, если первый аргумент из них больше, или если больше или равен, или если меньше и т.д., есть несколько возможностей работы с ними, которые мы увидим позже более подробно, когда будем рассматривать ОТЛАДЧИК.

ИНСТРУКЦИИ CALL И RET

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

24.png

Здесь мы видим пример инструкции CALL, который переходит по адресу 0x4013D8 для выполнения этой функция (мы видим приставку sub_ перед адресом 0x4013D8, которая говорит нам об этом).

Инструкция CALL сохраняет на ВЕРШИНЕ стэка значение, куда будет возвращаться выполнение или адрес возврата, в нашем случае это адрес 0x40123D, я могу войти во внутрь этой функции просто нажав ENTER на инструкции CALL.

25.png

После того, как эта функция завершится, она также достигнет инструкции RET, который берёт адрес возврата сохранённый в стэке,а именно адрес 0x40123D и перейдёт туда, продолжая выполнение после CALL.

26.png

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

До встрече в 8-ой части.

Рикардо Нарваха

8 1.197
yashechka

yashechka
Ростовский фанат Нарвахи
Команда форума

Регистрация:
2 янв 2012
Публикаций:
25

Комментарии


      1. virtuha 5 июн 2017
        yashechka,
        Спасибо, что работаешь. Дай бог тебе здоровья и не угаснуть твоему энтузиазму :good:

        yashechka 15 май 2017
        Как слышно? Приём!!! Или никто не видит?

        Мы слышим и видим. Просто пока все ясно и нет слов, ты молодец. Твои труды не напрасны - знай это!​
        yashechka нравится это.
      2. yashechka 4 июн 2017
        О,Классно, Спасибо за лайки, за них и работаю))
      3. yashechka 29 май 2017
        Готово.
      4. yashechka 17 май 2017
        Спасибо, немного занят, завтра продолжу.
      5. Thetrik 15 май 2017
        Как я понял:
        ... мы должны записывать -0х80 в его hex значение (0xffffff80 в DWORD) и затем, как мы видели, делая AND 0xffffffff с результатом, мы оставляем младшие 32 бита и получаем тот же самый адрес 0x401A6.
        yashechka нравится это.
      6. yashechka 15 май 2017
        Как слышно? Приём!!! Или никто не видит?
      7. yashechka 15 май 2017
        Большие предложение очень сложно переводить, с учётом того, что Нарваха не ставит точек и запятых. Давайте с Инглиша.

        In this case, as we go backwards, to fit everything with the formula and just for
        math purposes because Python doesn’t know that jumps can go forward or
        backwards from a value, we must write -0x80 to its hexa value in dword that is
        0xFFFFFF80 and then, as we saw when doing AND 0xFFFFFFFF of the result
        we clean all bits bigger than the necessary for a 32-bit number and we have the
        same address 0x4012A6.

        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        Перевел так, но нужно более точно

        В этом случае, когда мы возвращаемся назад, чтобы подогнать всё под формулу и только для математических целей, потому что PYTHON не знает, что переходы могут идти вперед или назад от инструкции, мы должны записывать -0x80 в его 16-ное значение, в DWORD, что равно 0xFFFFFF80, а затем, как мы видели при выполнении инструкции, сделать AND 0xFFFFFFFF результата, этим мы очищаем все биты, большие, чем необходимо для 32-битного числа, и у нас получается тот же самый адрес 0x4012A6.

        :::::::::::::::::::::::::::::::::::
        Жду помощи!!!
      8. yashechka 15 май 2017
        Народ, сейчас буду добавлять статью, кто на связи? Нужна небольшая помощь по переводу?