C++反彙編-虛函數

學無止盡,積土成山,積水成淵-《C++反彙編與逆向分析技術揭祕》 讀書筆記

在C++中,使用關鍵字virtual聲明爲虛函數。ios

  • 虛函數地址表(虛表)
    • 定義:當類中定義有虛函數時,編譯器會把該類中全部虛函數的首地址保存在一張地址表中,即虛函數地址表。
    • 虛表信息在編譯後被連接到執行文件中,所以所得到的虛表地址是一個固定的地址。
    • 虛表中虛函數的地址排列順序依據虛函數在類中的聲明順序而定。
  • 虛表指針
    • 同時編譯器還會在類的每一個對象添加一個隱藏數據成員,稱爲虛表指針,保存着虛表的首地址,用於記錄和查找虛函數。
    • 虛表指針的初始化是經過編譯器在構造函數中插入代碼實現的。因爲必須初始化虛表指針,編譯器會提供默認的構造函數。
  • 虛函數調用過程
    • 虛表間接尋址訪問:
      使用對象的指針或引用調用虛函數。根據對象的首地址,取出相應的虛表指針,在虛表查找對應的虛函數的首地址,並調用執行。
    • 直接調用訪問:
      使用對象調用虛函數,和調用普通成員函數同樣。
 
 
 
虛函數的識別:
  • 類中隱式定義一個數據成員
  • 數據成員在首地址處,佔4字節
  • 構造函數初始化該數據成員爲某個數組的首地址
  • 地址屬於數據區,相對固定的地址
  • 數組的成員是函數指針
  • 函數被調用方式是thiscall
構造函數與析構函數都會將虛表指針設置爲當前對象所屬類中的虛表地址。

 

  • 構造函數中是完成虛表指針的初始化,此時虛表指針並無指向虛表函數。
  • 執行析構函數時,其對象的虛表指針已經指向某個虛表首地址。虛函數是在還原虛表指針,讓其指向自身的虛表首地址,防止在析構函數中調用虛函數時取到非自身虛表。

 

示例C++源碼數組

 1 #include <iostream>
 2  using  namespace std;
 3 
 4  class CVirtual {
 5  public:
 6              virtual  int GetNumber() {  return m_nNumber; }
 7              virtual  void SetNumber( int nNumber) { m_nNumber = nNumber;}
 8             ~CVirtual(){ printf( " ~CVirtual! "); }
 9  private:
10              int m_nNumber;
11 
12  
13 };
14  
15  int main()
16 {
17     CVirtual myVirtual ,*pVirtual;
18     pVirtual = &myVirtual;
19     pVirtual->SetNumber( 10);
20     printf( " %d\r\n ", pVirtual->GetNumber());
21      return  0;

 

彙編代碼函數

1.構造函數 this

mov     [ebp-8], ecx    ;=>保存this指針
mov     eax, [ebp-8]    ;=>eax得到this指針
mov     dword ptr [eax], offset ??_7CVirtual@@6B@ ; const CVirtual::`vftable'  ;=>虛表指針初始化

 2.析構函數spa

mov     [ebp-4], ecx    ;=>保存this指針
mov     eax, [ebp-4]    ;=>eax得到this指針
mov     dword ptr [eax], offset ??_7CVirtual@@6B@ ; const CVirtual::`vftable'    =>虛表指針重置
push    offset aCvirtual ; "~CVirtual!"

3.虛函數調用指針

pVirtual->SetNumber(10); code

push    0Ah                ;=>參數10壓棧
mov     eax, [ebp-18h]     ;=>eax爲this指針
mov     edx, [eax]         ;=>edx爲虛表指針
mov     ecx, [ebp-18h]     ;=>ecx傳遞this指針
mov     eax, [edx+4]       ;=>虛函數SetNumber的地址=虛表+offset 4
call    eax
相關文章
相關標籤/搜索