我想知道如何检索使用内联汇编调用我的函数的函数的地址。我的想法是将地址发送到调用我的函数将返回的位置并使用前面的指令(这是对调用我的函数的调用)检索调用我的函数的地址添加到给定的偏移量调用,到下一条指令的地址(调用我的函数将返回的地址)。到目前为止,我能够做到这一点,但要获得我的地址。它非常简单,有效:
_asm
{
mov eax, [ebp+4]
mov returnTo,eax
}
long addressOfMine = (*((long*)(returnTo - sizeof(long)))) + returnTo)
这可以很好地检索我的地址。 (通过知道[ebp + 4]是我将返回的地址)
为了做同样的事情但是上面一步我尝试获得旧的ebp并做同样的事情。我在一个网站上看到[ebp + 0]是旧的ebp所以我试过了:
_asm
{
mov eax, [ebp]
mov ebx, [eax+4]
mov returnTo,ebx
}
long addressOfCaller = (*((long*)(returnTo - sizeof(long)))) + returnTo)
但它不起作用。所以,我的假设是错的,或者我做错了所以我想请你帮忙。
答案 0 :(得分:6)
好的,只要你知道你在做一些不便携的事情。你知道吗?
我的意思是,并不是十几个人都没有说过......
如此。对于x86(但不是X64)上的函数,当帧指针遗漏和其他优化未启用时,这应该可行。不适用于所有调用约定,但它应该适用于标准C / C ++调用约定。
void ** puEBP = NULL;
__asm { mov puEBP, ebp };
void * pvReturn = puEBP[1]; // this is the caller of my function
puEBP = (void**)puEBP[0]; // walk back to the previous frame
void * pvReturn2 = puEBP[1]; // this is the caller's caller
编辑:好的,我现在正式感到困惑。我再次看了你的问题,据我所知,你的第一个代码片段在功能上与我刚写的相同。但是你说那段代码会给你你的函数地址 - 但这不应该是真的。代码片段应将调用者的地址返回给您的函数。
edit2:添加了代码以获取调用者的调用者。
顺便提一下您在问题中显示的代码
long addressOfCaller = (*((long*)(returnTo - sizeof(long)))) + returnTo)
不起作用。它基于这样的假设,即打电话的唯一方法是
call symbol
其中symbol是函数的4字节绝对地址。 但这不是打电话的唯一方式。也可以调用间接
mov eax, symbol
call [eax]
或
call [ref_symbol]
也可以相对于当前指令进行调用
call +12
但是你不需要这样做,一旦你知道调用函数中的任何地址,你可以使用Debug Help Library来找到调用你的函数的地址。
要使用DebugHelp,您必须具有代码的调试符号。然后只是使用 SymFromAddr
答案 1 :(得分:2)
为什么不直接使用操作系统提供的功能?这是一对链接How can one grab a stack trace in C?
您可以尝试更复杂的http://www.yosefk.com/blog/getting-the-call-stack-without-a-frame-pointer.html
但是你必须要知道在x86模式下calling conventions并不容易,它取决于你的操作系统,编译器,使用的调用约定等一些因素。
答案 2 :(得分:1)
做起来并不容易。您必须知道调用者有多少个参数和局部变量,在大多数情况下,通过编程方式计算并不容易。如果你做出了可能错误的假设,即编译器将EBP
保留为stackframe持有者并且永远不会改变它(例如,这可能不适用于-O3 / 2/1)
在被调用函数内部,您可以执行类似
的操作mov ecx,ebp ;save our current stack frame
pop ebp ;get the old value of EBP that was there before our function got called
mov dword [_caller_address],dword [ebp+4] ;this isn't actually a valid opcode. you figure it out.
mov ebp,ecx ;restore old state
push ebp
但这非常不安全。对编译器的优化可能会破坏它。它只适用于x86-32,并且如果它遵循不同的调用标准,它可能无法工作,取决于操作系统和编译器。
它的工作方式是:
C中的函数类似于
_Sum:
push ebp ; create stack frame
mov ebp, esp
mov eax, [ebp+8] ; grab the first argument
mov ecx, [ebp+12] ; grab the second argument
add eax, ecx ; sum the arguments
pop ebp ; restore the base pointer
ret
所以,如果说你有_SumCall它看起来像是
_SumCall:
push ebp ; create stack frame
mov ebp, esp
mov eax, [ebp+8] ; grab the first argument
mov ecx, [ebp+12] ; grab the second argument
push ecx ;first argument for call
push eax ;second argument for call
call _Sum
pop ebp ; restore the base pointer
ret
所以你看,它依赖于被调用者负责堆栈帧保存的事实。
如果您尝试它并且它不起作用,请确保调用者和被调用者都至少有一个局部变量/参数。如果失败了,那么,你刚刚搞砸了,因为编译器进行了一些优化,打破了这个假设。
再一次。这非常不安全,非常不便携
参考:http://courses.ece.illinois.edu/ece390/books/labmanual/c-prog-mixing.html
答案 3 :(得分:0)
我有一个想法,你“未知(谷歌)”。检索函数将返回的地址,修补该地址以“到达它返回的位置并调用你的函数传递你的那个值”,返回,并在你的回调中使用你收到的参数并修复你修补的内容与原始值。
顺便说一句,我希望你不要用它来侵入五角大楼的电脑。