您的问题相当令人困惑,您可以互换使用不可互换的术语“地址”和“价值”。
返回值是函数在 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: %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,这个概念应该适用于任何架构,因为无论指令集如何不同,它们都遵循相同的程序计数器设计。
我写这个更多是因为我很久以前出于某种目的寻找这个答案,但我找不到一个好的答案,因为大多数人只是说“呃,这是不可能的或便携的或其他什么”,并且我觉得这会有所帮助。