1

関数を呼び出した関数のアドレスをインライン アセンブリで取得する方法を知りたいです。私の考えは、mine を呼び出した関数が戻るアドレスを取得し、その前の命令 (つまり、mine を呼び出した関数の呼び出し) を使用して、mine を呼び出した関数のアドレスを取得し、それに与えられたオフセットに追加することです。次の命令のアドレスへの呼び出し (私の呼び出した関数が戻るアドレス)。これまでのところ、これを作成することはできましたが、私のアドレスを取得することはできませんでした。それはかなり簡単で、うまくいきます:

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

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

これにより、私のアドレスがうまく取得されます。([ebp+4] が私の返信先のアドレスであることを知ることによって)

同じことをするために、1ステップ上で、古いebpを取得して同じことを試みました。[ebp + 0]が古いebpであることをサイトで見たので、試しました:

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

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

しかし、うまくいきません。それで、私の仮定が間違っているか、何か間違ったことをしているので、あなたの助けを求めたいと思います.

4

5 に答える 5

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

ただし、これを行う必要はありません。呼び出し元の関数内のアドレスがわかれば、デバッグヘルプライブラリを使用して、呼び出し元の関数のアドレスを見つけることができます。

DebugHelpを使用するには、コードのデバッグシンボルが必要です。次に、 SymFromAddrを使用します。

于 2010-01-31T03:40:14.020 に答える
1

それは簡単ではありません。呼び出し元が持っている引数とローカル変数の数を知る必要がありますが、ほとんどの場合、プログラムで把握するのは簡単ではありません。EBPコンパイラがスタックフレームホルダーとして保持し、それを決して変更しないという間違った仮定をした場合(-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 でのみ機能し、OS とコンパイラによっては、別の呼び出し標準に従っていると機能しない場合があります。

それが機能する方法は次のとおりです。

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

ご覧のとおり、呼び出し先がスタック フレームの保存を担当しているという事実に依存しています。

試してみてうまくいかない場合は、呼び出し元と呼び出し先の両方に少なくとも 1 つのローカル変数/引数があることを確認してください。それに失敗すると、コンパイラがこの仮定を破るいくつかの最適化を行ったため、失敗しました。

もう一度。これは非常に危険であり、非常に移植性がありません

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

于 2010-01-31T18:13:40.210 に答える