使用__builtin_extract_return_addr()函数查找ret指令的RSP值
Using __builtin_extract_return_addr() function to find the RSP value of ret instruction
我最近一直在尝试使用此处描述的 __builtin_extract_return_addr 函数 (https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html) 来获取 RSP 指针的编码值。我有意避免使用 __builtin_return_address(0) 函数,而只是尝试使用 RSP 寄存器将 return 地址值提供给调用者。
该功能的说明如下:
void * __builtin_extract_return_addr (void * 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
)
所以我有这个非常简单的测试代码,我一直在用它来了解一些这方面的知识,但没有得到我想要得到的值所以我想在 Whosebug 中问这个问题:
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 寄存器值将没有调用者的 return 地址。
在print_sp()函数的后两行中,我将void *addr硬编码为0x7fffffffe458的值,然后使用该地址值查看是否可以得到解码后的return 地址。原因是在ret
指令时,RSP值如图
here 如下:
RSP 0x7fffffffe458 —▸ 0x40058e (main+14) ◂— mov eax, 0
总而言之,我正在尝试使用 __builtin_return_address(0) 值 return 获取 0x400578 没有 的地址值。
我也尝试过使用内联汇编来实现 addq , %%rsp; jmpq -8(%%rsp)
但无济于事。是这样的吗?
uintptr_t result;
asm volatile ( "mov %%rsp, %[value]\n\t"
"addq , %[value]\n\t"
: [value]"=a"(result)
: );
uintptr_t caller_address = (uintptr_t)__builtin_extract_return_addr(result);
Here是print_sp和main()函数
的反汇编
此外,我在 Whosebug 上看到了一些类似的问题:
Retq instruction, where does it return
Meaning of 0x8($rsp)
我希望这个问题是有道理的。如果有任何不清楚的地方,请告诉我,我会尽快澄清。
谢谢,
我不确定您为什么要避免使用 __builtin_return_address(0)
,因为它可能是您可以用来获取 return 地址的最佳选择。如果您可以使用它,请使用它,但我们暂时同意。
你的反汇编表明你在函数序言中使用了帧指针寄存器,这意味着你可能在编译中使用了零优化。在这种情况下,您可以使用 rbp 而不是 rsp,因为它在函数开头采用 rsp 的值并保持不变。但是,您需要将 +1 添加到从 rbp 获得的指针(请注意,将 +1 添加到 long
指针实际上会在 x86_64 中添加 8 个字节)。
这样做是因为在将rsp复制到rbp的mov指令之前有一条push指令将旧的rbp值压入栈中,所以我们存储在rsp中的栈指针已经减少(栈向下增长)了8 个字节用于保存先前存储在 rbp 中的旧帧指针。
将 RBP 设置为帧指针的过程在 x86-64 调用约定中是可选的。 16 位 x86 曾经需要这个,一些 32 位调用约定依赖它来进行回溯,但 x86-64 调用约定不需要。如果您需要了解更多关于 return 地址在堆栈中相对于帧指针和堆栈指针的存储位置,您可以阅读它。
我认为不推荐使用寄存器变量,因此我将修改您的第二种内联汇编方法。
// 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 现在应该是您的 return 地址。不幸的是,这仅在您的编译器使用帧指针寄存器 rbp 时有效,如果您在编译时使用优化,它通常不会成立。例如,如果您使用 GCC,除 -O0
之外的任何级别都会在使用此方法时出错。
以防万一,您应该使用__builtin_extract_return_addr(ra)
。我不需要它。另请注意,除非您链接的是非 PIE 可执行文件,否则 return 地址将是您在反汇编中看到的值 + 可执行文件起始地址(这是您的可执行文件在内存中加载的位置)。您可以通过全局声明 extern char __executable_start;
然后打印其地址来获取此地址。
我最近一直在尝试使用此处描述的 __builtin_extract_return_addr 函数 (https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html) 来获取 RSP 指针的编码值。我有意避免使用 __builtin_return_address(0) 函数,而只是尝试使用 RSP 寄存器将 return 地址值提供给调用者。
该功能的说明如下:
void * __builtin_extract_return_addr (void * 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
)
所以我有这个非常简单的测试代码,我一直在用它来了解一些这方面的知识,但没有得到我想要得到的值所以我想在 Whosebug 中问这个问题:
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 寄存器值将没有调用者的 return 地址。
在print_sp()函数的后两行中,我将void *addr硬编码为0x7fffffffe458的值,然后使用该地址值查看是否可以得到解码后的return 地址。原因是在ret
指令时,RSP值如图
here 如下:
RSP 0x7fffffffe458 —▸ 0x40058e (main+14) ◂— mov eax, 0
总而言之,我正在尝试使用 __builtin_return_address(0) 值 return 获取 0x400578 没有 的地址值。
我也尝试过使用内联汇编来实现 addq , %%rsp; jmpq -8(%%rsp)
但无济于事。是这样的吗?
uintptr_t result;
asm volatile ( "mov %%rsp, %[value]\n\t"
"addq , %[value]\n\t"
: [value]"=a"(result)
: );
uintptr_t caller_address = (uintptr_t)__builtin_extract_return_addr(result);
Here是print_sp和main()函数
的反汇编此外,我在 Whosebug 上看到了一些类似的问题: Retq instruction, where does it return Meaning of 0x8($rsp)
我希望这个问题是有道理的。如果有任何不清楚的地方,请告诉我,我会尽快澄清。
谢谢,
我不确定您为什么要避免使用 __builtin_return_address(0)
,因为它可能是您可以用来获取 return 地址的最佳选择。如果您可以使用它,请使用它,但我们暂时同意。
你的反汇编表明你在函数序言中使用了帧指针寄存器,这意味着你可能在编译中使用了零优化。在这种情况下,您可以使用 rbp 而不是 rsp,因为它在函数开头采用 rsp 的值并保持不变。但是,您需要将 +1 添加到从 rbp 获得的指针(请注意,将 +1 添加到 long
指针实际上会在 x86_64 中添加 8 个字节)。
这样做是因为在将rsp复制到rbp的mov指令之前有一条push指令将旧的rbp值压入栈中,所以我们存储在rsp中的栈指针已经减少(栈向下增长)了8 个字节用于保存先前存储在 rbp 中的旧帧指针。
将 RBP 设置为帧指针的过程在 x86-64 调用约定中是可选的。 16 位 x86 曾经需要这个,一些 32 位调用约定依赖它来进行回溯,但 x86-64 调用约定不需要。如果您需要了解更多关于 return 地址在堆栈中相对于帧指针和堆栈指针的存储位置,您可以阅读它。
我认为不推荐使用寄存器变量,因此我将修改您的第二种内联汇编方法。
// 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 现在应该是您的 return 地址。不幸的是,这仅在您的编译器使用帧指针寄存器 rbp 时有效,如果您在编译时使用优化,它通常不会成立。例如,如果您使用 GCC,除 -O0
之外的任何级别都会在使用此方法时出错。
以防万一,您应该使用__builtin_extract_return_addr(ra)
。我不需要它。另请注意,除非您链接的是非 PIE 可执行文件,否则 return 地址将是您在反汇编中看到的值 + 可执行文件起始地址(这是您的可执行文件在内存中加载的位置)。您可以通过全局声明 extern char __executable_start;
然后打印其地址来获取此地址。