Что эквивалентно инструкции x86 "ret"?

Скажем, я пишу процедуру в сборке x86, например, "add", которая добавляет два числа, переданные в качестве аргументов.

По большей части это очень простой метод:

push ebp
mov ebp, esp
mov eax, [ebp+8]
add eax, [ebp+12]
mov esp, ebp
pop ebp
ret

Но, можно ли каким-либо образом переписать этот метод, чтобы избежать использования инструкции "ret" и все равно получить точный результат?

Ответ 1

Для моделирования ret не нужны никакие свободные регистры, но для этого требуется 4 байта памяти (dword). Использует косвенный jmp. Изменить: Как отметил Ирак Бакстер, этот код не является повторным. Работает отлично в однопоточном коде. Сбой при использовании в многопоточном коде.

push ebp
mov  ebp, esp
mov  eax, [ebp+8]
add  eax, [ebp+12]
mov  ebp, [ebp+4]
mov  [return_address], ebp
pop  ebp

add  esp,4
jmp  [return_address]

.data
return_address dd 0

Заменить только инструкцию ret, не изменяя остальную часть кода. Не реентерабельный. Не используйте в многопоточном коде. Изменить: исправлена ​​ошибка ниже кода.

push ebp
mov  ebp, esp
mov  ebp, [ebp+4]
mov  [return_address], ebp
pop  ebp

add  esp,4
jmp  [return_address]

.data
return_address dd 0

Ответ 2

Конечно.

push ebp
mov ebp, esp
mov eax, [ebp+8]
add eax, [ebp+12]
mov esp, ebp
pop ebp

pop ecx  ; these two instructions simulate "ret"
jmp ecx

Это предполагает, что у вас есть свободный регистр (например, ecx). Написание эквивалента, который использует "без регистров", возможно (ведь x86 является машиной Тьюринга), но, вероятно, будет включать множество свернутых регистров и перетасовку стека.

Большинство современных ОС предлагают хранилище, специфичное для потоков, доступное одному из регистров сегментов. Вы могли бы затем имитировать "ret" таким образом, безопасно:

 pop   gs:preallocated_tls_slot  ; pick one
 jmp   gs:preallocated_tls_slot

Ответ 3

Не тестировались, но вы можете сделать ret, не используя GPR:

add esp,4
jmp dword ptr [esp-4]

Ответ 4

push ebp
mov  ebp, esp
mov  ebp, [ebp+4]
mov  [return_address], ebp
nop
pop  ebp

add  esp,4
iret
jmp  [return_address]

.data
return_address dd 0

Инструкция nop отклонит процесс 8080 и пропустит mov, ebp, если значение недействительно или выполнимо, и выполнение следующей допустимой команды-добавления esp, 4 и jmp [return_address]. Также предотвращает утечку Killer.EXE его когти в виде Killer.EXE убивают плохие 8 и 16-битные операции. В VCPI это может быть очень неприятной ошибкой для debug.Also iret установит отдачу от точки после добавления esp, 4. Это необязательно, но во всех действиях для отладки - и это то, что вы делаете, вы смотрите на инструкцию, для которой не используются 8-битные мнемоники и вы хотите их исключить - поскольку вы устанавливаете это в своем первом сегменте кода push ebp, который базовый указатель.Если ошибка является ebp, то 8-битные регистры устанавливают команду nop под push ebp [base pointer], к которой недействителен, толкает esp в ebp и удаляет второй mov ebp, esp и вводит iiret туда, где эта строка существовала.

Это два жизнеспособных варианта - но я всего лишь ассемблер FLAT, и что я знаю о режимах, которые превышают двумерный формат. Я просто сказал вам, что вы должны выполнить отладку gui gpf, которая является плоской модель 32-разрядной инструкции.