简体   繁体   English

使用内联汇编获取调用我的 function 的 function 的地址

[英]Getting the address of the function that called my function with inline assembly

I would like to know how to retrieve the address of the function that called my function with inline assembly.我想知道如何使用内联汇编检索调用我的 function 的 function 的地址。 My idea is to get the address to where the function that called mine will return and using the instruction before it (that is a call to the function that called mine) retrieving the address of the one that called mine adding to the offset given to that call, to the address of the next instruction (the address to where the function that called mine will return).我的想法是获取调用我的 function 将返回的地址,并使用它之前的指令(即对调用我的 function 的调用)检索调用我的那个的地址添加到给定的偏移量调用,到下一条指令的地址(调用我的 function 将返回的地址)。 So far I was able to make this but to get the address of mine.到目前为止,我能够做到这一点,但得到了我的地址。 It is fairly simple and it works:它相当简单并且有效:

_asm
{
    mov eax, [ebp+4]
    mov returnTo,eax
}

long addressOfMine = (*((long*)(returnTo - sizeof(long)))) + returnTo)

This retrieves the address of mine just fine.这样可以很好地检索我的地址。 (By knowing that [ebp+4] is the address to where mine will return) (通过知道 [ebp+4] 是我的返回地址)

To do the same but one step above I tried to get the old ebp and do the same.为了做同样的事情,但比上面的一步我试着得到旧的 ebp 并做同样的事情。 I saw in a site that [ebp+0] is the old ebp so I tried:我在一个网站上看到 [ebp+0] 是旧的 ebp,所以我尝试了:

_asm
{ 
    mov eax, [ebp]
    mov ebx, [eax+4]
    mov returnTo,ebx
}

long addressOfCaller = (*((long*)(returnTo - sizeof(long)))) + returnTo)

But it doesn't work.但它不起作用。 So, my assumption is wrong or I'm doing something wrong so I would like to ask your help.所以,我的假设是错误的或者我做错了什么所以我想请你帮忙。

Ok, so as long as you know you are doing something non-portable. 好的,只要您知道自己正在做一些不可携带的事情。 You know that right? 你知道吗

I mean, it's not like a dozen people haven't said it already... 我的意思是,这并不是说十几个人没有说过……

so. 所以。 for functions on x86 (but not X64) when frame pointer omission and other optimizations aren't enabled, this should work. 对于未启用帧指针遗漏和其他优化功能的x86(而不是X64)上的功能,这应该可以工作。 doesn't work for all calling conventions, but it should work for standard C/C++ calling convention. 并非适用于所有调用约定,但应适用于标准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

edit: Ok I'm officially confused now. 编辑:好吧,我现在正式感到困惑。 I looked again at your question, and as far as I can tell, the your first code fragment is functionally the same as what I just wrote. 我再次查看了您的问题,据我所知,您的第一个代码片段在功能上与我刚写的相同。 But you say that that code gives you the address of your function - but that shouldn't be true. 但是您说该代码为您提供了函数的地址-但这不应该是真的。 that code fragment should be returning the address of the caller to your function. 该代码片段应将调用者的地址返回给您的函数。

edit2: added code to get the caller of the caller. edit2:添加代码以获取调用者。

By the way this code that you show in your question 顺便说一下,您在问题中显示的这段代码

long addressOfCaller = (*((long*)(returnTo - sizeof(long)))) + returnTo)

won't work. 将无法正常工作。 It's based on the assumption that the only way to make a call is 它基于这样的假设:拨打电话的唯一方法是

call symbol

where symbol is a 4 byte absolute address of a function. 其中symbol是函数的4字节绝对地址。 But thats not the only way to make a call. 但这不是打电话的唯一方法。 it's also possible to call indirect 也可以间接调用

mov eax, symbol
call [eax]

or 要么

call [ref_symbol]

And it also possible to call relative to the current instruction 而且还可以相对于当前指令进行调用

call +12

But you don't need to do this, once you know any address within the calling function, you can use the Debug Help Library to find the address of the function that called you. 但是您不需要这样做,一旦知道了调用函数中的任何地址,就可以使用“ 调试帮助库”来找到调用您的函数的地址。

In order to use DebugHelp, you must have the debug symbols for the code. 为了使用DebugHelp,您必须具有代码的调试符号。 then just use SymFromAddr . 然后只需使用SymFromAddr即可

Why don't just use the function provided by your OS? 为什么不只使用操作系统提供的功能? here are a pair of links How can one grab a stack trace in C? 这是一对链接如何在C中获取堆栈跟踪?

You may try something more complicated http://www.yosefk.com/blog/getting-the-call-stack-without-a-frame-pointer.html 您可以尝试更复杂的方法http://www.yosefk.com/blog/getting-the-call-stack-without-a-frame-pointer.html

But you have to be aware that in x86 mode the calling conventions are not easier, it depends on some factors like your OS, your compiler, the calling convention used, etc. 但是您必须意识到,在x86模式下, 调用约定并不容易,它取决于某些因素,例如操作系统,编译器,使用的调用约定等。

It's not easy to do. 这不容易做到。 You must know how many arguments and local variables the caller has, which in most cases, is not trivial to figure out programmatically. 您必须知道调用者有多少个参数和局部变量,在大多数情况下,以编程方式找出这些变量并不容易。 If you make the possibly wrong assumption that the compiler keeps EBP as the stackframe holder and never alters it(as in, this wouldn't work with -O3/2/1 probably) 如果您做出错误的假设,即编译器将EBP保留为堆栈框架持有者,并且从不对其进行更改(例如,这可能不适用于-O3 / 2/1)

inside of the called function, you could do something like 在被调用函数内部,您可以执行类似

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 

This is very unsafe though. 但是,这是非常不安全的。 Optimizations on the compiler will probably break it. 编译器上的优化可能会破坏它。 It will only work for x86-32, and depending on OS and compiler it may not work if it follows a different calling standard. 它仅适用于x86-32,并且取决于操作系统和编译器,如果遵循不同的调用标准,则可能不起作用。

The way it works is this: 它的工作方式是这样的:

Functions in C look something like 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

So, if say you had _SumCall it'd look somethign like 因此,如果说您有_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

So you see, it relies on the fact that the callee is responsible for stack frame preservation. 如此看来,它依赖于被调用方负责堆栈帧保留的事实。

If you try it and it doesn't work, ensure both the caller and callee have at least 1 local variable/argument. 如果尝试使用它不起作用,请确保调用方和被调用方都至少具有1个局部变量/参数。 Failing that, then well, your just screwed because the compiler made some optimization that broke this assumption. 失败了,那就太麻烦了,因为编译器进行了一些优化,打破了这一假设。

Once again. 再来一次。 This is very unsafe and is extremely not-portable 这是非常不安全的,而且非常不便携

Reference: http://courses.ece.illinois.edu/ece390/books/labmanual/c-prog-mixing.html 参考: http : //courses.ece.illinois.edu/ece390/books/labmanual/c-prog-mixing.html

I had an idea for you "unknown (google)". 我对您有个“未知的(google)”想法。 Retrieve the address of where your function will return to, patch that address to do "get to where it returns and call a function of yours passing you that value", return, and in your callback use the argument you receive and fix what you patched with the original values. 检索函数返回的地址,修补该地址以执行“到达返回的位置并调用传递该值的函数”,返回,并在回调中使用接收到的参数并修复修补的内容保留原始值。

By the way, I'm hoping you are not using this to invade the Pentagon computers. 顺便说一句,我希望您不要用它来入侵五角大楼的计算机。

What about something a bit more straight?更直接一点的东西怎么样? *Not sure if it works on any platform/CPU: *不确定它是否适用于任何平台/CPU:

void adr(void *x)
{
    *(void**)x = *(&x - 1); // x points to input parameter pushed before call
}

void *x;
adr(&x); // sets return adress to x

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM