青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

統(tǒng)計

  • 隨筆 - 50
  • 文章 - 42
  • 評論 - 147
  • 引用 - 0

留言簿(6)

隨筆分類

文章分類

Link

搜索

  •  

積分與排名

  • 積分 - 166924
  • 排名 - 159

最新評論

閱讀排行榜

評論排行榜

Reversing MS VC++ Part II: Classes, Methods and RTTI

摘要

       MS VC++ Win32平臺上最廣泛使用的編譯器,因此熟悉它的內(nèi)部工作機制對于Win32逆向愛好者非常重要。能夠理解編譯器生成的附加(glue)代碼有助于快速理解程序員寫的實際代碼。同樣也有助于恢復(fù)程序的高級結(jié)構(gòu)。

       Part II中,我將講到MSVC是如何實現(xiàn)C++的,包括類的布局,虛函數(shù),RTTI。假設(shè)你已經(jīng)熟悉C++基本知識和匯編語言。

基本的類布局

       為了解釋下面的內(nèi)容,讓我們看看這個簡單例子:

 

    class A

    {

      int a1;

    public:

      virtual int A_virt1();

      virtual int A_virt2();

      static void A_static1();

      void A_simple1();

    };

 

    class B

    {

      int b1;

      int b2;

    public:

      virtual int B_virt1();

      virtual int B_virt2();

    };

 

    class C: public A, public B

    {

      int c1;

    public:

      virtual int A_virt2();

      virtual int B_virt2();

};

 

       多數(shù)情形下,MSVC的類按如下格局分布:

Ÿ           指向虛函數(shù)表的指針(_vtable__vftable_),不過它只在類包括虛函數(shù),以及不能從基類復(fù)用合適的函數(shù)表時才會被添加。

Ÿ           基類。

Ÿ           函數(shù)成員。

 

虛函數(shù)表由虛函數(shù)的地址組成,表中函數(shù)地址的順序和它們第一次出現(xiàn)的順序(即在類定義的順序)一致。若有重載的函數(shù),則替換掉基類函數(shù)的地址。

因此,上面三個類的布局看起來象這樣:

 

    class A size(8):

        +---

     0  | {vfptr}

     4  | a1

        +---

 

    A's vftable:

     0  | &A::A_virt1

     4  | &A::A_virt2

 

    class B size(12):

        +---

     0  | {vfptr}

     4  | b1

     8  | b2

        +---

 

    B's vftable:

     0  | &B::B_virt1

     4  | &B::B_virt2

 

    class C size(24):

        +---

        | +--- (base class A)

     0  | | {vfptr}

     4  | | a1

        | +---

        | +--- (base class B)

     8  | | {vfptr}

    12  | | b1

    16  | | b2

        | +---

    20  | c1

        +---

 

    C's vftable for A:

     0  | &A::A_virt1

     4  | &C::A_virt2

 

    C's vftable for B:

     0  | &B::B_virt1

     4  | &C::B_virt2

 

上面的圖表是由VC8編譯器使用一個未公開的參數(shù)生成。為了看到這樣的類布局,使用編譯參數(shù) –d1 reportSingleClassLayout,可以輸出單個類的布局。-d1 reportAllClassLayout可以輸出全部類的布局(包括內(nèi)部的CRT類)。這些內(nèi)容都被輸出到stdout(標(biāo)準(zhǔn)輸出)。

       正如你看到的,C有兩個虛函數(shù)表vftables,因為它從兩個都有虛函數(shù)的類繼承。C::A_virt2的地址替換了A::A_virt2在類C虛函數(shù)表的地址,類似的,C::B_virt2替換了B::B_virt2

調(diào)用慣例和類方法

       MSVC中所有的類方法都默認(rèn)使用_thiscall_調(diào)用慣例。類實例的地址(_this_指針)作為隱含參數(shù)傳到ecx寄存器。在函數(shù)體中,編譯器通常立刻用其它寄存器(如esied),或棧中變量來指代。以后對類成員的引用都通過這個寄存器或棧變量。然而,在實現(xiàn)COM類時,則使用_stdcall_調(diào)用習(xí)慣。下文是對各種類型的類方法的一個概述。

 

1)      靜態(tài)方法

調(diào)用靜態(tài)方法不需要類的實例,所以它們和普通函數(shù)一樣的工作原理。沒有_this_指針傳入。因此也就不可能可靠的分辨靜態(tài)方法和簡單的普通函數(shù)。例如:

 

A::A_static1();

call    A::A_static1

 

2)      簡單方法

簡單方法需要一個類實例,_this_指針隱式的作為第一個參數(shù)傳入,通常使用_thiscall_調(diào)用慣例,例如通過_ecx_寄存器。當(dāng)基類對象沒有分配在派生類對象的開始處,在調(diào)用函數(shù)前,_this_指針需要被調(diào)整到指向基類子對象的實際開始位置。例如:

 

    ;pC->A_simple1(1);

    ;esi = pC

    push    1

    mov ecx, esi

    call    A::A_simple1

 

    ;pC->B_simple1(2,3);

    ;esi = pC

    lea edi, [esi+8] ;adjust this

    push    3

    push    2

    mov ecx, edi

    call    B::B_simple1

 

正如你看到的,在調(diào)用B的方法前,_this_指針被調(diào)整到指向B的子對象。

3)      虛方法(虛函數(shù))

為了調(diào)用虛函數(shù),編譯器首先需要從_vftable_取得函數(shù)地址,然后就像調(diào)用簡單方法一樣(例如,傳入_this_指針作為隱含參數(shù))。例如:

 

    ;pC->A_virt2()

    ;esi = pC

    mov eax, [esi]  ;fetch virtual table pointer

    mov ecx, esi

    call [eax+4]  ;call second virtual method

   

    ;pC->B_virt1()

    ;edi = pC

    lea edi, [esi+8] ;adjust this pointer

    mov eax, [edi]   ;fetch virtual table pointer

    mov ecx, edi

call [eax]       ;call first virtual method

 

4)      構(gòu)造函數(shù)和析構(gòu)函數(shù)

構(gòu)造函數(shù)和析構(gòu)函數(shù)類似于簡單方法,它們?nèi)〉秒[式的_this_指針(例如,在_thiscall_調(diào)用慣例下通過ecx寄存器)。雖然形式上構(gòu)造函數(shù)沒有返回值,但它在eax中返回_this_指針。

RTTI實現(xiàn)

       RTTIRun-Time Type Identification)運行時類型識別是由編譯器生成的特殊信息,用于支持像dynamic_cast<>typeid()這樣的C++運算符,以及C++異常。基于這個本質(zhì),RTTI只為多態(tài)類生成,例如帶虛函數(shù)的類。

       MSVC編譯器在vftable前設(shè)置了一個指針,指向叫做“Complete Object Locator”(完整對象定位器)的結(jié)構(gòu)。這樣稱呼是因為它允許編譯器從特定的vftable指針(因為一個類可能有若干vftable)找到完整對象的位置。COL就像如下定義:

 

struct RTTICompleteObjectLocator

{

DWORD signature; //always zero ?

    DWORD offset;    //offset of this vtable in the complete class

    DWORD cdOffset;  //constructor displacement offset

    struct TypeDescriptor* pTypeDescriptor; //TypeDescriptor of the complete class

    struct RTTIClassHierarchyDescriptor* pClassDescriptor; //describes inheritance hierarchy

};

 

       類層次描述符描述了類的繼承層次。對于一個類,所有COL共享一個。

 

struct RTTIClassHierarchyDescriptor

{

    DWORD signature;      //always zero?

    DWORD attributes;     //bit 0 set = multiple inheritance, bit 1 set = virtual inheritance

    DWORD numBaseClasses; //number of classes in pBaseClassArray

    struct RTTIBaseClassArray* pBaseClassArray;

};

 

       基類數(shù)組描述了所有基類,并包含在執(zhí)行_dynamic_cast_過程中編譯器是否允許強制轉(zhuǎn)換派生類到這些基類的信息。基類描述符中每一項都包含如下結(jié)構(gòu):

 

struct RTTIBaseClassDescriptor

{

    struct TypeDescriptor* pTypeDescriptor; //type descriptor of the class

    DWORD numContainedBases; //number of nested classes following in the Base Class Array

    struct PMD where;        //pointer-to-member displacement info

    DWORD attributes;        //flags, usually 0

};

 

struct PMD

{

    int mdisp;  //member displacement

    int pdisp;  //vbtable displacement

    int vdisp;  //displacement inside vbtable

};

 

       PMD描述了一個基類如何放置在完整類中。在簡單的繼承體系中,它位于從整個對象起始位置的一個固定偏移處,這個偏移量就是_mdisp_。如果是一個虛基類,那需要從vbtable中取得一個額外的偏移量加上。從派生類到基類調(diào)整_this_指針的偽碼如下:

 

    //char* pThis; struct PMD pmd;

    pThis+=pmd.mdisp;

    if (pmd.pdisp!=-1)

    {

      char *vbtable = pThis+pmd.pdisp;

      pThis += *(int*)(vbtable+pmd.vdisp);

}

 

       例如,我們的三個類的RTTI層次關(guān)系是:

 

RTTI hierarchy for our example classes

提取信息

1)      RTTI

如果存在,RTTI對于逆向工作來說是無價的信息。從RTTI,有可能恢復(fù)類的名字,繼承層次,有時候也能恢復(fù)部分的類布局。我的RTTI掃描器腳本可以顯示大多數(shù)此類信息。(參考附錄I

2)      靜態(tài)和全局初始化例程(initializer

全局和靜態(tài)對象需要在main主程序開始前初始化。MSVC通過生成初始化例程函數(shù)(funclet)來實現(xiàn),并把這些函數(shù)地址放入一個表中,當(dāng)_cinit初始化CRT時,會調(diào)用它們。這個表通常位于.data段的開始。一個典型的初始化例程如下:

 

    _init_gA1:

        mov     ecx, offset _gA1

        call    A::A()

        push    offset _term_gA1

        call    _atexit

        pop     ecx

        retn

    _term_gA1:

        mov     ecx, offset _gA1

        call    A::~A()

        retn

 

從這個表我們可以找到:

·         全局/靜態(tài)對象的地址

·         它們的構(gòu)造函數(shù)

·         它們的析構(gòu)函數(shù)

還可以參考MSVC _#pragma_directive_init_seg_[5]

3)      Unwind Funclets

若在函數(shù)中創(chuàng)建了自動類型的對象,VC++編譯器會自動生成異常處理代碼以確保在異常發(fā)生時會刪除這些對象。請參看Part I以了解對C++異常實現(xiàn)的細節(jié)。一個典型的unwind funclet在棧上銷毀一個對象的過程是:

 

    unwind_1tobase:  ; state 1 -> -1

        lea     ecx, [ebp+a1]

        jmp     A::~A()

 

通過在函數(shù)體中尋找相反的狀態(tài)變化,或者是在第一次訪問相同的棧中變量時,我們也可以找到構(gòu)造函數(shù)。

 

    lea     ecx, [ebp+a1]

    call    A::A()

mov     [ebp+__$EHRec$.state], 1

 

對于由new創(chuàng)建的對象,unwind funclet確保了萬一構(gòu)造失敗也能刪除分配的內(nèi)存:

 

    unwind_0tobase: ; state 0 -> -1

        mov     eax, [ebp+pA1]

        push    eax

        call    operator delete(void *)

        pop     ecx

        retn

 

在函數(shù)體中:

 

    ;A* pA1 = new A();

        push   

        call    operator new(uint)

        add     esp, 4

        mov     [ebp+pA1], eax

        test    eax, eax

        mov     [ebp+__$EHRec$.state], 0; state 0: memory allocated but object is not yet constructed

        jz      short @@new_failed

        mov     ecx, eax

        call    A::A()

        mov     esi, eax

        jmp     short @@constructed_ok

    @@new_failed:

        xor     esi, esi

    @@constructed_ok:

        mov     [esp+14h+__$EHRec$.state], -1

     ;state -1: either object was constructed successfully or memory allocation failed

     ;in both cases further memory management is done by the programmer

 

另一種類型的unwind funclets用于構(gòu)造函數(shù)和析構(gòu)函數(shù)中。它確保了萬一發(fā)生異常時刪除類成員。這時候,funclets要使用保存在一個棧變量的_this_指針,

 

    unwind_2to1:

        mov     ecx, [ebp+_this] ; state 2 -> 1

        add     ecx, 4Ch

        jmp     B1::~B1

 

這是funclet析構(gòu)類型B1位于偏移4Ch處一個類成員的代碼。從這里我們可以找到:

·         棧變量代表了C++對象或者指向用new分配的對象的指針

·         它們的構(gòu)造函數(shù)

·         它們的析構(gòu)函數(shù)

·         new創(chuàng)建的對象的大小

4)      構(gòu)造/析構(gòu)函數(shù)的遞歸調(diào)用

規(guī)則很簡單:構(gòu)造函數(shù)調(diào)用其他的構(gòu)造函數(shù)(其他基類和成員變量的構(gòu)造函數(shù)),析構(gòu)函數(shù)調(diào)用其它的析構(gòu)函數(shù)。一個典型的構(gòu)造函數(shù)按下列順序執(zhí)行:

·         調(diào)用基類構(gòu)造函數(shù)

·         調(diào)用復(fù)雜的類成員的構(gòu)造函數(shù)

·         若類有虛函數(shù),初始化vfptr

·         執(zhí)行當(dāng)前的構(gòu)造函數(shù)代碼(即由程序員寫得構(gòu)造代碼)

 

典型的析構(gòu)函數(shù)幾乎按照反序執(zhí)行:

·         若有虛函數(shù),初始化vfptr

·         執(zhí)行當(dāng)前的析構(gòu)函數(shù)代碼

·         調(diào)用復(fù)雜類成員的析構(gòu)函數(shù)

·         調(diào)用基類的析構(gòu)函數(shù)

 

MSVC生成的析構(gòu)函數(shù)另一個獨特的特征是它們的_state_變量通常初始化為最大值,每次析構(gòu)一個子對象就減一,這樣使得識別它們更容易。要注意簡單的構(gòu)造/析構(gòu)函數(shù)經(jīng)常被MSVC內(nèi)聯(lián)(inline)。那就是為什么你經(jīng)常看到vftable指針在同一個函數(shù)中被不同指針重復(fù)的調(diào)用。

5)      數(shù)組的構(gòu)造和析構(gòu)

MSVC使用一個輔助函數(shù)來構(gòu)造和析構(gòu)數(shù)組。思考下面的代碼:

 

    A* pA = new A[n];

    delete [] pA

 

翻譯成下面的偽碼:

 

    array = new char(sizeof(A)*n+sizeof(int))

    if (array)

    {

      *(int*)array=n; //store array size in the beginning

      'eh vector constructor iterator'(array+sizeof(int),sizeof(A),count,&A::A,&A::~A);

    }

    pA = array;

   

'eh vector destructor iterator'(pA,sizeof(A),count,&A::~A);

 

如果A有一個vftable,當(dāng)刪除數(shù)組時,相應(yīng)的會以調(diào)用一個刪除析構(gòu)函數(shù)的向量來替代:

 

    ;pA->'vector deleting destructor'(3);

    mov ecx, pA

    push 3 ; flags: 0x2=deleting an array, 0x1=free the memory

    call A::'vector deleting destructor'

 

A的析構(gòu)函數(shù)是虛函數(shù),則按照調(diào)虛函數(shù)的方式調(diào)用:

 

    mov ecx, pA

    push 3

    mov eax, [ecx] ;fetch vtable pointer

call [eax]     ;call deleting destructor

 

因此,從向量構(gòu)造/析構(gòu)函數(shù)疊代子調(diào)用我們可以知道:

·         對象數(shù)組的地址

·         它們的構(gòu)造函數(shù)

·         它們的析構(gòu)函數(shù)

·         類的大小

6)      刪除析構(gòu)函數(shù)

當(dāng)類有虛析構(gòu)函數(shù)時,編譯器生成一個輔助函數(shù)來刪除它。其目的是當(dāng)析構(gòu)一個類時確保_delete_操作符被調(diào)用。刪除析構(gòu)函數(shù)的偽碼如下:

 

    virtual void * A::'scalar deleting destructor'(uint flags)

    {

      this->~A();

      if (flags&1) A::operator delete(this);

};

 

這個函數(shù)的地址被放入vftable替換析構(gòu)函數(shù)地址。通過這種方式,如果另外一個類覆蓋了這個虛析構(gòu)函數(shù),那么它的_delete_將被調(diào)用。然而實際代碼中_delete_幾乎不會被覆蓋,所以你通常只看到調(diào)用默認(rèn)的delete()。有時候,編譯器也生成一個刪除析構(gòu)函數(shù)向量,就像下面一樣:

  

virtual void * A::'vector deleting destructor'(uint flags)

    {

      if (flags&2) //destructing a vector

      {

        array = ((int*)this)-1; //array size is stored just before the this pointer

        count = array[0];

        'eh vector destructor iterator'(this,sizeof(A),count,A::~A);

        if (flags&1) A::operator delete(array);

      }

      else {

        this->~A();

        if (flags&1) A::operator delete(this);

      }

};

 

我跳過了有虛基類的類的大部分實現(xiàn)細節(jié),因為它們使得事情更復(fù)雜了,而且在現(xiàn)實生活中很少用到。請參考Jan Gray寫的文章[1]。它已經(jīng)很詳盡了,只是用匈牙利命名法有點頭痛。文章[2]描述了一個MSVC實現(xiàn)虛繼承的實現(xiàn)。更多細節(jié)還可以看MS專利[3]

附錄 I ms_rtti4.idc

       這是我寫的解析RTTIvtfable的腳本。你可以從Microsoft VC++ Reversing Helpers打包下載我的兩篇文章和腳本。這個腳本的特點包括:

  • 解析RTTI結(jié)構(gòu),用對應(yīng)的類名重命名vftables
  • 對于某些簡單情形,識別和重命名構(gòu)造函數(shù)和析構(gòu)函數(shù)
  • 輸出所有的虛函數(shù)表,引用的函數(shù),及類的層次到一個文件里

 

使用說明:

在第一次分析結(jié)束后,加載ms_rtti4.idc。它會問你是否想要掃描exe文件來獲得vtable。注意這可能是一個漫長的過程。即使你跳過了掃描,你還是可以手工分析vtables。若你選擇了掃描,腳本將會試著識別所有的vtablesRTTI,重命名它們,識別和重命名構(gòu)造函數(shù)、析構(gòu)函數(shù)。有時候它會失敗,特別是存在虛繼承時。掃描結(jié)束后,它會打開記錄了結(jié)果的文本文件。

在加載腳本后,你可以用下面的快捷鍵手動分析一些MSVC結(jié)構(gòu):

  • Alt-F8 分析vtable。光標(biāo)應(yīng)該位于vtable的開始處。若有RTTI,腳本會使用類名。若沒有RTTI,你可以輸入一個類名,然后腳本將重命名vtable。若有可識別的虛析構(gòu)函數(shù),腳本也會重命名它。
  • Alt-F7 分析FuncInfoFuncInfo是存在于有對象分配在棧中或使用了異常處理的函數(shù)中的結(jié)構(gòu)體。它的地址被傳給函數(shù)異常處理程序的_CxxFrameHandler

 

    mov eax, offset FuncInfo1

jmp _CxxFrameHandler

 

大多數(shù)情況下,它可以被IDA自動識別和分析。但我的腳本提供了更豐富的信息。你也可以用我第一篇文章中的_ehseh.idc分析所有的FuncInfo

 

把光標(biāo)放在FuncInfo的開始處,用快捷鍵。

  • Alt-F9 分析ThrowInfoThrowInfo_CxxThrowException用來實現(xiàn)_throw_操作符的一個輔助結(jié)構(gòu)。它的地址是_CxxThrowException的第二個參數(shù)。

 

    lea     ecx, [ebp+e]

    call    E::E()

    push    offset ThrowInfo_E

    lea     eax, [ebp+e]

    push    eax

call    _CxxThrowException

 

把光標(biāo)放在ThrowInfo的開始處,使用該快捷鍵。腳本會分析該結(jié)構(gòu)體,重復(fù)添加thrown類的名字到注釋中。它還可以識別和重命名異常的析構(gòu)函數(shù)和拷貝構(gòu)造函數(shù)。

附錄II:恢復(fù)一個類的實踐

我們的題目是:MSN Messenger 7.5msnmsgr.exe版本號是7.5.324,大小7094272字節(jié))。它使用了大量的C++,含有很多RTTI信息。讓我們考慮兩個vftable,地址分別在.0040EFD8.0040EFE0。它們完整的RTTI結(jié)構(gòu)層次如下圖:

 

RTTI hierarchy for MSN Messenger 7.5

 

所以,這兩個vftables都屬于一個類-CContentMenuItem。通過查看它的基類描述符,我們看到:

  • CContentMenuItem包括三個基類-CDownloader, CNativeEventSinkCNativeEventSource
  • CDownloader包含一個基類-CNativeEventSink
  • 因此CContentMenuItem直接從CDownloader, CNativeEventSinkCNativeEventSource繼承,而CDownloaderCNativeEventSink繼承。
  • CDownloader位于完整對象的起始處,CNativeEventSource是在0x24偏移處。

 

 

所以我們可以得出結(jié)論,第一個vftable列出了CNativeEventSource的方法,第二個列出了CDownloaderCNatvieEventSink的方法(若干二者均沒有虛方法,CContentMenuItem將復(fù)用CNativeEventSourcevftable)。現(xiàn)在我們看看有什么指向了這兩個表。它們都被兩個函數(shù)引用,在.052B5E0.052B547。(這更說明了它們都屬于同一個類)。進一步,如果我們看看在函數(shù).052B547的開始處,_state_變量初始化為6,意味著那個函數(shù)是析構(gòu)函數(shù)。因為一個類只有一個析構(gòu)函數(shù),我們可以斷定.052B5E0就是它的構(gòu)造函數(shù)。讓我們看得更近些:

 

CContentMenuItem::CContentMenuItem   proc near

this = esi

    push    this

    push    edi

    mov     this, ecx

    call    sub_4CA77A

    lea     edi, [this+24h]

    mov     ecx, edi

    call    sub_4CBFDB

    or      dword ptr [this+48h], 0FFFFFFFFh

    lea     ecx, [this+4Ch]

    mov     dword ptr [this], offset const CContentMenuItem::'vftable'{for 'CContentMenuItem'}

    mov     dword ptr [edi], offset const CContentMenuItem::'vftable'{for 'CNativeEventSource'}

    call    sub_4D8000

    lea     ecx, [this+50h]

    call    sub_4D8000

    lea     ecx, [this+54h]

    call    sub_4D8000

    lea     ecx, [this+58h]

    call    sub_4D8000

    lea     ecx, [this+5Ch]

    call    sub_4D8000

    xor     eax, eax

    mov     [this+64h], eax

    mov     [this+68h], eax

    mov     [this+6Ch], eax

    pop     edi

    mov     dword ptr [this+60h], offset const CEventSinkList::'vftable'

    mov     eax, this

    pop     this

    retn

sub_52B5E0      endp

 

編譯器在prolog后的第一件事情就是從exc拷貝_this_指針到esi,因此隨后的地址引用都是基于esi。在初始化vfptr前,它調(diào)了兩個其它函數(shù),一定是基類的構(gòu)造函數(shù) 我們的例子中就是CDownloaderCNativeEventSource。進到這兩個函數(shù)中,我們可以確認(rèn)第一個用CDownloader::’vftable’初始化它的vfptr,第二個用CNativeEventSource::’vftable’。我們還可以進一步看看CDownloader的構(gòu)造函數(shù)-它調(diào)用了基類CNativeEventSink的構(gòu)造函數(shù)。

同樣,從edi中取得傳給第二個函數(shù)的_this_指針,它指向this+24h。根據(jù)我們的類結(jié)構(gòu)圖,這個地址是CNativeEventSource子對象的位置。這從另一個方向確認(rèn)了調(diào)用的第二個函數(shù)是CNativeEventSource的構(gòu)造函數(shù)。

調(diào)用完基類構(gòu)造函數(shù)后,基類對象的vfptr都被CContentMenuItem的實現(xiàn)重寫了,意味著CContentMenuItem覆蓋了基類的某些虛方法(或添加了它自己的)。(如果有需要,我們可以比較這些表,查看哪些指針被改變或者添加-被添加的就是CContentMenuItem新實現(xiàn)的。

下面我們看看幾個在地址.04D8000的函數(shù)調(diào)用,這時ecxthis+4Ch被設(shè)置到this+5Ch 很顯然,初始化了一些成員變量。我們?nèi)绾蔚弥鞘且粋€編譯器生成的構(gòu)造函數(shù)調(diào)用還是以程序員寫的初始化函數(shù)呢?這里有幾個提示:

  • 函數(shù)使用_thiscall_調(diào)用習(xí)慣,而且是第一次訪問這些域。
  • 這些域的初始化是按照地址增長的方向進行的。

為了保證我們可以查看析構(gòu)函數(shù)中的unwind funclet 那里我們可以看得為這些成員變量,編譯器生成的析構(gòu)函數(shù)調(diào)用。

這個新類不包括虛函數(shù),也就沒有RTTI,所以我們不知道它的真實名字。就叫它RefCountedPtr吧。我們已經(jīng)確定,4D8000是它的構(gòu)造函數(shù)。析構(gòu)函數(shù)我們可以從CContentMenuItem析構(gòu)函數(shù)的unwind funclet找到,它在63CCB4

回到CContentMenuItem的構(gòu)造函數(shù),我們看得3個域初始化為0,還有一個vftable指針。這看起來像是一個成員變量內(nèi)聯(lián)展開的構(gòu)造函數(shù)(不是基類的,因為若是基類,就應(yīng)該在繼承樹中存在)。從用到的vftableRTTI,我們看得它是CEventSinkList模板的一個實例。

現(xiàn)在,我們來寫一個可能的類聲明:

 

class CContentMenuItem: public CDownloader, public CNativeEventSource

{

/* 00 CDownloader */

/* 24 CNativeEventSource */

/* 48 */ DWORD m_unknown48;

/* 4C */ RefCountedPtr m_ptr4C;

/* 50 */ RefCountedPtr m_ptr50;

/* 54 */ RefCountedPtr m_ptr54;

/* 58 */ RefCountedPtr m_ptr58;

/* 5C */ RefCountedPtr m_ptr5C;

/* 60 */ CEventSinkList m_EventSinkList;

/* size = 70? */

};

 

我們不確定在偏移48處的變量是否不是CNativeEventSource的一部分,因為在CNativeEventSource的構(gòu)造函數(shù)中沒有訪問過,它很可能是CContentMenuItem的一部分。包含被重命名的方法的構(gòu)造函數(shù)和類結(jié)構(gòu)如下:

 

public: __thiscall CContentMenuItem::CContentMenuItem(void) proc near

    push    this

    push    edi

    mov     this, ecx

    call    CDownloader::CDownloader(void)

    lea     edi, [this+CContentMenuItem._CNativeEventSource]

    mov     ecx, edi

    call    CNativeEventSource::CNativeEventSource(void)

    or      [this+CContentMenuItem.m_unknown48], -1

    lea     ecx, [this+CContentMenuItem.m_ptr4C]

    mov     [this+CContentMenuItem._CDownloader._vfptr], offset const CContentMenuItem::'vftable'{for 'CContentMenuItem'}

    mov     [edi+CNativeEventSource._vfptr], offset const CContentMenuItem::'vftable'{for 'CNativeEventSource'}

    call    RefCountedPtr::RefCountedPtr(void)

    lea     ecx, [this+CContentMenuItem.m_ptr50]

    call    RefCountedPtr::RefCountedPtr(void)

    lea     ecx, [this+CContentMenuItem.m_ptr54]

    call    RefCountedPtr::RefCountedPtr(void)

    lea     ecx, [this+CContentMenuItem.m_ptr58]

    call    RefCountedPtr::RefCountedPtr(void)

    lea     ecx, [this+CContentMenuItem.m_ptr5C]

    call    RefCountedPtr::RefCountedPtr(void)

    xor     eax, eax

    mov     [this+CContentMenuItem.m_EventSinkList.field_4], eax

    mov     [this+CContentMenuItem.m_EventSinkList.field_8], eax

    mov     [this+CContentMenuItem.m_EventSinkList.field_C], eax

    pop     edi

    mov     [this+CContentMenuItem.m_EventSinkList._vfptr], offset const CEventSinkList::'vftable'

    mov     eax, this

    pop     this

    retn

public: __thiscall CContentMenuItem::CContentMenuItem(void) endp

鏈接和參考資料

[1] http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/dnarvc/html/jangrayhood.asp
with illustrations (but in Japanese): http://www.microsoft.com/japan/msdn/vs_previous/visualc/techmat/feature/jangrayhood/
C++: Under the Hood (PDF)

[2] http://www.lrdev.com/lr/c/virtual.html

[3] Microsoft patents which describe various parts of their C++ implementation. Very insightful.

  • 5410705: Method for generating an object data structure layout for a class in a compiler for an object-oriented programming language
  • 5617569: Method for implementing pointers to members in a compiler for an object-oriented programming language
  • 5754862: http://freepatentsonline.com/5854931.html Method and system for accessing virtual base classes
  • 5297284: Method and system for implementing virtual functions and virtual base classes and setting a this pointer for an object-oriented programming language
  • 5371891: Method for object construction in a compiler for an object-oriented programming language
  • 5603030: Method and system for destruction of objects using multiple destructor functions in an object-oriented computer system
  • 6138269: Determining the actual class of an object at run time

[4] Built-in types for compiler's RTTI and exception support.
http://members.ozemail.com.au/~geoffch@ozemail.com.au/samples/programming/msvc/language/predefined/index.html

[5] #pragma init_seg
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_predir_init_seg.asp

posted on 2009-03-11 15:37 pear_li 閱讀(586) 評論(0)  編輯 收藏 引用 所屬分類: C++

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            免费观看在线综合| 制服诱惑一区二区| 午夜久久tv| 国产一区视频在线观看免费| 午夜亚洲福利在线老司机| 一本色道久久综合狠狠躁的推荐| 老牛国产精品一区的观看方式| 欧美精品电影在线| 亚洲一二三区精品| 亚洲午夜一级| 国产欧美日韩另类一区| 久久精品av麻豆的观看方式| 99精品国产在热久久婷婷| 欧美国产另类| 国产精品99久久久久久久久久久久| 亚洲国产精选| 久久精品中文字幕免费mv| 亚洲第一在线| 亚洲高清在线视频| 欧美激情亚洲视频| 亚洲一区二区三区777| 最新亚洲视频| 国产精品美女在线观看| 久久爱www.| 久久人人爽爽爽人久久久| 国内精品久久久久久影视8| 欧美激情影院| 国产精品激情| 久久夜色精品国产欧美乱极品 | 999亚洲国产精| 亚洲另类黄色| 国产一区二区丝袜高跟鞋图片| 亚洲国产精品黑人久久久| 欧美日韩在线视频一区| 欧美影院一区| 久久综合给合| 亚洲美女视频在线观看| 欧美在线视频免费| 亚洲开发第一视频在线播放| 亚洲女爱视频在线| 亚洲国产精品ⅴa在线观看| 亚洲精品国产无天堂网2021| 国产精品综合视频| 亚洲国产精品成人va在线观看| 国产精品美女999| 欧美成人69| 欧美日韩一区二区在线视频 | 欧美精品一区视频| 你懂的成人av| 亚洲欧美偷拍卡通变态| 欧美超级免费视 在线| 亚洲国产成人精品久久| 国外成人在线| 欧美在线三级| 欧美在线播放一区二区| 欧美亚州在线观看| 亚洲精品欧美极品| 亚洲日本va午夜在线影院| 美腿丝袜亚洲色图| 亚洲靠逼com| 久久一区二区精品| 亚洲欧洲av一区二区| 黄色国产精品| 欧美偷拍另类| 免费试看一区| 欧美一级理论性理论a| 免费成人性网站| 欧美在线不卡| 亚洲乱码一区二区| 一区二区三区在线高清| 欧美日韩在线一区二区三区| 亚洲一区二区三区影院| 亚洲精品影院在线观看| 亚洲一区二区视频在线观看| 久久先锋影音av| 亚洲国产精品ⅴa在线观看| 夜夜爽www精品| 欧美日韩高清不卡| 久久精品国产一区二区三区免费看| 欧美成人官网二区| 亚洲精品欧美极品| 欧美日韩视频在线一区二区| 亚洲一区二区三区免费在线观看 | 久久久久久夜| 欧美激情一区二区三区高清视频 | 欧美性生交xxxxx久久久| 狂野欧美一区| 久久永久免费| 免费观看成人鲁鲁鲁鲁鲁视频| 久久www成人_看片免费不卡| 久久精品人人做人人爽| 久久久久国产一区二区| a4yy欧美一区二区三区| 99re亚洲国产精品| 亚洲一区在线播放| 欧美一区国产一区| 久久亚洲国产成人| 欧美日韩精品高清| 国产欧美一二三区| 亚洲人成欧美中文字幕| 亚洲网址在线| 欧美jizz19性欧美| 99re66热这里只有精品4| 性做久久久久久久免费看| 久久久99久久精品女同性| 欧美精品激情在线| 国内精品视频在线观看| 日韩视频―中文字幕| 久久青草福利网站| 亚洲一级片在线观看| 久久久久久久网站| 国产精品久久久久av免费| 久久精品一本| 欧美一区二区福利在线| 国产色综合天天综合网| 欧美一区二区三区在线视频| 欧美一区二区三区四区在线观看| 久热精品视频| 欧美国产综合| 精品成人免费| 久久深夜福利| 久久蜜桃香蕉精品一区二区三区| 一本色道久久综合亚洲精品按摩 | 亚洲国产精品成人综合色在线婷婷| 正在播放欧美视频| 久久综合色影院| 99精品热视频只有精品10| 久久久综合香蕉尹人综合网| 亚洲视频在线观看| 国内精品久久久久久久果冻传媒| 亚洲在线一区| 亚洲自拍偷拍视频| 国内一区二区三区| 亚洲丰满少妇videoshd| 免费日韩av片| 99视频一区| 久久av一区二区三区漫画| 国产综合久久| 91久久久久久| 国产亚洲欧洲一区高清在线观看| 亚洲深爱激情| 欧美中文字幕视频| 亚洲精品美女免费| 午夜精品福利在线| 一本到高清视频免费精品| 一区二区三区视频免费在线观看| 国产日韩欧美综合一区| 亚洲国产精品久久91精品| 欧美日韩一级片在线观看| 久久婷婷综合激情| 妖精视频成人观看www| 国外视频精品毛片| 一本色道久久加勒比88综合| 国产综合色产| 亚洲一区二区免费视频| ●精品国产综合乱码久久久久| 亚洲片在线资源| 影音先锋久久资源网| 欧美一站二站| 欧美在线播放一区二区| 国产欧美日韩亚洲| 欧美一区二区三区在线| 99国产精品99久久久久久| 欧美高清视频一区| 国产精品综合网站| 亚洲一区二区三区在线看| 99国产一区| 最新精品在线| 亚洲愉拍自拍另类高清精品| 久久国产精彩视频| 在线观看一区视频| 亚洲精品你懂的| 亚洲一区二区三区精品在线| 久久精品免费看| 久久久久网址| 另类春色校园亚洲| 在线综合+亚洲+欧美中文字幕| 久久成人免费网| 欧美精品久久久久久久| 欧美国产乱视频| 亚洲免费成人av| 一本到高清视频免费精品| 欧美丝袜一区二区| 在线亚洲一区| 久久精品人人做人人爽| 亚洲欧美日本国产有色| 最新日韩av| 在线综合视频| 亚洲欧美一区二区视频| 亚洲国产日韩一级| 国产精品网站一区| 国产精品激情偷乱一区二区∴| 91久久久在线| 亚洲视频播放| 久久久国产成人精品| 欧美日韩综合| 亚洲第一网站| 国产婷婷97碰碰久久人人蜜臀| 欧美尤物一区| 99精品国产热久久91蜜凸|