При выполнении программы специальный регистр-указатель инструкции или 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