zoukankan      html  css  js  c++  java
  • Reversing Microsoft Visual C++

    Reversing Microsoft Visual C++ Part I: Exception Handling

     

    Abstract


    Microsoft Visual C++ is the most widely used compiler for Win32 so it is important for the Win32 reverser to be familiar with its inner working. Being able to recognize the compiler-generated glue code helps to quickly concentrate on the actual code written by the programmer. It also helps in recovering the high-level structure of the program.

    In part I of this 2-part article (see also: Part II: Classes, Methods and RTTI), I will concentrate on the stack layout, exception handling and related structures in MSVC-compiled programs. Some familiarity with assembler, registers, calling conventions etc. is assumed.

    Terms:

    • Stack frame: A fragment of the stack segment used by a function. Usually contains function arguments, return-to-caller address, saved registers, local variables and other data specific to this function. On x86 (and most other architectures) caller and callee stack frames are contiguous.
    • Frame pointer: A register or other variable that points to a fixed location inside the stack frame. Usually all data inside the stack frame is addressed relative to the frame pointer. On x86 it's usually ebp and it usually points just below the return address.
    • Object: An instance of a (C++) class.
    • Unwindable Object: A local object with auto storage-class specifier that is allocated on the stack and needs to be destructed when it goes out of scope.
    • Stack UInwinding: Automatic destruction of such objects that happens when the control leaves the scope due to an exception.

    There are two types of exceptions that can be used in a C or C++ program.

    • SEH exceptions (from "Structured Exception Handling"). Also known as Win32 or system exceptions. These are exhaustively covered in the famous Matt Pietrek article[1]. They are the only exceptions available to C programs. The compiler-level support includes keywords __try, __except, __finally and a few others.
    • C++ exceptions (sometimes referred to as "EH"). Implemented on top of SEH, C++ exceptions allow throwing and catching of arbitrary types. A very important feature of C++ is automatic stack unwinding during exception processing, and MSVC uses a pretty complex underlying framework to ensure that it works properly in all cases.

    In the following diagrams memory addresses increase from top to bottom, so the stack grows "up". It's the way the stack is represented in IDA and opposite to the most other publications.

    Basic Frame Layout


    The most basic stack frame looks like following:

        ...

        Local variables

        Other saved registers

        Saved ebp

        Return address

        Function arguments

        ...


    Note: If frame pointer omission is enabled, saved ebp might be absent.

    SEH


    In cases where the compiler-level SEH (__try/__except/__finally) is used, the stack layout gets a little more complicated.


    SEH3 Stack Layout



    When there are no __except blocks in a function (only __finally), Saved ESP is not used. Scopetable is an array of records which describe each __try block and relationships between them:

        struct _SCOPETABLE_ENTRY {

          DWORD EnclosingLevel;

          void* FilterFunc;

          void* HandlerFunc;

        }


    For more details on SEH implementation see[1]. To recover try blocks watch how the try level variable is updated. It's assigned a unique number per try block, and nesting is described by relationship between scopetable entries. E.g. if scopetable entry i has EnclosingLevel=j, then try block j encloses try block i. The function body is considered to have try level -1. See Appendix 1 for an example.

    Buffer Overrun Protection


    The Whidbey (MSVC 2005) compiler adds some buffer overrun protection for the SEH frames. The full stack frame layout in it looks like following:


    SEH4 Stack Layout


    The GS cookie is present only if the function was compiled with /GS switch. The EH cookie is always present. The SEH4 scopetable is basically the same as SEH3 one, only with added header:

        struct _EH4_SCOPETABLE {

            DWORD GSCookieOffset;

            DWORD GSCookieXOROffset;

            DWORD EHCookieOffset;

            DWORD EHCookieXOROffset;

            _EH4_SCOPETABLE_RECORD ScopeRecord[1];

        };

        struct _EH4_SCOPETABLE_RECORD {

            DWORD EnclosingLevel;

            long (*FilterFunc)();

                union {

                void (*HandlerAddress)();

                void (*FinallyFunc)();

            };

        };

    GSCookieOffset = -2 means that GS cookie is not used. EH cookie is always present. Offsets are ebp relative. Check is done the following way: (ebp+CookieXOROffset) ^ [ebp+CookieOffset] == _security_cookie Pointer to the scopetable in the stack is XORed with the _security_cookie too. Also, in SEH4 the outermost scope level is -2, not -1 as in SEH3.

    C++ Exception Model Implementation

    When C++ exceptions handling (try/catch) or unwindable objects are present in the function, things get pretty complex.


    C++ EH Stack Layout



    EH handler is different for each function (unlike the SEH case) and usually looks like this:

         (VC7+)

        mov eax, OFFSET __ehfuncinfo

        jmp ___CxxFrameHandler


    __ehfuncinfo is a structure of type FuncInfo which fully describes all try/catch blocks and unwindable objects in the function.

        struct FuncInfo {

          // compiler version.

          // 0x19930520: up to VC6, 0x19930521: VC7.x(2002-2003), 0x19930522: VC8 (2005)

          DWORD magicNumber;

     

          // number of entries in unwind table

          int maxState;

     

          // table of unwind destructors

          UnwindMapEntry* pUnwindMap;

     

          // number of try blocks in the function

          DWORD nTryBlocks;

     

          // mapping of catch blocks to try blocks

          TryBlockMapEntry* pTryBlockMap;

     

          // not used on x86

          DWORD nIPMapEntries;

     

          // not used on x86

          void* pIPtoStateMap;

     

          // VC7+ only, expected exceptions list (function "throw" specifier)

          ESTypeList* pESTypeList;

     

          // VC8+ only, bit 0 set if function was compiled with /EHs

          int EHFlags;

        };


    Unwind map is similar to the SEH scopetable, only without filter functions:

        struct UnwindMapEntry {

          int toState;        // target state

          void (*action)();   // action to perform (unwind funclet address)

        };


    Try block descriptor. Describes a try{} block with associated catches.

        struct TryBlockMapEntry {

          int tryLow;

          int tryHigh;    // this try {} covers states ranging from tryLow to tryHigh

          int catchHigh;  // highest state inside catch handlers of this try

          int nCatches;   // number of catch handlers

          HandlerType* pHandlerArray; //catch handlers table

        };


    Catch block descriptor. Describes a single catch() of a try block.

    struct HandlerType {

      // 0x01: const, 0x02: volatile, 0x08: reference

      DWORD adjectives;

     

      // RTTI descriptor of the exception type. 0=any (ellipsis)

      TypeDescriptor* pType;

     

      // ebp-based offset of the exception object in the function stack.

      // 0 = no object (catch by type)

      int dispCatchObj;

     

      // address of the catch handler code.

      // returns address where to continues execution (i.e. code after the try block)

      void* addressOfHandler;

    };


    List of expected exceptions (implemented but not enabled in MSVC by default, use /d1ESrt to enable).

        struct ESTypeList {

          // number of entries in the list

          int nCount;

     

          // list of exceptions; it seems only pType field in HandlerType is used

          HandlerType* pTypeArray;

        };


    RTTI type descriptor. Describes a single C++ type. Used here to match the thrown exception type with catch type.

    struct TypeDescriptor {

      // vtable of type_info class

      const void * pVFTable;

     

      // used to keep the demangled name returned by type_info::name()

      void* spare;

     

      // mangled type name, e.g. ".H" = "int", ".?AUA@@" = "struct A", ".?AVA@@" = "class A"

      char name[0];

    };


    Unlike SEH, each try block doesn't have a single associated state value. The compiler changes the state value not only on entering/leaving a try block, but also for each constructed/destroyed object. That way it's possible to know which objects need unwinding when an exception happens. You can still recover try blocks boundaries by inspecting the associated state range and the addresses returned by catch handlers (see Appendix 2).

    Throwing C++ Exceptions


    throw statements are converted into calls of _CxxThrowException(), which actually raises a Win32 (SEH) exception with the code 0xE06D7363 ('msc'|0xE0000000). The custom parameters of the Win32 exception include pointers to the exception object and its ThrowInfo structure, using which the exception handler can match the thrown exception type against the types expected by catch handlers.

        struct ThrowInfo {

          // 0x01: const, 0x02: volatile

          DWORD attributes;

     

          // exception destructor

          void (*pmfnUnwind)();

     

          // forward compatibility handler

          int (*pForwardCompat)();

     

          // list of types that can catch this exception.

          // i.e. the actual type and all its ancestors.

          CatchableTypeArray* pCatchableTypeArray;

        };

     

        struct CatchableTypeArray {

          // number of entries in the following array

          int nCatchableTypes;

          CatchableType* arrayOfCatchableTypes[0];

        };


    Describes a type that can catch this exception.

        struct CatchableType {

          // 0x01: simple type (can be copied by memmove), 0x02: can be caught by reference only, 0x04: has virtual bases

          DWORD properties;

     

          // see above

          TypeDescriptor* pType;

     

          // how to cast the thrown object to this type

          PMD thisDisplacement;

     

          // object size

          int sizeOrOffset;

     

          // copy constructor address

          void (*copyFunction)();

        };

     

        // Pointer-to-member descriptor.

        struct PMD {

          // member offset

          int mdisp;

     

          // offset of the vbtable (-1 if not a virtual base)

          int pdisp;

     

          // offset to the displacement value inside the vbtable

          int vdisp;

        };


    We'll delve more into this in the next article.

    Prologs and Epilogs


    Instead of emitting the code for setting up the stack frame in the function body, the compiler might choose to call specific prolog and epilog functions instead. There are several variants, each used for specific function type:

    Name

    Type

    EH Cookie

    GS Cookie

    Catch Handlers

    _SEH_prolog/_SEH_epilog

    SEH3

    -

    -

     

    _SEH_prolog4/_SEH_epilog4 S

    EH4

    +

    -

     

    _SEH_prolog4_GS/_SEH_epilog4_GS

    SEH4

    +

    +

     

    _EH_prolog

    C++ EH

    -

    -

    +/-

    _EH_prolog3/_EH_epilog3

    C++ EH

    +

    -

    -

    _EH_prolog3_catch/_EH_epilog3

    C++ EH

    +

    -

    +

    _EH_prolog3_GS/_EH_epilog3_GS

    C++ EH

    +

    +

    -

    _EH_prolog3_catch_GS/_EH_epilog3_catch_GS

    C++ EH

    +

    +

    +

    SEH2


    Apparently was used by MSVC 1.XX (exported by crtdll.dll). Encountered in some old NT programs.

        ...

        Saved edi

        Saved esi

        Saved ebx

        Next SEH frame

        Current SEH handler (__except_handler2)

        Pointer to the scopetable

        Try level

        Saved ebp (of this function)

        Exception pointers

        Local variables

        Saved ESP

        Local variables

        Callee EBP

        Return address

        Function arguments

        ...

    Appendix I: Sample SEH Program


    Let's consider the following sample disassembly.

    func1           proc near

     

    _excCode        = dword ptr -28h

    buf             = byte ptr -24h

    _saved_esp      = dword ptr -18h

    _exception_info = dword ptr -14h

    _next           = dword ptr -10h

    _handler        = dword ptr -0Ch

    _scopetable     = dword ptr -8

    _trylevel       = dword ptr -4

    str             = dword ptr  8

     

      push    ebp

      mov     ebp, esp

      push    -1

      push    offset _func1_scopetable

      push    offset _except_handler3

      mov     eax, large fs:0

      push    eax

      mov     large fs:0, esp

      add     esp, -18h

      push    ebx

      push    esi

      push    edi

     

      ; --- end of prolog ---

     

      mov     [ebp+_trylevel], 0 ;trylevel -1 -> 0: beginning of try block 0

      mov     [ebp+_trylevel], 1 ;trylevel 0 -> 1: beginning of try block 1

      mov     large dword ptr ds:123, 456

      mov     [ebp+_trylevel], 0 ;trylevel 1 -> 0: end of try block 1

      jmp     short _endoftry1

     

    _func1_filter1:                         ; __except() filter of try block 1

      mov     ecx, [ebp+_exception_info]

      mov     edx, [ecx+EXCEPTION_POINTERS.ExceptionRecord]

      mov     eax, [edx+EXCEPTION_RECORD.ExceptionCode]

      mov     [ebp+_excCode], eax

      mov     ecx, [ebp+_excCode]

      xor     eax, eax

      cmp     ecx, EXCEPTION_ACCESS_VIOLATION

      setz    al

      retn

     

    _func1_handler1:                        ; beginning of handler for try block 1

      mov     esp, [ebp+_saved_esp]

      push    offset aAccessViolatio ; "Access violation"

      call    _printf

      add     esp, 4

      mov     [ebp+_trylevel], 0 ;trylevel 1 -> 0: end of try block 1

     

    _endoftry1:

      mov     edx, [ebp+str]

      push    edx

      lea     eax, [ebp+buf]

      push    eax

      call    _strcpy

      add     esp, 8

      mov     [ebp+_trylevel], -1 ; trylevel 0 -> -1: end of try block 0

      call    _func1_handler0     ; execute __finally of try block 0

      jmp     short _endoftry0

     

    _func1_handler0:                        ; __finally handler of try block 0

      push    offset aInFinally ; "in finally"

      call    _puts

      add     esp, 4

      retn

     

    _endoftry0:

      ; --- epilog ---

      mov     ecx, [ebp+_next]

      mov     large fs:0, ecx

      pop     edi

      pop     esi

      pop     ebx

      mov     esp, ebp

      pop     ebp

      retn

    func1           endp

     

    _func1_scopetable

      ;try block 0

      dd -1                      ;EnclosingLevel

      dd 0                       ;FilterFunc

      dd offset _func1_handler0  ;HandlerFunc

     

      ;try block 1

      dd 0                       ;EnclosingLevel

      dd offset _func1_filter1   ;FilterFunc

      dd offset _func1_handler1  ;HandlerFunc


    The try block 0 has no filter, therefore its handler is a __finally{} block. EnclosingLevel of try block 1 is 0, so it's placed inside try block 0. Considering this, we can try to reconstruct the function structure:

        void func1 (char* str)

        {

          char buf[12];

          __try // try block 0

          {

             __try // try block 1

             {

               *(int*)123=456;

             }

             __except(GetExceptCode() == EXCEPTION_ACCESS_VIOLATION)

             {

                printf("Access violation");

             }

             strcpy(buf,str);

          }

          __finally

          {

             puts("in finally");

          }

        }

     

    Appendix II: Sample Program with C++ Exceptions

     

    func1           proc near

     

    _a1             = dword ptr -24h

    _exc            = dword ptr -20h

    e               = dword ptr -1Ch

    a2              = dword ptr -18h

    a1              = dword ptr -14h

    _saved_esp      = dword ptr -10h

    _next           = dword ptr -0Ch

    _handler        = dword ptr -8

    _state          = dword ptr -4

     

      push    ebp

      mov     ebp, esp

      push    0FFFFFFFFh

      push    offset func1_ehhandler

      mov     eax, large fs:0

      push    eax

      mov     large fs:0, esp

      push    ecx

      sub     esp, 14h

      push    ebx

      push    esi

      push    edi

      mov     [ebp+_saved_esp], esp

     

      ; --- end of prolog ---

     

      lea     ecx, [ebp+a1]

      call    A::A(void)

      mov     [ebp+_state], 0          ; state -1 -> 0: a1 constructed

      mov     [ebp+a1], 1              ; a1.m1 = 1

      mov     byte ptr [ebp+_state], 1 ; state 0 -> 1: try {

      lea     ecx, [ebp+a2]

      call    A::A(void)

      mov     [ebp+_a1], eax

      mov     byte ptr [ebp+_state], 2 ; state 2: a2 constructed

      mov     [ebp+a2], 2              ; a2.m1 = 2

      mov     eax, [ebp+a1]

      cmp     eax, [ebp+a2]            ; a1.m1 == a2.m1?

      jnz     short loc_40109F

      mov     [ebp+_exc], offset aAbc  ; _exc = "abc"

      push    offset __TI1?PAD         ; char *

      lea     ecx, [ebp+_exc]

      push    ecx

      call    _CxxThrowException       ; throw "abc";

     

    loc_40109F:

      mov     byte ptr [ebp+_state], 1 ; state 2 -> 1: destruct a2

      lea     ecx, [ebp+a2]

      call    A::~A(void)

      jmp     short func1_try0end

     

    ; catch (char * e)

    func1_try0handler_pchar:

      mov     edx, [ebp+e]

      push    edx

      push    offset aCaughtS ; "Caught %s\n"

      call    ds:printf       ;

      add     esp, 8

      mov     eax, offset func1_try0end

      retn

     

    ; catch (...)

    func1_try0handler_ellipsis:

      push    offset aCaught___ ; "Caught ...\n"

      call    ds:printf

      add     esp, 4

      mov     eax, offset func1_try0end

      retn

     

    func1_try0end:

      mov     [ebp+_state], 0          ; state 1 -> 0: }//try

      push    offset aAfterTry ; "after try\n"

      call    ds:printf

      add     esp, 4

      mov     [ebp+_state], -1         ; state 0 -> -1: destruct a1

      lea     ecx, [ebp+a1]

      call    A::~A(void)

      ; --- epilog ---

      mov     ecx, [ebp+_next]

      mov     large fs:0, ecx

      pop     edi

      pop     esi

      pop     ebx

      mov     esp, ebp

      pop     ebp

      retn

    func1           endp

     

    func1_ehhandler proc near

      mov     eax, offset func1_funcinfo

      jmp     __CxxFrameHandler

    func1_ehhandler endp

     

    func1_funcinfo

      dd 19930520h            ; magicNumber

      dd 4                    ; maxState

      dd offset func1_unwindmap ; pUnwindMap

      dd 1                    ; nTryBlocks

      dd offset func1_trymap  ; pTryBlockMap

      dd 0                    ; nIPMapEntries

      dd 0                    ; pIPtoStateMap

      dd 0                    ; pESTypeList

     

    func1_unwindmap

      dd -1

      dd offset func1_unwind_1tobase ; action

      dd 0                    ; toState

      dd 0                    ; action

      dd 1                    ; toState

      dd offset func1_unwind_2to1 ; action

      dd 0                    ; toState

      dd 0                    ; action

     

    func1_trymap

      dd 1                    ; tryLow

      dd 2                    ; tryHigh

      dd 3                    ; catchHigh

      dd 2                    ; nCatches

      dd offset func1_tryhandlers_0 ; pHandlerArray

      dd 0

     

    func1_tryhandlers_0

    dd 0                    ; adjectives

    dd offset char * `RTTI Type Descriptor' ; pType

    dd -1Ch                 ; dispCatchObj

    dd offset func1_try0handler_pchar ; addressOfHandler

    dd 0                    ; adjectives

    dd 0                    ; pType

    dd 0                    ; dispCatchObj

    dd offset func1_try0handler_ellipsis ; addressOfHandler

     

    func1_unwind_1tobase proc near

    a1 = byte ptr -14h

      lea     ecx, [ebp+a1]

      call    A::~A(void)

      retn

    func1_unwind_1tobase endp

     

    func1_unwind_2to1 proc near

    a2 = byte ptr -18h

      lea     ecx, [ebp+a2]

      call    A::~A(void)

      retn

    func1_unwind_2to1 endp


    Let's see what we can find out here. The maxState field in FuncInfo structure is 4 which means we have four entries in the unwind map, from 0 to 3. Examining the map, we see that the following actions are executed during unwinding:

    • state 3 -> state 0 (no action)
    • state 2 -> state 1 (destruct a2)
    • state 1 -> state 0 (no action)
    • state 0 -> state -1 (destruct a1)

    Checking the try map, we can infer that states 1 and 2 correspond to the try block body and state 3 to the catch blocks bodies. Thus, change from state 0 to state 1 denotes the beginning of try block, and change from 1 to 0 its end. From the function code we can also see that -1 -> 0 is construction of a1, and 1 -> 2 is construction of a2. So the state diagram looks like this:


    Where did the arrow 1->3 come from? We cannot see it in the function code or FuncInfo structure since it's done by the exception handler. If an exception happens inside try block, the exception handler first unwinds the stack to the tryLow value (1 in our case) and then sets state value to tryHigh+1 (2+1=3) before calling the catch handler.

    The try block has two catch handlers. The first one has a catch type (char*) and gets the exception object on the stack (-1Ch = e). The second one has no type (i.e. ellipsis catch). Both handlers return the address where to resume execution, i.e. the position just after the try block. Now we can recover the function code:

        void func1 ()

        {

          A a1;

          a1.m1 = 1;

          try {

            A a2;

            a2.m1 = 2;

            if (a1.m1 == a1.m2) throw "abc";

          }

          catch(char* e)

          {

            printf("Caught %s\n",e);

          }

          catch(...)

          {

            printf("Caught ...\n");

          }

          printf("after try\n");

        }

     

    Appendix III: IDC Helper Scripts


    I wrote an IDC script to help with the reversing of MSVC programs. It scans the whole program for typical SEH/EH code sequences and comments all related structures and fields. Commented are stack variables, exception handlers, exception types and other. It also tries to fix function boundaries that are sometimes incorrectly determined by IDA. You can download it from MS SEH/EH Helper.

    Links and References


    [1] Matt Pietrek. A Crash Course on the Depths of Win32 Structured Exception Handling.
    http://www.microsoft.com/msj/0197/exception/exception.aspx
    Still THE definitive guide on the implementation of SEH in Win32.

    [2] Brandon Bray. Security Improvements to the Whidbey Compiler.
    http://blogs.msdn.com/branbray/archive/2003/11/11/51012.aspx
    Short description on changes in the stack layout for cookie checks.

    [3] Chris Brumme. The Exception Model.
    http://blogs.msdn.com/cbrumme/archive/2003/10/01/51524.aspx
    Mostly about .NET exceptions, but still contains a good deal of information about SEH and C++ exceptions.

    [4] Vishal Kochhar. How a C++ compiler implements exception handling.
    http://www.codeproject.com/cpp/exceptionhandler.asp
    An overview of C++ exceptions implementation.

    [5] Calling Standard for Alpha Systems. Chapter 5. Event Processing.
    http://www.cs.arizona.edu/computer.help/policy/DIGITAL_unix/AA-PY8AC-TET1_html/callCH5.html
    Win32 takes a lot from the way Alpha handles exceptions and this manual has a very detailed description on how it happens.

    Structure definitions and flag values were also recovered from the following sources:

    • VC8 CRT debug information (many structure definitions)
    • VC8 assembly output (/FAs)
    • VC8 WinCE CRT source


    Reversing Microsoft Visual C++ Part II: Classes, Methods and RTTI

     

     

    Abstract


    Microsoft Visual C++ is the most widely used compiler for Win32 so it is important for the Win32 reverser to be familiar with its inner working. Being able to recognize the compiler-generated glue code helps to quickly concentrate on the actual code written by the programmer. It also helps in recovering the high-level structure of the program.

    In part II of this 2-part article (see also: Part I: Exception Handling), I will cover how C++ machinery is implemented in MSVC, including classes layout, virtual functions, RTTI. Familiarity with basic C++ and assembly language is assumed.

    Basic Class Layout


    To illustrate the following material, let's consider this simple example:

        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();

        };


    In most cases MSVC lays out classes in the following order:

    • 1. Pointer to virtual functions table (_vtable_ or _vftable_), added only when the class has virtual methods and no suitable table from a base class can be reused.
    • 2. Base classes
    • 3. Class members

    Virtual function tables consist of addresses of virtual methods in the order of their first appearance. Addresses of overloaded functions replace addresses of functions from base classes.

    Thus, the layouts for our three classes will look like following:

         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


    The above diagram was produced by the VC8 compiler using an undocumented switch. To see the class layouts produced by the compiler, use: -d1reportSingleClassLayout to see the layout of a single class -d1reportAllClassLayout to see the layouts of all classes (including internal CRT classes) The layouts are dumped to stdout.

    As you can see, C has two vftables, since it has inherited two classes which both already had virtual functions. Address of C::A_virt2 replaces address of A::A_virt2 in C's vftable for A, and C::B_virt2 replaces B::B_virt2 in the other table.

    Calling Conventions and Class Methods


    All class methods in MSVC by default use _thiscall_ convention. Class instance address (_this_ pointer) is passed as a hidden parameter in the ecx register. In the method body the compiler usually tucks it away immediately in some other register (e.g. esi or edi) and/or stack variable. All further adressing of the class members is done through that register and/or variable. However, when implementing COM classes, _stdcall_ convention is used. The following is an overview of the various class method types.

    1) Static Methods
    Static methods do not need a class instance, so they work the same way as common functions. No _this_ pointer is passed to them. Thus it's not possible to reliably distinguish static methods from simple functions. Example:

     

        A::A_static1();

        call    A::A_static1


    2) Simple Methods
    Simple methods need a class instance, so _this_ pointer is passed to them as a hidden first parameter, usually using _thiscall_ convention, i.e. in _ecx_ register. When the base object is not situated at the beginning of the derived class, _this_ pointer needs to be adjusted to point to the actual beginning of the base subobject before calling the function. Example:

        ;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


    As you see, _this_ pointer is adjusted to point to the B subobject before calling B's method.

    3) Virtual Methods
    To call a virtual method the compiler first needs to fetch the function address from the _vftable_ and then call the function at that address same way as a simple method (i.e. passing _this_ pointer as an implicit parameter). Example:

        ;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) Constructors and Destructors
    Constructors and destructors work similar to a simple method: they get an implicit _this_ pointer as the first parameter (e.g. ecx in case of _thiscall_ convention). Constructor returns the _this_ pointer in eax, even though formally it has no return value.

    RTTI Implementation


    RTTI (Run-Time Type Identification) is special compiler-generated information which is used to support C++ operators like dynamic_cast<> and typeid(), and also for C++ exceptions. Due to its nature, RTTI is only required (and generated) for polymorphic classes, i.e. classes with virtual functions.

    MSVC compiler puts a pointer to the structure called "Complete Object Locator" just before the vftable. The structure is called so because it allows compiler to find the location of the complete object from a specific vftable pointer (since a class can have several of them). COL looks like following: 
     

    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

    };


    Class Hierarchy Descriptor describes the inheritance hierarchy of the class. It is shared by all COLs for a class.

    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;

    };


    Base Class Array describes all base classes together with information which allows compiler to cast the derived class to any of them during execution of the _dynamic_cast_ operator. Each entry (Base Class Descriptor) has the following structure:

    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

    };


    The PMD structure describes how a base class is placed inside the complete class. In the case of simple inheritance it is situated at a fixed offset from the start of object, and that value is the _mdisp_ field. If it's a virtual base, an additional offset needs to be fetched from the vbtable. Pseudo-code for adjusting _this_ pointer from derived class to a base class looks like the following:

        //char* pThis; struct PMD pmd;

        pThis+=pmd.mdisp;

        if (pmd.pdisp!=-1)

        {

          char *vbtable = pThis+pmd.pdisp;

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

        }


    For example, the RTTI hierarchy for our three classes looks like this:


    RTTI hierarchy for our example classes

     

    Extracting Information


    1) RTTI
    If present, RTTI is a valuable source of information for reversing. From RTTI it's possible to recover class names, inheritance hierarchy, and in some cases parts of the class layout. My RTTI scanner script shows most of that information. (see Appendix I)

    2) Static and Global Initializers
    Global and static objects need to be initialized before the main program starts. MSVC implements that by generating initializer funclets and putting their addresses in a table, which is processed during CRT startup by the _cinit function. The table usually resides in the beginning of .data section. A typical initializer looks like following:

        _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


    Thus, from this table way we can find out:

    • Global/static objects addresses
    • Their constructors
    • Their destructors

    See also MSVC _#pragma_ directive _init_seg_ [5].

    3) Unwind Funclets
    If any automatic objects are created in a function, VC++ compiler automatically generates exception handling structures which ensure deletion of those objects in case an exception happens. See Part I for a detailed description of C++ exception implementation. A typical unwind funclet destructs an object on the stack:

        unwind_1tobase:  ; state 1 -> -1

            lea     ecx, [ebp+a1]

            jmp     A::~A()


    By finding the opposite state change inside the function body or just the first access to the same stack variable, we can also find the constructor:

       

        lea     ecx, [ebp+a1]

        call    A::A()

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


    For the objects constructed using new() operator, the unwind funclet ensures deletion of allocated memory in case the constructor fails:

        unwind_0tobase: ; state 0 -> -1

            mov     eax, [ebp+pA1]

            push    eax

            call    operator delete(void *)

            pop     ecx

            retn


    In the function body:

        ;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


    Another type of unwind funclets is used in constructors and destructors. It ensures destruction of the class members in case of exception. In this case the funclets use the _this_ pointer, which is kept in a stack variable:

        unwind_2to1:

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

            add     ecx, 4Ch

            jmp     B1::~B1


    Here the funclet destructs a class member of type B1 at the offset 4Ch. Thus, from unwind funclets we can find out:

    • Stack variables representing C++ objects or pointers to objects allocated with _operator new_.
    • Their destructors
    • Their constructors
    • in case of new'ed objects, their size


    4) Constructors / Destructors Recursion
    This rule is simple: constructors call other constructors (of base classes and member variables) and destructors call other destructors. A typical constructor does the following:

    • Call constructors of the base classes.
    • Call constructors of complex class members.
    • Initialize vfptr(s) if the class has virtual functions
    • Execute the constructor body written by the programmer.

    Typical destructor works almost in the reverse order:

    • Initialize vfptr if the class has virtual functions
    • Execute the destructor body written by the programmer.
    • Call destructors of complex class members
    • Call destructors of base classes

    Another distinctive feature of destructors generated by MSVC is that their _state_ variable is usually initialized with the highest value and then gets decremented with each destructed subobject, which make their identification easier. Be aware that simple constructors/destructors are often inlined by MSVC. That's why you can often see the vftable pointer repeatedly reloaded with different pointers in the same function.

    5) Array Construction Destruction
    The MSVC compiler uses a helper function to construct and destroy an array of objects. Consider the following code:

        A* pA = new A[n];

       

        delete [] pA;


    It is translated into the following pseudocode: 
     

        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);


    If A has a vftable, a 'vector deleting destructor' is invoked instead when deleting the array:

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

        mov ecx, pA

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

        call A::'vector deleting destructor'


    If A's destructor is virtual, it's invoked virtually:

        mov ecx, pA

        push 3

        mov eax, [ecx] ;fetch vtable pointer

        call [eax]     ;call deleting destructor


    Consequently, from the vector constructor/destructor iterator calls we can determine:

    • addresses of arrays of objects
    • their constructors
    • their destructors
    • class sizes


    6) Deleting Destructors
    When class has a virtual destructor, compiler generates a helper function - deleting destructor. Its purpose is to make sure that a proper _operator delete_ gets called when destructing a class. Pseudo-code for a deleting destructor looks like following:

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

        {

          this->~A();

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

        };


    The address of this function is placed into the vftable instead of the destructor's address. This way, if another class overrides the virtual destructor, _operator delete_ of that class will be called. Though in real code _operator delete_ gets overriden quite rarely, so usually you see a call to the default delete(). Sometimes compiler can also generate a vector deleting destructor. Its code looks like this:

        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);

          }

        };


    I skipped most of the details on implementation of classes with virtual bases since they complicate things quite a bit and are rather rare in the real world. Please refer to the article by Jan Gray[1]. It's very detailed, if a bit heavy on Hungarian notation. The article [2] describes an example of the virtual inheritance implementation in MSVC. See also some of the MS patents [3] for more details.

    Appendix I: ms_rtti4.idc


    This is a script I wrote for parsing RTTI and vftables. You can download the scripts associated with both this article and the previous article from Microsoft VC++ Reversing Helpers. The script features:

    • Parses RTTI structures and renames vftables to use the corresponding class names.
    • For some simple cases, identifies and renames constructors and destructors.
    • Outputs a file with the list of all vftables with referencing functions and class hierarchy.


      Usage: after the initial analysis finishes, load ms_rtti4.idc. It will ask if you want to scan the exe for the vtables. Be aware that it can be a lengthy process. Even if you skip the scanning, you can still parse vtables manually. If you do choose to scan, the script will try to identify all vtables with RTII, rename them, and identify and rename constructors and destructors. In some cases it will fail, especially with virtual inheritance. After scanning, it will open the text file with results.

      After the script is loaded, you can use the following hotkeys to parse some of the MSVC structures manually:
    • Alt-F8 - parse a vtable. The cursor should be at the beginning of the vtable. If there is RTTI, the script will use the class name from it. If there is none, you can enter the class name manually and the script will rename the vtable. If there is a virtual destructor which it can identify, the script will rename it too.
    • Alt-F7 - parse FuncInfo. FuncInfo is the structure present in functions which have objects allocated on the stack or use exception handling. Its address is passed to _CxxFrameHandler in the function's exception handler:

    ·                    mov eax, offset FuncInfo1

    ·                    jmp _CxxFrameHandler

    In most cases it is identified and parsed automatically by IDA, but my script provides more information. You can also use ms_ehseh.idc from the first part of this article to parse all FuncInfos in the file.
    Use the hotkey with cursor placed on the start of the FuncInfo structure.

    • Alt-F9 - parse throw info. Throw info is a helper structure used by _CxxThrowException to implement the _throw_ operator. Its address is the second argument to _CxxThrowException:

    ·                          lea     ecx, [ebp+e]

    ·                          call    E::E()

    ·                          push    offset ThrowInfo_E

    ·                          lea     eax, [ebp+e]

    ·                          push    eax

    ·                          call    _CxxThrowException

     

    Use the hotkey with the cursor placed on the start of the throw info structure. The script will parse the structure and add a repeatable comment with the name of the thrown class. It will also identify and rename the exception's destructor and copy constructor.

    Appendix II: Practical Recovery of a Class Structure


    Our subject will be MSN Messenger 7.5 (msnmsgr.exe version 7.5.324.0, size 7094272). It makes heavy use of C++ and has plenty of RTTI for our purposes. Let's consider two vftables, at .0040EFD8 and .0040EFE0. The complete RTTI structures hierarchy for them looks like following:


    RTTI hierarchy for MSN Messenger 7.5


    So, these two vftables both belong to one class - CContentMenuItem. By checking its Base Class Descriptors we can see that:

    • CContentMenuItem contains three bases that follow it in the array - i.e. CDownloader, CNativeEventSink and CNativeEventSource.
    • CDownloader contains one base - CNativeEventSink.
    • Hence, CContentMenuItem inherits directly from CDownloader and CNativeEventSource, and CDownloader in turn inherits from CNativeEventSink.
    • CDownloader is situated in the beginning of the complete object, and CNativeEventSource is at the offset 0x24.


    So we can conclude that the first vftable lists methods of CNativeEventSource and the second one of either CDownloader or CNativeEventSink (if neither of them had virtual methods, CContentMenuItem would reuse the vftable of CNativeEventSource). Now let's check what refers to these tables. They both are referred by two functions, at .052B5E0 and .052B547. (That reinforces the fact that they both belong to one class.) Moreover, if we look at the beginning of the function at .052B547, we see the _state_ variable initialized with 6, which means that that function is the destructor. As a class can have only one destructor, we can conclude that .052B5E0 is its constructor. Let's looks closer at it:

    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


    The first thing compiler does after prolog is copying _this_ pointer from ecx to esi, so all further addressing is done based on esi. Before initializing vfptrs it calls two other functions; those must be constructors of the base classes - in our case CDownloader and CNativeEventSource. We can confirm that by going inside each of the functions - first one initializes its vfptr field with CDownloader::'vftable' and the second with CNativeEventSource::'vftable'. We can also investigate CDownloader's constructor further - it calls constructor of its base class, CNativeEventSink.

    Also, the _this_ pointer passed to the second function is taken from edi, which points to this+24h. According to our class structure diagram it's the location of the CNativeEventSource subobject. This is another confirmation that the second function being called is the constructor of CNativeEventSource.

    After calling base constructors, the vfptrs of the base objects are overwritten with CContentMenuItem's implementations - which means that CContentMenuItem overrides some of the virtual methods of the base classes (or adds its own). (If needed, we can compare the tables and check which pointers have been changed or added - those will be new implementations by CContentMenuItem.)

    Next we see several function calls to .04D8000 with _ecx_ set to this+4Ch to this+5Ch - apparently some member variables are initialized. How can we know whether that function is a compiler-generated constructor call or an initializer function written by the programmer? There are several hints that it's a constructor.

    • The function uses _thiscall_ convention and it is the first time these fields are accessed.
    • The fields are initialized in the order of increasing addresses.

    To be sure we can also check the unwind funclets in the destructor - there we can see the compiler-generated destructor calls for these member variables.

    This new class doesn't have virtual methods and thus no RTTI, so we don't know its real name. Let's name it RefCountedPtr. As we have already determined, 4D8000 is its constructor. The destructor we can find out from the CContentMenuItem destructor's unwind funclets - it's at 63CCB4.

    Going back to the CContentMenuItem constructor, we see three fields initialized with 0 and one with a vftable pointer. This looks like an inlined constructor for a member variable (not a base class, since a base class would be present in the inheritance tree). From the used vftable's RTTI we can see that it's an instance of CEventSinkList template.

    Now we can write a possible declaration for our class.

    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? */

    };


    We can't know for sure that the field at offset 48 is not a part of CNativeEventSource; but since it wasn't accessed in CNativeEventSource constructor, it is most probably a part of CContentMenuItem. The constructor listing with renamed methods and class structure applied:

    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

     

    Links and References


    [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



     

     

     

  • 相关阅读:
    SpringBoot RequestBody ajax提交对象
    微信小程序常用样式汇总
    微信小程序常用控件汇总
    java多线程实现多客户端socket通信
    客户端连接Codis集群
    crontab 解析
    在 RHEL/CentOS 7 上配置NTP时间服务器
    tomcat的bin目录中startup.bat/tomcat.6.exe/tomcat6w.exe区别
    Windows 下tomcat安装及将多个tomcat注册为Windows服务
    Oracle 数据库排错之 ORA-00600
  • 原文地址:https://www.cnblogs.com/sunkang/p/2038810.html
Copyright © 2011-2022 走看看