首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

VC++ 函數與成員函數呼叫慣例深度解析:參數傳遞、this 指標與返回值處理

     編輯:關於VC++

理解 C++ 編譯器如何將函數呼叫轉換為組合語言指令,對實現反射、動態呼叫、Hook、序列化等底層機制至關重要。本文基於 x86 語彙 (VC6/VC7),系統梳理各類參數類型、呼叫慣例與類成員函數的堆疊/暫存器佈局。

核心前提

  • 預設呼叫慣例:__cdecl (呼叫端清理堆疊,參數右至左入堆)
  • 成員函數隱含 this 指標,VC 編譯器透過 ECX 傳遞 (__thiscall)
  • 返回值:整數/指標放 EAX,浮點數放 ST(0),大型結構體由呼叫端配置空間並傳遞隱含指標

1. 一般函數呼叫模式

A. 純量/指標參數 (by value)

void SetValue(LONG val);        // 呼叫端:push val → call SetValue
                                // 函數內:mov eax, [ebp+8] 取得 val

LONG GetValue(LONG val);        // 同樣入堆,返回值放 EAX
                                // 呼叫端:push val → call → mov result, eax

B. 類別物件參數 (by value)

void SetValue(std::string val); // 呼叫端:在堆疊建構暫時物件 → call
                                // 函數內:lea eax, [ebp+8] 取得物件位址

std::string GetValue(...);      // 呼叫端:配置返回值空間 → lea eax, [ret] → push eax → call
                                // 函數內:以 eax 為目標建構返回物件

C. 參考參數 (by reference)

void SetValue(std::string& val); // 呼叫端:lea eax, val → push eax → call
                                 // 函數內:mov eax, [ebp+8] 取得參考位址

std::string GetValue(std::string& val); // 參考同樣傳址,返回值另行處理

2. 成員函數呼叫模式 (thiscall)

關鍵差異:ECX = this 指標,參數仍右至左入堆。

A. 純量參數

void X::SetValue(LONG val);     // 呼叫端:push val → mov ecx, this → call
                                // 函數內:ecx=this, val=[ebp+8]

LONG X::GetValue(LONG val);     // 同上,返回值放 EAX

B. 類別物件參數 (by value)

void X::SetValue(std::string val); // 呼叫端:堆疊建構暫時物件 → mov ecx, this → call
                                   // 函數內:ecx=this, lea eax, [ebp+8] 取暫時物件

std::string X::GetValue(...);      // 呼叫端:配置返回空間 → lea eax, ret → push eax
                                   //       → mov ecx, this → call

C. 參考參數

void X::SetValue(std::string& val); // 呼叫端:lea eax, val → push eax → mov ecx, this → call
                                    // 函數內:ecx=this, mov eax, [ebp+8]

std::string X::GetValue(std::string& val); // 參考+返回值雙重隱含指標
                                           // 呼叫端:lea eax, val → push eax
                                           //       → lea eax, ret → push eax
                                           //       → mov ecx, this → call

3. 實戰應用:通用函數轉發器

利用上述規律,可構建繞過編譯器參數檢查的動態呼叫器:

// 初始化映射工廠
InitializeMappingFactory();

// 標準呼叫
IMVOKE_METHOD(NULL, &MessageBox, NULL, "hello", "title", MB_OK);

// 甚至可傳遞額外參數 (編譯器不檢查,執行期由被呼叫端忽略)
IMVOKE_METHOD(NULL, &MessageBox, NULL, "hello", "title", MB_OK, "extra1", "extra2");

實現核心:手工構造堆疊幀、設定 ECX、跳轉目標位址,需精確對應上述慣例。

64 位元 (x64) 差異提醒

  • 前 4 個整數/指標參數:RCX, RDX, R8, R9
  • 浮點參數:XMM0~XMM3
  • 額外參數:堆疊 (右至左),呼叫端清理 (無 __cdecl/__stdcall 之分)
  • this 仍放 RCX (第一參數位置)
  • 紅區 (Red Zone) 128 bytes 可供葉子函數使用

掌握呼叫慣例細節,是從「使用庫」進階到「構建基礎設施」的關鍵門檻,也是逆向工程、熱修補、語言互操作的核心能力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved