從逆向分析角度看C++虛函數

來源:互聯網
上載者:User

談到虛函數,我想很多朋友都應該知道虛函數表指標VPTR和虛函數表VTABLE,如果不清楚的朋友,建議先看看侯捷先生翻譯的《深度探索C++物件模型》:)

剛開始的時候,我僅僅知道虛函數的多態機制是通過VPTR和VTABLE操控的,完全地相信書上所描述的,並沒有親自去證明過,或許是因為那時候我還沒有接觸到逆向分析吧:)

 

提幾個問題:

1.      VPTR的大小如何確定?

2.      VPTR在類執行個體中的位移值是多少?

3.      VPTR是如何索引到需要調用的函數的?

4.      VTABLE中函數的順序是如何確定的?

 

瞭解上述問題的朋友,可以關閉這個視窗了:)

考慮如下代碼:

#include <cstdio>class Base{public:int i;char c;double d;Base(){i  = 4;c  = 'A';d  = 2.0;}virtual void Virtual_Func_A(){printf("Virtual_Func_A()\n");}virtual void Virtual_Func_B(){printf("Virtual_Func_B()\n");}};int main(void){Base* b = new Base;b->Virtual_Func_A();b->Virtual_Func_A();return 0;}

分析工具:VC 6.0

分析過程:

在解答這個問題前,讀者有必要瞭解一下“資料對齊”的概念,如果不瞭解的讀者,可以參考下:)

http://blog.csdn.net/yeweiouyang/article/details/8636458

注意,Inter cpu 採用的是小端法

VPTR對程式員來說是隱形的,在Win32平台下,不考慮資料對齊的情況,VPTR佔用4個位元組,用於儲存VTABLE的地址

 考慮如下程式:

#include <cstdio>class Base{public:virtual void Common_Func(){printf("Base::Common_Func()\n");}virtual void Base_Func(){printf("Base::Func()\n");}};class Derived : public Base{public:virtual void Common_Func(){printf("Derived::Common_Func()\n");}virtual void Derived_Func(){printf("Derived::Func()\n");}};int main(void){Base* b = new Base;b->Common_Func();b->Base_Func();Derived* d = new Derived;d->Common_Func();d->Base_Func();d->Derived_Func();return 0;}

分析工具:IDA Pro

靜態反組譯碼:

.text:00401000    push    esi.text:00401001    push    4               ; 為 Base::VPTR 申請 4 個位元組的棧空間.text:00401003    call    ??2@YAPAXI@Z    ; operator new(uint).text:00401008    add     esp, 4.text:0040100B    test    eax, eax.text:0040100D    jz      short loc_401019.text:0040100F    mov     dword ptr [eax], offset Base_VPTR ; *b 的堆空間放入 Base::VPTR.text:00401015    mov     esi, eax.text:00401017    jmp     short loc_40101B

對應的offset Base_VPTR:

.rdata:004060BC Base_VPTR       dd offset Base_Common_Func ; DATA XREF: _main+Fo.rdata:004060C0                 dd offset Base_Func.rdata:004060C4                 align 8

不難發現Base_VPTR正是VTABLE的首地址,即Base::VPTR所指向之處

由這一句:

.text:0040100F    mov    dword ptr [eax], offset Base_VPTR ;

不難發現,VPTR存放的位置是Base類執行個體b的開始處,即位移值為0

.text:00401019.text:00401019 loc_401019:                              ; CODE XREF: _main+Dj.text:00401019    xor     esi, esi.text:0040101B.text:0040101B loc_40101B:                              ; CODE XREF: _main+17j.text:0040101B    mov     eax, [esi]                    ; eax = Base::VTABLE.text:0040101D    mov     ecx, esi.text:0040101F    call    dword ptr [eax]               ; call Base::Common_Func().text:00401021    mov     edx, [esi]                    ; edx = Base::VTABLE.text:00401023    mov     ecx, esi.text:00401025    call    dword ptr [edx+4]             ; call Base::Base_Func().text:00401028    push    4                             ; 為 Derived::VPTR 申請 4 個位元組的棧空間.text:0040102A    call    ??2@YAPAXI@Z                  ; operator new(uint).text:0040102F    add     esp, 4.text:00401032    test    eax, eax.text:00401034    jz      short loc_401040.text:00401036    mov     dword ptr [eax], offset Derived_VPTR ; *d 的堆空間放入 Derived_VPTR.text:0040103C    mov     esi, eax.text:0040103E    jmp     short loc_401042.text:00401040 ; ---------------------------------------------------------------------------.text:00401040.text:00401040 loc_401040:                              ; CODE XREF: _main+34j.text:00401040    xor     esi, esi.text:00401042.text:00401042 loc_401042:                              ; CODE XREF: _main+3Ej.text:00401042    mov     eax, [esi]                    ; eax = Derived::VTABLE.text:00401044    mov     ecx, esi.text:00401046    call    dword ptr [eax]               ; call Derived::Common_Func().text:00401048    mov     edx, [esi]                    ; edx = Derived::VTABLE.text:0040104A    mov     ecx, esi.text:0040104C    call    dword ptr [edx+4]             ; call Base_Func(),Base_Func()是從Base類中繼承而來的.text:0040104F    mov     eax, [esi]                    ; eax = Derived::VTABLE.text:00401051    mov     ecx, esi.text:00401053    call    dword ptr [eax+8]             ; call Derived::Derived_Func().text:00401056    xor     eax, eax.text:00401058    pop     esi.text:00401059    retn.text:00401059 _main           endp

對應的Derived_VPTR:

.rdata:004060B0 Derived_VPTR    dd offset Derived_Common_Func ; DATA XREF: _main+36o……(IDA 並沒有將所有函數指標顯示出來)

經過分析,Base和Derived的VTABLE結構大致如下:

在VTABLE中儲存有函數的地址(即函數指標),而非函數本身或函數名,在Win32平台下,函數指標佔用4個位元組,當通過類執行個體調用虛函數時,VPTR就會在VTABLE中進行索引,若找到對應的虛函數,就會取出函數指標並進行調用

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.