获取来电者的回信地址

Jan*_*zer 6 c++ x86 assembly visual-c++

我试图弄清楚如何在MSVC中获取调用方的返回地址。我可以使用_ReturnAddress()来获取函数的返回地址,但似乎找不到找到调用方的方法。

我曾尝试使用CaptureStackBackTrace,但由于某些原因,它在多次调用后崩溃。我也希望通过内联汇编解决方案。

void my_function(){
    cout << "return address of caller_function: " << [GET CALLER'S RETURN VALUE];
} // imaginary return address: 0x15AF7C0

void caller_function(){
     my_function();
}// imaginary return address: 0x15AFA70
Run Code Online (Sandbox Code Playgroud)

输出: return address of caller_function: 0x15AFA70

到目前为止,我还没有运气。谢谢你的帮助

ded*_*cos 6

如果存在保留的调用堆栈(即在调试版本中或不存在优化时),并且考虑将MSVC x86作为目标PE,则可以执行以下操作:

// getting our own return address is easy, and should always work
// using inline asm at all forces MSVC to set up EBP as a frame pointer even with optimization enabled
// But this function might still inline into its caller
__cdecl void *get_own_retaddr()
{
   // consider you can put this asm inline snippet inside the function you want to get its return address
   __asm
   {
       MOV EAX, DWORD PTR SS:[EBP + 4]
   }
   // fall off the end of a non-void function after asm writes EAX:
   // supported by MSVC but not clang's -fasm-blocks option
}
Run Code Online (Sandbox Code Playgroud)

在调试版本中,当在编译器上禁用优化时(MSVC编译器参数:),/Od并且不省略帧指针(MSVC编译器参数:),/Oy-cdecl函数的函数调用将始终将返回地址保存+4在被调用方堆栈帧的偏移量处。寄存器EBP存储正在运行的函数的堆栈帧的开头。因此在上面的代码foo中将返回其调用者的返回地址。


通过再次阅读您的问题,我认为您可能需要呼叫者的返回地址。您可以通过访问直接调用者的堆栈框架,然后获取其返回地址来实现。像这样:

// only works if *the caller* was compiled in debug mode
__cdecl void *get_caller_retaddr_unsafe_debug_mode_only()
{
   __asm
   {
       MOV ECX, DWORD PTR SS:[EBP + 0] // [EBP+0] points to caller stack frame pointer
       MOV EAX, DWORD PTR SS:[ECX + 4] // get return address of the caller of the caller
   }
}
Run Code Online (Sandbox Code Playgroud)

重要的是要注意,这要求调用者将EBP设置为具有传统堆栈框架布局的框架指针。这不是现代操作系统中的调用约定或ABI的一部分;堆栈展开异常使用不同的元数据。但是,如果禁用了调用方的优化功能,情况将会如此。

如Michael Petch所述,MSVC不允许在x86-64 C / C ++代码上使用asm内联构造。尽管如此,编译器允许使用一组完整的内部函数来处理该问题。

  • 在最上面您说_(对于x64同样适用,更改r64对应的相应r32寄存器,并将堆栈帧的偏移量+4更改为+8)_。这就意味着您要做的就是调整偏移量,并使用r64寄存器使内联汇编程序以64位工作。问题是[构建x86-64 C / C ++代码时不再支持嵌入式汇编。](https://docs.microsoft.com/zh-cn/cpp/assembler/inline/inline-assembler?view=vs -2019)。从该链接的文档中说,_ARM和x64处理器不支持内联汇编。 (2认同)
  • 确实存在 Windows api `RtlCaptureStackBackTrace` 和 `RtlWalkFrameChain` 可以完成这项工作,支持 x86/x64,因此不需要编写自定义代码。如果正确的话,它们当然不会崩溃。如果编写自己的代码,需要检查下一个可能的帧 *eax*: `mov eax,[ebp]` 高于 *ebp* 并低于堆栈顶部 - `fs:[4]` (fs:[NT_TIB.StackBase]) (2认同)