0

我需要 EBP/RBP 的内容来检索函数的返回地址。此地址应位于堆栈帧内的位置 8(%RBP)(我们仅考虑 x86_64 位架构)。

我从传递给信号处理程序的 ucontex_t 结构中检索此值,但程序有一个非常奇怪的行为。有时 RBP 寄存器中包含的值根本没有意义(例如,0x00、0x01),有时它包含正确的堆栈基值。当然,这种行为会导致多个应用程序崩溃。

我需要检索函数的返回地址,因为我想找出调用函数的地址。

这是我正在使用的代码:

  syscall = ctx->uc_mcontext.gregs[REG_SYSCALL];
  pc=ctx->uc_mcontext.gregs[REG_PC];
  stack=ctx->uc_mcontext.gregs[REG_STACK];
  stack_base=ctx->uc_mcontext.gregs[REG_BASE];

  function_address=get_function_address((char *)stack_base); 

  DPRINT(DEBUG_INFO, "Received SYS_SECCOMP signal : syscall %lu\n", syscall); 
  DPRINT(DEBUG_ALL, "Syscall instruction address %p\n", info->si_call_addr);
  DPRINT(DEBUG_ALL, "PC  0x%lx, BASE_STACK  0x%lx, Stack 0x%lx\n", pc, stack_base, stack);
  DPRINT(DEBUG_ALL, "Syscall number %d\n", info->si_syscall);
  DPRINT(DEBUG_ALL, "Syscall arch   %u\n", info->si_arch);

宏定义如下:

#define REG_SYSCALL REG_RAX
#define REG_PC    REG_RIP
#define REG_BASE  REG_RBP
#define REG_STACK REG_RSP

前面代码的输出示例是:正确值:

[DEBUG_INFO] Received SYS_SECCOMP signal : syscall 3
Syscall instruction address 0x7fa058d67452
PC  0x7fa058d67452, BASE_STACK  0x7fff145c1d00, Stack 0x7fff145c1bc0
Syscall number 3
Syscall arch   3221225534

错误的值:

[DEBUG_INFO] Received SYS_SECCOMP signal : syscall 78
Syscall instruction address 0x7fa058df2495
PC  0x7fa058df2495, BASE_STACK  0xffffffffffffffa8, Stack 0x7fff145c1ed0
Syscall number 78
Syscall arch   3221225534

更糟 :

[DEBUG_INFO] Received SYS_SECCOMP signal : syscall 3
Syscall instruction address 0x7fa058e18360
PC  0x7fa058e18360, BASE_STACK  0x0, Stack 0x7fff145c1e68
Syscall number 3
Syscall arch   3221225534

我注意到,当 RBP 包含零时,它会保持相同的值,直到应用程序结束。

4

2 回答 2

4

如果使用最近的 GCC 编译器,您可能会对一些GCC 内置函数感兴趣,例如__builtin_return_address, __builtin_extract_return_addr,__builtin_frame_address

您可能对 GCC libbacktrace(可在 GCC 之外使用)和 Glibc回溯函数感兴趣。

于 2013-06-27T11:13:03.410 回答
1

There is no guarantee that all functions will use a standard stack frame with ebp being pushed and then set to esp at the start of the function. It's not that uncommon for functions to use ebp as a general purpose register, and then refer to function parameters and local variables via the esp register.

That's obviously more complicated for the code generator, because the value of esp will change over time (for example as variables are being pushed in a function call), but it's certainly possible to generate code that way.

At best, you could try and guess the return address by scanning up the stack, looking for a potential return address (e.g. by checking if the VMA for that address has the VM_EXEC flag set). Then having found a potential address, you would need to scan backwards from that address, looking for code that appears to be a function call (one example being an E8 five bytes back).

You could go further, by checking where the function call (assuming it's not an indirect call) is pointing to an address somewhere near your current IP, although figuring out what is a safe definition of "near" is not an easy decision either.

The bottom line is that it's going to very complicated, and there's still no guarantee that you're going to find the correct address.

于 2013-06-27T09:25:21.933 回答