如何获取函数内的函数返回地址?

时间:2012-11-20 15:46:58

标签: c++ visual-c++ gcc

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

  1. 如何获得回复地址?
  2. 返回位置在~Tracer()之后的OR之前更新?
  3. 此处需要文字,因此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 ???
    }
    

3 个答案:

答案 0 :(得分:3)

您无法在C ++中移植或可靠地执行此操作。返回值可能在内存中或寄存器中,在不同情况下可能会或可能不会出现。

你可以使用内联汇编来使某些东西在某些硬件/编译器上运行。

一种可能的方法是让你的Tracer模板引用一个返回值变量(在适当的时候)并在破坏之前输出

另请注意,__(双下划线)的标识符是为实现保留的。

答案 1 :(得分:3)

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

对于gcc,__ builtin_return_address http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html

对于Visual C ++,_ ReturnAddress

答案 2 :(得分:1)

您的问题相当混乱,您可以互换使用术语“地址”和“值”,这两个术语不能互换。

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

另一方面,

返回地址是E / RSP在调用时指向的地址(又称堆栈顶部的内容),并保存函数完成时“跳回”位置的地址(什么按照定义称为返回)。

现在,我什至不知道什么是示踪剂,但是我可以告诉您如何获取示踪剂,这全都与钩子有关。

对于值,并假设您在内部进行操作,只需将函数与具有相同定义的函数挂钩,然后返回该值即可。

对于地址来说,它有点复杂,因为您必须降低位置,并可能要进行一些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: %u\n", caller, param1, param2);
    return 20;
}

void __declspec(noinline) doshit() {
    printf("us: %p\nFunction we're calling: %p\n", doshit, printCaller);
    CallFunc_RetPtr(printCaller, 69, 420);  
}

现在确定,您可以并且可能应该使用_ReturnAddress()或任何其他编译器的内部函数,但是如果不可用(根据您的工作,这应该是一种非常罕见的情况),并且您知道您的ASM,则此概念应适用任何架构,由于指令集可能不同,因此它们都遵循相同的程序计数器设计。

我写这封信的原因更多,是因为我很久以前一直在为某个目的寻找答案,而我找不到很好的答案,因为大多数人只会说“这是不可能的,无法携带的或其他任何东西” ”,我觉得这会有所帮助。