VB6 VTable Layout


Author: David Zimmer
Date: 09.26.22 - 10:06pm



to see the vtable layout of a vb6 COM object compile a native exe with debug symbols, in form load do something like
    Dim c As New Class1
    InputBox "", "", Hex(ObjPtr(c))
    End
attach ollydbg, in the bottom dump window right click view Long - as address then ctrl-g to go to the address copied from the inputbox. for me it was 4EB260
offset          value @ address       symbols resolved as 
-------------------------------------------------------
004EB260       00403354              OFFSET Project1.___vba@07F1B854
004EB264       00000000             (class based variables can be stored in memory below here)
follow that pointer to get to the vtable
00403354  6F2A306E  MSVBVM60.BASIC_CLASS_QueryInterface
00403358  6F2A2F45  MSVBVM60.Zombie_AddRef
0040335C  6F2A2D18  MSVBVM60.Zombie_Release
00403360  6F352FE1  MSVBVM60.BASIC_DISPINTERFACE_GetTICount
00403364  6F2E3ADC  MSVBVM60.BASIC_DISPINTERFACE_GetTypeInfo
00403368  6F2AF26C  MSVBVM60.BASIC_CLASS_GetIDsOfNames
0040336C  6F2AF141  MSVBVM60.BASIC_CLASS_Invoke
00403370  00401484  Project1.00401484             ;first user function on class (+0x1c)
00403374  00000000
00403378  00000000
403370 holds a pointer to code
offset          instruction byte dump        disassembly
--------------------------------------------------------
00401484   . E9 A7090000                   JMP Project1.Class1::x  (resolves to 0x401E30 could patch to hook here..)
actual function body
00401E30     55             PUSH EBP
00401E31   . 8BEC           MOV EBP,ESP
00401E33   . 83EC 0C        SUB ESP,0C
00401E36   . 68 F6104000    PUSH Project1.___vbaExceptHandler      
...
For a form the vtable is different such as:
Public a As Long

Private Sub Form_Load()
    a = &H11223344
    InputBox "", "", Hex(ObjPtr(Me))
    End
End Sub

Function x(a)
    DoEvents
End Function
ObjPtr(form)
$ ==>    >004032E4  OFFSET Project1.___vba@0763FC74
$+4      >00000000
$+8      >00689784
$+C      >02C0CA34
$+10     >02C0C9BC
$+14     >00000000
$+18     >00000000
$+1C     >6F2BAE80  MSVBVM60.6F2BAE80
$+20     >00000007
$+24     >00000001
$+28     >00000000
$+2C     >0000100F
$+30     >00000000
$+34     >11223344   <-- variable a

Code:

$4032E4 6F2A306E  MSVBVM60.BASIC_CLASS_QueryInterface
$+4      >6F2A2F45  MSVBVM60.Zombie_AddRef
$+8      >6F2A2D18  MSVBVM60.Zombie_Release
$+C      >6F352FE1  MSVBVM60.BASIC_DISPINTERFACE_GetTICount
$+10     >6F2E3ADC  MSVBVM60.BASIC_DISPINTERFACE_GetTypeInfo
$+14     >6F2AF26C  MSVBVM60.BASIC_CLASS_GetIDsOfNames
$+18     >6F2AF141  MSVBVM60.BASIC_CLASS_Invoke
$+1C     >6F3A6C14  MSVBVM60.6F3A6C14
...
$+6F8    >00401508  Project1.095D7F30    \__ get / let for public a as long 
$+6FC    >00401517  Project1.00401517    /
$+700    >0040153B  Project1.0040153B      ; 0040153B   . E9 40070000     JMP Project1.Form1::x
$+704    >0040152E  Project1.0040152E      ; 0040152E   . E9 4D050000     JMP Project1.Form1::Form_Load
$+708    >00000000
$+70C    >00000000
so vtable changes based on what kind of object it is with housekeeping stuff. (form vrs class) and what public variables you have plus methods implemented.

If you have a private variable in a form or class, and set its value in native code, the compiler will generate this:
0040199B  |. C746 34 443322>MOV DWORD PTR DS:[ESI+34],11223344
Where esi is the this pointer and local instance variables are stored at this+x with first slot being +34

If you instead declare the variable as
Public a As Long
a = &H11223344
You get the same thing in asm..However if you do
Public a As Long
me.a = &H11223344
Now COM comes into play and you get this..
00401A1B  |. 8B16           MOV EDX,DWORD PTR DS:[ESI]
00401A1D  |. 68 44332211    PUSH 11223344
00401A22  |. 56             PUSH ESI
00401A23  |. FF92 FC060000  CALL DWORD PTR DS:[EDX+6FC]  ;in a form so huge default vtable

0040149F   . 814424 04 3400>ADD DWORD PTR SS:[ESP+4],34  ;offset of instance var this+34 as above..
004014A7   . B9 60114000    MOV ECX,
004014AC   . FFE1           JMP ECX

00401160   .-FF25 2C104000  JMP DWORD PTR DS:[<&MSVBVM60.#307>]  ;  MSVBVM60.PutMem4

PutMem4
6F32EC11 > 8B4C24 04        MOV ECX,DWORD PTR SS:[ESP+4]
6F32EC15   8B4424 08        MOV EAX,DWORD PTR SS:[ESP+8]
6F32EC19   8901             MOV DWORD PTR DS:[ECX],EAX
6F32EC1B   33C0             XOR EAX,EAX
6F32EC1D   C2 0800          RETN 8

And the abbreviated vtable for the form
$ ==>    >6F23306E  MSVBVM60.BASIC_CLASS_QueryInterface
$+4      >6F232F45  MSVBVM60.Zombie_AddRef
$+8      >6F232D18  MSVBVM60.Zombie_Release
$+C      >6F2E2FE1  MSVBVM60.BASIC_DISPINTERFACE_GetTICount
$+10     >6F273ADC  MSVBVM60.BASIC_DISPINTERFACE_GetTypeInfo
$+14     >6F23F26C  MSVBVM60.BASIC_CLASS_GetIDsOfNames
$+18     >6F23F141  MSVBVM60.BASIC_CLASS_Invoke
$+1C     >6F336C14  MSVBVM60.6F336C14
...
$+6F8    >00401490  Project1.00401490  ;get a using GetMem4
$+6FC    >0040149F  Project1.0040149F  ;let a using PutMem4
$+700    >004014B6  Project1.004014B6  ;form_load
$+704    >00000000
And just to clarify objptr(me)
$ ==>    >004022F0  Project1.004022F0 ;points to vtable
$+4      >00000000
$+8      >005B5B0C  ;points to $+1C
$+C      >0227C9EC  -> 66068960  MSVBVM60.??_7CUnkDesk@@6B@
$+10     >0227C974  -> 0224C170 -> obj3
$+14     >00000000
$+18     >00000000
$+1C     >660682E0  MSVBVM60.??_7PRIVATE_UNKNOWN@BASIC_CLASS@@6B@
$+20     >00000006  ; com obj reference count (AddRef@CEnumConPnts)
$+24     >00000001
$+28     >00000000
$+2C     >0000100F
$+30     >00000000
$+34     >00000000  ;first user variable data slot

660682E0  >66007ED0  MSVBVM60.?QueryInterface@PRIVATE_UNKNOWN@BASIC_CLASS@@UAGJABU_GUID@@PAPAX@Z
$+4      >66001B4B  MSVBVM60.?AddRef@CEnumConPnts@@UAGKXZ
$+8      >66002F60  MSVBVM60.?Release@PRIVATE_UNKNOWN@BASIC_CLASS@@UAGKXZ
$+C      >660E2930  MSVBVM60.?GetClassInfoA@PRIVATE_UNKNOWN@BASIC_CLASS@@UAGJPAPAUITypeInfo@@@Z


obj3: 0224C170 66057CDE MSVBVM60.?QueryInterface@DESK@@UAGJABU_GUID@@PAPAX@Z 0224C174 66001B35 MSVBVM60.?AddRef@DESK@@UAGKXZ 0224C178 66001B1A MSVBVM60.?Release@DESK@@UAGKXZ 0224C17C 660E26F7 MSVBVM60.BASIC_DISPINTERFACE_GetTICount 0224C180 660B36C3 MSVBVM60.?GetTypeInfo@DESK@@UAGJIKPAPAUITypeInfo@@@Z 0224C184 6605E448 MSVBVM60.?GetIDsOfNames@DESK@@UAGJABU_GUID@@PAPAGIKPAJ@Z 0224C188 660579F3 MSVBVM60.?Invoke@DESK@@UAGJJABU_GUID@@KGPAUtagDISPPARAMS@@PAUtagVARIANT@@PAUtagEXCEPINFO@@PAI@Z 0224C18C 66019A57 MSVBVM60.?HctlDemandLoad@DESK@@UAEJPAPAUCTL@@@Z 0224C190 6609CF0C MSVBVM60.?SetPropA@CVBControlDefNoRefCount@@EAEJHTtagDATA@@@Z 0224C194 6609CF0C MSVBVM60.?SetPropA@CVBControlDefNoRefCount@@EAEJHTtagDATA@@@Z 0224C198 6609CF0C MSVBVM60.?SetPropA@CVBControlDefNoRefCount@@EAEJHTtagDATA@@@Z 0224C19C 6609CF0C MSVBVM60.?SetPropA@CVBControlDefNoRefCount@@EAEJHTtagDATA@@@Z 0224C1A0 6609CF0C MSVBVM60.?SetPropA@CVBControlDefNoRefCount@@EAEJHTtagDATA@@@Z 0224C1A4 6609CF0C MSVBVM60.?SetPropA@CVBControlDefNoRefCount@@EAEJHTtagDATA@@@Z 0224C1A8 660637C8 MSVBVM60.?GetPalette@CVBControlDefNoRefCount@@EAEPAUHPALETTE__@@XZ 0224C1AC 66048AC4 MSVBVM60.EbLibraryUnload 0224C1B0 007A5168 0224C1B4 007A5198 0224C1B8 007A51C8

So the objptr is a data block that actually contains pointers to several COM interfaces, along with the reference count and then local class variables, each stored per instance. The vtable itself is stored independently of the instance data makes sense.




Comments: (0)

 
Leave Comment:
Name:
Email: (not shown)
Message: (Required)
Math Question: 2 + 18 = ? followed by the letter: T 



About Me
More Blogs
Main Site
Posts: (All)
2022 (6)
     VB6 Implements
     vbdec remote scripting
     VB6 Stubs BS
     VB6 TypeInfo
     VB6 VTable Layout
     Yara isPCode rule
2021 (4)
     VB6 Hijacking
     rtcTypeName
     VB6 Gosub
     VB App object
2020 (5)
     AutoIT versions
     IDA JScript 2
     Using VB6 Obj files from C
     Yara Corrupt Imports
     Yara Undefined values
2019 ( 6 )
2017 ( 5 )
2016 ( 4 )
2015 ( 6 )
2014 ( 5 )
2013 ( 9 )
2012 ( 13 )
2011 ( 19 )
2010 ( 11 )
2009 ( 1 )