我使用 x86 (具体来说是 64 位) 的 Linux。有没有一种方法可以获取当前指令的地址。事实上,我想编写自己简化版本的setjmp/longjmp。在这里,R.. 发布了一个简化版的longjmp。有没有关于setjmp的实现方法,一个不考虑异常和信号等因素的简化版本...
我认为在64位代码中,你可以简单地使用lea rax, [rip]
。
而32位的习惯用法是:
call next
next: pop eax
__builtin_return_address
。当前段寄存器中的偏移地址寄存器 (EIP
) 通常是不可访问的。然而,有一种hack的方法可以间接读取它 - 欺骗程序将 EIP
的值推入栈中,然后只需从栈中读取即可。您可以创建一个如下所示的子例程:
GetAddress:
mov eax, [esp]
ret
...
call GetAddress ; address of this line stored in eax
call NextLine
NextLine:
pop eax ; address of previous line stored in EAX
CALL FAR
指令,则段值(CS
)也将被推入堆栈中。
这个网站提供了setjmp和longjmp的简单版本,如下所示。
#include "setjmp.h"
#define OFS_EBP 0
#define OFS_EBX 4
#define OFS_EDI 8
#define OFS_ESI 12
#define OFS_ESP 16
#define OFS_EIP 20
__declspec(naked) int setjmp(jmp_buf env)
{
__asm
{
mov edx, 4[esp] // Get jmp_buf pointer
mov eax, [esp] // Save EIP
mov OFS_EIP[edx], eax
mov OFS_EBP[edx], ebp // Save EBP, EBX, EDI, ESI, and ESP
mov OFS_EBX[edx], ebx
mov OFS_EDI[edx], edi
mov OFS_ESI[edx], esi
mov OFS_ESP[edx], esp
xor eax, eax // Return 0
ret
}
}
__declspec(naked) void longjmp(jmp_buf env, int value)
{
__asm
{
mov edx, 4[esp] // Get jmp_buf pointer
mov eax, 8[esp] // Get return value (eax)
mov esp, OFS_ESP[edx] // Switch to new stack position
mov ebx, OFS_EIP[edx] // Get new EIP value and set as return address
mov [esp], ebx
mov ebp, OFS_EBP[edx] // Restore EBP, EBX, EDI, and ESI
mov ebx, OFS_EBX[edx]
mov edi, OFS_EDI[edx]
mov esi, OFS_ESI[edx]
ret
}
}
call next
并使用[esp]
访问该值,而不是将其弹出到寄存器中。这样可以为您提供一个额外的可用寄存器,并且当您完成后只需执行add esp, 4
(在向下增长的堆栈上)即可清除堆栈。 - Polynomialmov (r|e)ax, $
或mylabel: mov (r|e)ax, offset mylabel
。 - Alexey Frunzelea rax, [rip]
无法工作。看起来RIP只能与rel
一起间接使用,例如lea rax, [rel _start]
? - Ciro Santilli OurBigBook.com