获取x86当前指令的地址

12

我使用 x86 (具体来说是 64 位) 的 Linux。有没有一种方法可以获取当前指令的地址。事实上,我想编写自己简化版本的setjmp/longjmp。在这里,R.. 发布了一个简化版的longjmp。有没有关于setjmp的实现方法,一个不考虑异常和信号等因素的简化版本...

4个回答

29

我认为在64位代码中,你可以简单地使用lea rax, [rip]

而32位的习惯用法是:

      call next
next: pop eax

在32位系统中是否可以做类似的事情? - MetallicPriest
在代码块中,我经常需要重复使用地址,因此我使用call next并使用[esp]访问该值,而不是将其弹出到寄存器中。这样可以为您提供一个额外的可用寄存器,并且当您完成后只需执行add esp, 4(在向下增长的堆栈上)即可清除堆栈。 - Polynomial
2
汇编中的另一个选项是 mov (r|e)ax, $mylabel: mov (r|e)ax, offset mylabel - Alexey Frunze
这个习语实际上并不被推荐使用。http://blogs.msdn.com/b/oldnewthing/archive/2004/12/16/317157.aspx - phuclv
1
在NASM 2.10中,lea rax, [rip]无法工作。看起来RIP只能与rel一起间接使用,例如lea rax, [rel _start] - Ciro Santilli OurBigBook.com

11

2
请记住,为了达到预期效果,您需要将其封装在一个函数中,否则您将得到当前堆栈帧的返回地址,而不是当前指令的地址。 - Jason
3
如果使用GCC,更容易使用somelabel: return &&somelabel; - phuclv
1
@Jason是正确的,还要确保包含__builtin_return_address的包装函数的定义不在头文件中,并且永远不会被内联。 - Michal Fapso
@phuclv 是的,还可以参考:https://dev59.com/nHI-5IYBdhLWcg3wlpeW - Ciro Santilli OurBigBook.com

9

当前段寄存器中的偏移地址寄存器 (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)也将被推入堆栈中。
如果您正在使用C语言,您可以在此页面上使用各种编译器特定的C扩展。请参阅这篇有趣的文章

OP询问x86_64,它具有相对寻址,因此存在对RIP“可访问”的指令。 - phuclv

1

这个网站提供了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
  }
}

4
这是你的问题的答案吗,@MetallicPriest? - karlphillip
不一定,如果您的答案好,我可能会选择它 :-p! - MetallicPriest

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接