使用__builtin_extract_return_addr()函数查找ret指令的RSP值 [英] Using __builtin_extract_return_addr() function to find the RSP value of ret instruction

查看:223
本文介绍了使用__builtin_extract_return_addr()函数查找ret指令的RSP值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近一直在尝试使用此处描述的__builtin_extract_return_addr函数( https: //gcc.gnu.org/onlinedocs/gcc/Return-Address.html )以获取RSP指针的编码值。我故意避免使用__builtin_return_address(0)函数,而只是尝试使用RSP寄存器将返回地址值获取给调用者。

I have recently been trying to use the __builtin_extract_return_addr function described here (https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html) to obtain the encoded value of the RSP pointer. I am intentionally avoiding using __builtin_return_address(0) function, and solely trying to get the return address value to the caller using the RSP register.

该函数的描述如下:


void * __ builtin_extract_return_addr void * addr

由__builtin_return_address返回的地址可能必须通过此函数提供,以获取实际的编码地址。如果不需要修复,则此函数只需通过addr即可。

The address as returned by __builtin_return_address may have to be fed through this function to get the actual encoded address. If no fixup is needed, this function simply passes through addr.

据我了解,此函数似乎可以获取任意地址并获得实际地址编码地址。 (例如,如果
RSP 0x7fffffffe458 —▸ 0x40058e(main + 14)
然后使用__builtin_extract_return_addr(0x7fffffffe458)应为0x40058e

From my understanding, it seems as this function can take the arbitrary address and obtain the actual encoded address. (e.g., if RSP 0x7fffffffe458 —▸ 0x40058e (main+14), then using __builtin_extract_return_addr(0x7fffffffe458) should be 0x40058e )

因此,我一直在使用这个非常简单的测试代码来学习一些有关此内容的信息,但没有得到我想要获得的值,所以我想在StackOverflow中提问:

So I have this very simple test code I have been using to learn a bit about this, but not getting the values I am trying to get so I wanted to ask the question in the StackOverflow:

void print_sp() {
   register void *sp asm ("rsp");
   printf("%p\n", __builtin_extract_return_addr(sp));

   void *addr = 0x7fffffffe458;
   printf("%p\n", __builtin_extract_return_addr((addr)));

   printf("%p\n", __builtin_return_address(0)); // I am trying to avoid using this
}

int main() {
   print_sp();
}

在print_sp()函数的前两行中,我正在阅读和打印RSP寄存器值,然后使用builtin_extract_return_addr查看我是否可以获得RSP寄存器中存储内容的编码地址。这是我使用gdb调试时的失败,我了解这是因为调用此行时的RSP寄存器值将没有返回给调用方的地址。

In the first two lines of print_sp() function, I'm reading and printing the RSP register value, then using the builtin_extract_return_addr to see if I can get the encoded address of what is stored in the RSP register. This was a failure as I debugged using gdb, I am understanding it as the RSP register value at the time of this line is called will not have the return address to the caller.

在print_sp()函数的后两行中,我将void * addr硬编码为0x7fffffffe458的值,然后使用该地址值查看是否可以获得解码后的返回地址。原因是在执行 ret 指令时,RSP值如
此处如下:

In the second two lines of print_sp() function, I am hardcoding the void *addr to the value of 0x7fffffffe458, and then using this address value to see if I can get the decoded return address. The reason is that at the time of ret instruction, the RSP value as shown here is the following:

RSP 0x7fffffffe458 —▸ 0x40058e(main + 14)◂— mov eax,0

总而言之,我试图使用__builtin_return_address(0)值获取返回地址值0x400578

To summarize, I am trying to get the return address value of 0x400578 without using the __builtin_return_address(0) value.

我还尝试实现 addq $ 8,%% rsp; jmpq -8(%% rsp)使用内联汇编程序,但无济于事。像这样吗?

I have also tried to implement addq $8, %%rsp; jmpq -8(%%rsp) using the inline assembly but to no avail. Something like this?

uintptr_t result;
asm volatile ( "mov %%rsp, %[value]\n\t"
               "addq $8,   %[value]\n\t"
                 : [value]"=a"(result)
                 : ); 
uintptr_t  caller_address = (uintptr_t)__builtin_extract_return_addr(result);

这里是对print_sp和main()函数的反汇编

Here is the disassembly of print_sp and main() function

此外,这是我在StackOverflow上发布的一些类似问题:
Retq指令,它在哪里返回
含义是0x8($ rsp)

Also, here are a few similar questions posted in the StackOverflow that I took a look: Retq instruction, where does it return Meaning of 0x8($rsp)

我希望这个问题有意义。请让我知道是否有任何不清楚的地方,我会尽力澄清它们。

I hope the question makes sense. Please let me know if there is anything unclear and I will try to clarify them right away.

谢谢,

推荐答案

我不确定您为什么要避免使用 __ builtin_return_address(0),因为这可能是获取返回地址的最佳选择。如果可以使用它,请这样做,但我们现在将继续使用它。

I am not sure why you are avoiding __builtin_return_address(0) as it is probably the best option you can use to get the return address. If you can use it please do, but we will go along with that for now.

您的反汇编表明您正在函数序言中使用帧指针寄存器,这意味着您可能在编译中使用了零优化。在那种情况下,您可以使用rbp而不是rsp,因为它在函数开始时采用rsp的值并保持不变。但是,您将需要在从rbp获得的指针上添加+1(请注意,在 long 指针上添加+1实际上会在x86_64中添加8个字节)。

Your disassembly shows that you are using the frame pointer register in the function prologue, meaning that you are probably using zero optimization in compilation. In that case you can use rbp instead of rsp as it takes the value of rsp at the beginning of the function and remains constant. However, you will need to add +1 to the pointer you get from rbp (note that adding +1 to a long pointer actually adds 8 bytes in x86_64).

之所以这样做,是因为在将rsp复制到rbp的mov指令之前,有一条push指令会将旧的rbp值推入堆栈,因此存储在rsp中的堆栈指针已减少(堆栈向下增长)8个字节来保存先前存储在rbp中的旧帧指针。

This is done because before the mov instruction that copies rsp into rbp there is a push instruction that pushes the old rbp value to the stack, so our stack pointer stored in rsp have been decreased (the stack grows downward) by 8 bytes to hold the old frame pointer previously stored in rbp.

在x86-中,将此RBP设置为帧指针的过程是可选的64个调用约定。 16位x86曾经需要这样做,有些32位调用约定依赖它作为回溯,而x86-64调用约定则不需要。如果您需要更多地了解返回地址相对于帧指针和堆栈指针在堆栈中的存储位置,则可以阅读一下。

This process of setting up RBP as a frame pointer is optional in x86-64 calling conventions. 16-bit x86 used to require this, and some 32-bit calling conventions relied on it for backtraces, but x86-64 calling conventions don't. You can read about it if you need to know more about where the return address is stored in the stack relative to the frame pointer and stack pointer.

我认为使用不建议使用寄存器变量,因此我将修改您的第二种内联汇编方法。

I think the use of register variables is deprecated so I will modify your second approach of inline assembly.

  // works only if compiled without optimization, or -fno-omit-frame-pointer
  // RBP points to the saved-RBP value just below the return address
void *bp;
asm ("movq %%rbp, %0\n"
     : "=r" (bp));
void *ra;
ra = (void *) *((long *)bp + 1);

ra现在应该是您的寄信人地址。不幸的是,这仅在编译器使用帧指针寄存器rbp时才有效,而在编译时使用优化通常不成立。例如,如果您使用的是GCC,则使用 -O0 以外的任何级别都会给您带来错误。

ra should now be your return address. Unfortunately, this only works if your compiler uses frame pointer register rbp, which does not usually hold if you use optimization while compiling. If you are using GCC for example any level other than -O0 will give you an error using this approach.

以防万一,您应该使用 __ builtin_extract_return_addr(ra)。在我的情况下,我不需要它。还要注意,除非链接非PIE可执行文件,否则返回地址将是您在反汇编+可执行文件起始地址(这是将可执行文件加载到内存中的位置)中看到的值。您可以通过全局声明 extern char __executable_start; 然后打印其地址来获取此地址。

Just in case, you should use __builtin_extract_return_addr(ra). I didn't need it in my case. Also note that unless you are linking a non-PIE executable, the return address will be the value you see in the disassembly + executable start address (which is the location where your executable is loaded in memory). You can get this address by declaring extern char __executable_start; globally then printing its address.

这篇关于使用__builtin_extract_return_addr()函数查找ret指令的RSP值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆