如何在函数中获取函数返回地址

How to get function return address within function?

本文关键字:函数 返回 地址 获取      更新时间:2023-10-16

我想在我的跟踪器中打印返回值,有两个问题

  1. 如何获取返回地址
  2. 返回位置在~Tracer()之前或之后更新

此处需要文本,以便Stackoverflow格式化代码:

struct Tracer
{
int* _retval;
~Tracer() 
{ printf("return value is %d", *_retval); }
};

int foo()
{
Tracer __tracter = { __Question_1_how_to_get_return_address_here__ };
if(cond) {
return 0;
} else {
return 99;
}
//Question-2: 
// return postion is updated before OR after ~Tracer() called ???
}

我发现了问题1的一些提示,现在检查Vc代码

对于gcc,__builtin_return_addresshttp://gcc.gnu.org/onlinedocs/gcc/Return-Address.html

对于Visual C++,_ReturnAddress

您无法在C++中以便携或可靠的方式做到这一点。返回值可以在存储器或寄存器中,在不同的情况下可以是间接的,也可以不是间接的。

您可能会使用内联汇编来使某些东西在某些硬件/编译器上工作。

一种可能的方法是使Tracer成为一个模板,它引用一个返回值变量(在适当的时候),并在销毁之前打印出。

还要注意,带有__(双下划线)的标识符是为实现保留的。

您的问题相当令人困惑,您可以互换使用术语"地址"answers"值",这两个术语是不可互换的。

返回值是函数在x86(_64)中输出的值,它在E/RAX、EDX:EAX或XMM0等中以4/8字节值的形式出现,您可以在此处阅读更多信息。

另一方面,返回地址是E/RSP在进行调用时所指向的地址(也就是堆栈顶部的东西),并保存函数完成时"跳"回的地址(定义称为返回)。

现在我甚至不知道示踪剂是什么tbh,但我可以告诉你你是怎么得到的,这都是关于钩子的。

对于值,假设您在内部执行,只需将函数与具有相同定义的函数挂钩,一旦它返回,您就会得到结果。

对于地址,它有点复杂,因为你必须降低一点,并且可能会做一些asm恶作剧,我真的不知道你到底想做什么,但如果你愿意的话,我做了一个小"存根",为被调用者提供返回指针。

这里是:

void __declspec(noinline) __declspec(naked) __stdcall _replaceFirstArgWithRetPtrAndJump_() {
__asm {                   //let's call the function we jump to "callee", and the function that called us "caller"                       
push ebp             //save ebp, ESP IS NOW -4
mov ebp, [esp + 4]  //save return address
mov eax, [esp + 8] //get callee's address (which is the first param) - eax is volatile so iz fine
mov[esp + 8], ebp //put the return address where the callee's address was (to the callee, it will be the caller)
pop ebp          //restore ebp
jmp eax         //jump to callee
}   }
#define CallFunc_RetPtr(Function, ...) ((decltype(&Function))_replaceFirstArgWithRetPtrAndJump_)(Function, __VA_ARGS__)
unsigned __declspec(noinline) __stdcall printCaller(void* caller, unsigned param1, unsigned param2) {
printf("I'm printCaller, Called By %p; Param1: %u, Param2: %un", caller, param1, param2);
return 20;
}
void __declspec(noinline) doshit() {
printf("us: %pnFunction we're calling: %pn", doshit, printCaller);
CallFunc_RetPtr(printCaller, 69, 420);  
}

现在可以肯定的是,您可以也可能应该使用_ReturnAddress()或任何不同编译器的内部函数,但如果这不可用(根据您的工作,这应该是一种非常罕见的情况),并且您了解ASM,这个概念应该适用于任何体系结构,因为无论指令集如何不同,它们都遵循相同的程序计数器设计。

我之所以写这篇文章,是因为很久以前出于某种目的,我一直在寻找答案,但我找不到一个好的答案,因为大多数人都会说"尽管这不可能,也不便携",我觉得这会有所帮助。