在linux中检索EBP寄存器的内容

时间:2013-06-27 09:04:32

标签: c linux cpu-registers ucontext

我需要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包含零时,它会保持相同的值,直到应用程序结束。

2 个答案:

答案 0 :(得分:4)

如果使用最近的GCC编译器,您可能会对某些GCC builtins感兴趣,例如__builtin_return_address__builtin_extract_return_addr__builtin_frame_address

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

答案 1 :(得分:1)

无法保证所有函数都将使用标准堆栈帧,并且ebp被推送,然后在函数开始时设置为esp。函数使用ebp作为通用寄存器,然后通过esp寄存器引用函数参数和局部变量并不少见。

对于代码生成器来说,这显然更复杂,因为esp的值会随着时间的推移而改变(例如,在函数调用中推送变量),但是以这种方式生成代码当然是可能的。

充其量,您可以尝试通过扫描堆栈来查找返回地址,查找潜在的返回地址(例如,通过检查该地址的VMA是否设置了VM_EXEC标志)。然后找到了一个潜在的地址,你需要从该地址向后扫描,寻找看起来像是函数调用的代码(一个例子是E8五字节后面)。

你可以更进一步,通过检查函数调用的位置(假设它不是间接调用)指向当前IP附近的某个地址,虽然找出“近”的安全定义并不是一个简单的决定任

最重要的是它会变得非常复杂,而且仍然无法保证你会找到正确的地址。

相关问题