2010-01-31 6 views
0

我想知道如何使用內聯程序集檢索調用我的函數的函數的地址。我的想法是將地址放到調用我的函數返回的地方,並使用它之前的指令(即調用函數調用我的函數),檢索調用我的函數的地址並添加到給定的偏移量調用下一條指令的地址(調用我的函數返回的地址)。到目前爲止,我能夠做到這一點,但得到我的地址。這是相當簡單的,它的工作原理:使用內聯程序集獲取調用我的函數的函數的地址

_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) 

但它不起作用。所以,我的假設是錯誤的,或者我做錯了,所以我想問你的幫助。

+2

您知道,通過全局交叉函數優化,可能會出現一些非常有趣的問題。如果編譯器正在執行跨功能CSE,則代碼可能並不嚴格位於任何用戶定義的函數中。 – Omnifarious 2010-01-31 02:36:23

+0

我明白事情並不總是簡單,但事實是,我的功能將返回到一個函數,該函數將返回到另一個。所以,我想要的價值必須在某個地方。 – user246100 2010-01-31 02:38:46

+1

除非它被內聯...... – 2010-01-31 02:43:31

回答

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不容易,這取決於一些因素,如您的操作系統,編譯器,使用的通話慣例等。

+0

我不希望顯示調用我的函數的名稱。我想獲得它的地址,以便我可以使用它。我會檢查你的第二個鏈接。 – user246100 2010-01-31 02:56:58

+0

爲了得到函數的名字,你首先需要它的地址。 – Ismael 2010-01-31 02:59:39

+0

檢查第一個鏈接,瞭解我所說的 – user246100 2010-01-31 03:01:45

5

好的,只要你知道你在做一些不可移植的東西。你知道嗎?

我的意思是,它不是像十幾人都沒有說,它已經...

左右。對於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 

其中符號是函數的4字節絕對地址。 但那不是打電話的唯一方式。它也可以叫間接

mov eax, symbol 
call [eax] 

call [ref_symbol] 

而且它也可以相對於當前指令

call +12 

調用但你並不需要這樣做,一旦您知道調用函數中的任何地址,您可以使用Debug Help Library來查找調用您的函數的地址。

爲了使用DebugHelp,您必須具有代碼的調試符號。那麼只需使用 SymFromAddr

+0

好的。現在更清楚了。你想要你的來電者的來電者。好。 – 2010-01-31 03:56:56

+0

未知:查看我的最新編輯。我已經在debuffer中驗證了這個代碼,並關閉了編譯器優化。 – 2010-01-31 04:09:06

+0

我瞭解我假設的問題。無論如何,我使用os API來做我想做的事情。在linux中很容易。在Windows中,我仍然在思考如何使用StackWalk64功能。 – user246100 2010-01-31 04:44:45

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,並且根據操作系統和編譯器的不同,如果遵循不同的調用標準,它可能無法正常工作。

它的工作方式是這樣的:用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它會尋找somethign像

_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

+0

它不適用於我的-O2,我不能丟棄優化,因爲包含調用方的庫是不是我的。在Linux中,它非常簡單。只需要調用一次「int backtrace(void ** buffer,int size)」。在Windows中,我猜「StackWalk64」是我的解決方案,但尚未設法使其工作。 – user246100 2010-01-31 18:53:24

+0

即使這樣也不安全,因爲你怎麼知道棧中的函數是函數調用?你所要求的是不平凡的東西。 – Earlz 2010-02-01 20:25:06

0

我有一個想法爲你「未知(谷歌)」。檢索你的函數返回的地址,修改該地址做「回到它返回的位置並調用你的函數傳給你該值」,返回,並在你的回調函數中使用你收到的參數並修復你修補的內容與原始值。順便說一句,我希望你不是用這個來侵犯五角大樓的電腦。