> For the complete documentation index, see [llms.txt](https://yutewiyof.gitbook.io/intro-rev-ida-pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yutewiyof.gitbook.io/intro-rev-ida-pro/chast-07.md).

# Часть 7

## **ИНСТРУКЦИИ УПРАВЛЕНИЯ ПОТОКОМ ВЫПОЛНЕНИЯ ПРОГРАММЫ**

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

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

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

**JMP A**

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

![](/files/-Ljtu5CtPH9Dkbq__6Ru)

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

![](/files/-Ljp8u9EDDZY8z00GPGV)

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

![](/files/-LjpMRtnnWUGSe6yFEoI)

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

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

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

![](/files/-LjpMRtpTQIbTFDdr0ga)

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

В **VIEW->DATABASE SNAPSHOT MANAGER**

![](/files/-Ljtu5EZmtYozaR22wCp)

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

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

![](/files/-Ljtu5Eu5ApiS0DrXJO5)

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

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

![](/files/-Ljtu5FE2wpV_VlEvSp2)

![](/files/-LjpMRu-eAVjN6KD7_v0)

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

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

![](/files/-LjpMRu1DNhExQdPzogQ)

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

![](/files/-Ljp8u9aoVpkeZFHva9L)

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

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

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

![](/files/-LjpMRu59rHmOvX8Qrb4)

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

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

![](/files/-Ljp8u9eNF37psDyrqzy)

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

![](/files/-LjpMRu9Ou-bxfUa6Gwu)

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

![](/files/-LjpMRuBxqgYd4U6y2-a)

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

![](/files/-Ljtu5HFZEt4FKQt1Sci)

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

![](/files/-Ljtu5HbOLI578uVilgW)

![](/files/-Ljtu5Htgu9Uvs5HD26c)

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

![](/files/-Ljp8u9qtrIemLdqfdmW)

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

![](/files/-LjpMRuLwLZsqUDPMcf_)

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

![](/files/-Ljtu5IVRBN4oMqHxCZp)

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

![](/files/-LjpMRuRR3MFJ8RI07sJ)

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

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

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

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

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

![](/files/-LjpMRuTs80OXEhe91H9)

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

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

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

![](/files/-LjpMRuYdvC8Dali0crq)

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

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

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

![](/files/-Ljtu5Jd3JMW6pP3ttMs)

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

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

![](/files/-LjpMRubKt--NpPdaqv7)

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

![](/files/-Ljtu5K1CRdFtGsE2IBH)

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

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

Автор оригинального текста — Рикардо Нарваха.

Перевод и адаптация на английский язык — IvinsonCLS.

Перевод и адаптация на русский язык — Яша Яшечкин.

Перевод специально для форума системного и низкоуровневого программирования - WASM.IN

[**Источник: ricardonarvaja.info**](http://ricardonarvaja.info/WEB/IDA%20DESDE%20CERO/CURSO%20DE%20IDA%20TUTES/7-INTRODUCCION%20AL%20REVERSING%20CON%20IDA%20PRO%20DESDE%20CERO.docx)
