Переходы. Инструкция jmp

Последнее обновление: 12.10.2023

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

Инструкции перехода указывают процессору изменить ход выполнения программы, установив для указателя инструкции значение, отличное от того, которое процессор собирался установить. Самая простая форма инструкций перехода — это инструкция JMP , которая выполняет безусловный переход и имеет следующие формы:

jmp метка
jmp регистр
jmp адрес_в_памяти

В первом варианте после инструкции указывается метка, к которой надо перейти. Названия меток произвольные. В языках высокого уровня этой инструкции соответствует оператор goto. Например, возьмем слеудующую программу для Linux:

global _start

section .text
_start:

    mov rdi, 11         ; RDI = 11
    jmp exit            ; переходим к метке exit
    mov rdi, 22         ; не выполняется
exit:                   ; метка exit
    mov rax, 60         ; 60 - номер системного вызова exit
    syscall             ; выполняем системный вызов exit

Здесь выполнение программы начинается с копирования в регистр RDI числа 11. Затем инструкции безусловного перехода jmp осуществляет переход к метке exit

jmp exit

В итоге последующая инструкция mov, которая помещается в регистр RDI число 22, НЕ выполняется

mov rdi, 22

А выполняются инструкции, которые помещаются после метки exit. Для установки метки после ее имени указывается двоеточие:

exit:                   ; метка exit
    mov rax, 60         ; 60 - номер системного вызова exit
    syscall

То есть в итоге в регистре RDI будет число 11. Аналогичный пример для Windows:

global _start

section .text
_start:
    mov rax, 11         ; RAX = 11
    jmp exit            ; переходим к метке exit
    mov rax, 22         ; не выполняется
exit:                   ; метка exit
    mov rax, rdi     
    ret        

Переход к адресу в регистре

Другая форма инструкции jmp в качестве операнда принимает регистр и позволяет перейти к инструкции, адрес которой указан в 64-битном регистре общего назначения. Например, программа для Linux

global _start

section .text
_start:

    mov rbx, exit       ; в регистр RBX помещаем адрес метки exit
    mov rdi, 22         ; RDI = 12
    jmp rbx             ; переходим к адресу из регистра RBX
    mov rdi, 33         ; не выполняется
exit:                   ; метка exit
    mov rax, 60         ; 60 - номер системного вызова exit
    syscall             ; выполняем системный вызов exit

Здесь в регистр RBX загружается адрес метки exit. Поэтому когда инструкции jmp передается регистр RBX, то будет происходить переход к метке учше.

Аналогичный пример для Windows

global _start

section .text
_start:
    mov rbx, exit
    mov rax, 22         ; RAX = 22
    jmp rbx             ; переходим к адресу из регистра RBX
    mov rax, 33         ; не выполняется
exit:                   ; метка exit    
    ret        

Переход к адресу в адресу в памяти

Третий вариант безусловных переходов представляет переход к адресу, который хранится, например, в переменной. Подобная переменная должна представлять тип qword, четверичное слово, которое занимает 64 бит, так как 8 байт - это размер адреса в архитектуре x86-64. Например, перейдем в программе для Linux по адресу, который хранится в переменной:

global _start

section .text
_start:
    mov rdi, 23         ; RDI = 23
    jmp [exitPtr]       ; переходим к адресу из exitPtr
    mov rdi, 33         ; не выполняется
exit:                   ; метка exit
    mov rax, 60         ; 60 - номер системного вызова exit
    syscall             ; выполняем системный вызов exit  
exitPtr: dq exit      ; переменная exitPtr хранит адрес метки exit  

В самом конце программы определена переменная exitPtr. Причем она определена после самой последней инструкции, благодаря чему она не будет выполняться как инструкция. Она определена с помощью директивы dq, поэтому представляет тип qword/четверное слово. Причем эта переменная хранит адрес метки exit. Для теста в регистр RDI помещаем число 23. Затем выполняем непрямой переход по адресу, который хранится в переменной exitPtr:

jmp [exitPtr]

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

В программе на Windows перед названием переменной-адреса лучше указывать оператор rel:

global _start

section .text
_start:
    mov rax, 23         ; RAX = 23
    jmp [rel exitPtr]   ; переходим к адресу из exitPtr
    mov rax, 33         ; не выполняется
exit:                   ; метка exit     
    ret   
exitPtr: dq exit      ; переменная exitPtr хранит адрес метки exit     
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850