簡體   English   中英

C ++中的函數指針和未知數量的參數

[英]Function pointers and unknown number of arguments in C++

我遇到了以下奇怪的代碼塊。想象你有以下typedef:

typedef int (*MyFunctionPointer)(int param_1, int param_2);

然后,在函數中,我們嘗試以下列方式從DLL運行函數:

LPCWSTR DllFileName;    //Path to the dll stored here
LPCSTR _FunctionName;   // (mangled) name of the function I want to test

MyFunctionPointer functionPointer;

HINSTANCE hInstLibrary = LoadLibrary( DllFileName );
FARPROC functionAddress = GetProcAddress( hInstLibrary, _FunctionName );

functionPointer = (MyFunctionPointer) functionAddress;

//The values are arbitrary
int a = 5;
int b = 10;
int result = 0;

result = functionPointer( a, b );  //Possible error?

問題是,沒有任何方法可以知道我們使用LoadLibrary獲取的地址的功能是否有兩個整數參數.dll名稱由用戶在運行時提供,然后列出導出函數的名稱和用戶選擇要測試的那個(再次,在運行時:S:S)。 那么,通過在最后一行執行函數調用,我們不是打開可能的堆棧損壞的大門嗎? 我知道這會編譯,但是在我們將錯誤的參數傳遞給我們指向的函數的情況下會發生什么樣的運行時錯誤?

我擔心沒有辦法知道 - 程序員在獲取函數指針並使用它時需要事先知道原型。

如果你事先不知道原型那么我猜你需要用DLL實現某種協議,你可以通過調用DLL中的已知函數來枚舉任何函數名稱及其參數。 當然,需要編寫DLL以符合此協議。

如果預期和使用的參數數量或類型以及調用約定不同,我可以想到三個錯誤:

  • 如果調用約定不同,則將讀取錯誤的參數值
  • 如果函數實際上需要的參數多於給定的參數,則隨機值將用作參數(我會讓你想象如果涉及指針的后果)
  • 在任何情況下,返回地址都是完全垃圾,因此一旦函數返回,就會運行隨機數據和隨機數據。

用兩個詞來說: Undefined behavior

如果它是一個__stdcall函數, 並且它們保留了完整的名稱(兩個都是大的ifs,但當然可能仍然可能),名稱最后會有@nn ,其中nn是一個數字。 該數字是函數期望作為參數的字節數,並將在返回之前清除堆棧。

因此,如果這是一個主要問題,您可以查看函數的原始名稱,並檢查您放入堆棧的數據量是否與要清除堆棧的數據量相匹配。

請注意,這仍然只是對Murphy的保護,而不是對Machiavelli的保護。 在創建DLL時,可以使用導出文件來更改函數的名稱。 這經常被用來剝離名稱錯誤 - 但我很確定它也可以讓你將一個函數從xxx @ 12重命名為xxx @ 16(或其他),以誤導讀者關於它所期望的參數。

編輯:(主要是回復msalters的評論):你確實不能將__stdcall應用於類似成員函數的東西,但你肯定可以在全局函數中使用它,無論它們是用C還是C ++編寫的。

對於像成員函數這樣的東西,函數的導出名稱將被破壞。 在這種情況下,您可以使用UndecorateSymbolName來獲取其完整簽名。 使用它有點不重要,但也不是非常復雜。

我不這么認為,這是一個很好的問題,唯一的規定就是你必須知道函數指針的工作參數是什么,如果不這樣做,盲目地填充參數並調用它,它會崩潰或跳轉永遠不會再被人看到...這需要程序員傳達關於函數期望和參數類型的信息,幸運的是你可以反匯編它並從查看堆棧指針和預期地址中找出通過'堆棧指針'(sp)來找出參數的類型。

例如,使用PE Explorer,您可以找出使用的函數並檢查反匯編轉儲...

希望這會有所幫助,最好的問候,湯姆。

它會在DLL代碼中崩潰(因為它傳遞了損壞的數據),或者:我認為Visual C ++在調試版本中添加了代碼以檢測此類問題。 它會說:“ESP的值不是通過函數調用保存的”,而是指向調用附近的代碼。 它有幫助,但並不完全健壯 - 我不認為它會阻止你傳入錯誤但相同大小的參數(例如.int而不是x86上的char *參數)。 正如其他答案所說,你必須知道,真的。

沒有一般的答案。 標准要求在某些情況下拋出某些異常,但除此之外描述了如何執行符合性的程序,有時還說某些違規必須導致診斷。 (這里或那里可能有更具體的東西,但我當然不記得了。)

代碼在那里做的不是根據標准,並且由於有一個演員,編譯器有權繼續做任何程序員想要的愚蠢的事情而不抱怨。 因此,這將是一個實施問題。

您可以查看您的實施文檔,但它可能也不存在。 您可以嘗試或研究如何在您的實現上完成函數調用。

不幸的是,答案很可能就是它會把事情搞砸而不會立即顯而易見。

通常,如果您正在調用LoadLibrary和GetProcByAddrees,那么您可以獲得告訴您原型的文檔。 更常見的是,與所有windows.dll一樣,您將獲得一個頭文件。 雖然這會導致錯誤,如果錯誤,通常很容易觀察,而不是那種潛入生產的錯誤。

大多數C / C ++編譯器讓調用者在調用之前設置堆棧,然后重新調整堆棧指針。 如果被調用的函數不使用指針或引用參數,則不存在內存損壞,盡管結果將毫無價值。 正如重新運行所說,指針/參考錯誤幾乎總是會出現一些測試。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM