Author: David Zimmer
Date: 01.28.23 - 9:49pm
So i am looking at the Object.aPublicBytes structure. It contains type data that is used during tear down to free resources (ie prevent memory leaks)
The types of some kinds of (public and private) variables can be inferred from its information. bytes and longs etc will not have entries. Strings, objects, and arrays will have embedded information however.
Consider the following (in a module)
Private s As String Private d As Long Private c As New Collection Sub sets() s = "tesT" d = 3 c.Add s End Sub 4017B8 008 aPublicBytes 401914 4017C0 010 aModulePublic 402024 - data section 00402024 (+0) 004FF90C UNICODE "tesT" 00402028 (+4) 00000003 0040202C (+8) 004FF930 -> vtable for collection object .text:00401914 dw 14h |--;total data size - header .text:00401916 dd 10h | ;size in mem of live vars? .text:0040191A dd 2 | ;entries .text:0040191E dw 0 |__;requires alloc 660625AD .text:00401920 dw 0 ;data offset - variable data .text:00401922 dw 1 ;string .text:00401924 dw 8 ;data offset .text:00401926 dw 3 ;objectSo (for a module) aModulePublic points to the address in the data section where the private variables will be stored. aPublicBytes points to the structure describing the type bytes for the private vars.
Only 2 entries are represented because only two require cleanup.
We can infer there are intrinsic types since the data offset for object 2 is +8 bytes in.
type bytes: 1 = string 3 = object 2 = variant 0x105 = array type followsThings get complicated once you get into an array, and crazy if you get an array of UDT types.
Private Type x a As Long b As String End Type Dim o() As x .text:00401694 word_401694 dw 10h .text:00401696 dw 8 .text:00401698 dw 0 .text:0040169A dw 1 .text:0040169C dw 8 .text:0040169E dw 2C04h .text:004016A0 dw 4 .text:004016A2 dw 1 .text:004016A4 Module1_PubBytes dw 2Ch .text:004016A6 dw 8 .text:004016A8 dw 0 .text:004016AA dw 1 .text:004016AC dw 0 .text:004016AE dw 1000h .text:004016B0 dw 0 .text:004016B2 dw 2105h .text:004016B4 dw 0 .text:004016B6 dw 0FFFFh .text:004016B8 dw 2069h .text:004016BA db 0 .text:004016BB db 0 .text:004016BC dd offset word_401694 IRecordInfo Desc? .text:004016C0 db 0 .text:004016C1 db 0 .text:004016C2 db 0 .text:004016C3 db 0 .text:004016C4 db 0 .text:004016C5 db 0 .text:004016C6 db 0 .text:004016C7 db 0 .text:004016C8 db 0 .text:004016C9 db 0 .text:004016CA db 0 .text:004016CB db 0 .text:004016CC db 0 .text:004016CD db 0 .text:004016CE db 0 .text:004016CF db 0I did some debugging on this just to verify its usage. For more refer to
'RESDESCTB - Resource Descriptor Table? .text:66061E42 ; void __thiscall RESDESCTBL::DestructItem( RESDESCTBL *this, void *aModulePublic, struct RESDESC *a3, unsigned int *a4 )There is also a RESDESCTBL::ConstructItem which is probably used for initialization and allocation of some types such as say Private x(5) As Long. So there will be rich type information in there which makes this more delicious
Right now I am mostly looking at data variations from compiling different combinations and looking for data patterns since its quicker. An array of UDT where the UDT does not require any cleanup of its own is easy
Private Type x a As Long b As Long End Type Dim o() As x .text:004016A0 dw 16h .text:004016A2 dw 8 .text:004016A4 dw 0 .text:004016A6 dw 1 .text:004016A8 dw 0 .text:004016AA dw 0 .text:004016AC dw 0 .text:004016AE dw 105h .text:004016B0 dw 0 .text:004016B2 dw 0 .text:004016B4 dw 0 .text:004016B6 dw 0We already list public variables from entries from the IDispatch implementation, so we would have to ignore some of these selectively as duplicates as well...
It is enticing though to extract var types and data offsets for listing in the UI and potentially extracting for the live class instances form. We can not know the scoping (private/public/global) since the compiler enforces that. If we have a live instance running, I can call TypeName() remotely for object types.
Not sure if I care enough to handle the crazy UDT cases but it looks like I already have enough info to extract the other bits reliably...
I know I should probably pick a more relevant target for research, I am basically an archaeologist at this point. But there are still some interesting things to squeeze out of the runtime and it is a good puzzle so...I basically wanted this for 20yrs and once I move on to the next project this info will go stale in my mind so I guess its now or never.
Another example of an array to construct:
Dim x(&H11223344 To &H22334455) As Integer '11111112 elements .text:0040165C dw 38h .text:0040165E dw 20h .text:00401660 dw 1 ; 660625AD cmp [esi+6], ax .text:00401662 dw 1 .text:00401664 dw 0 .text:00401666 dw 0 ; RescDesctbl ends here .text:00401668 dw 4 ; 660625B1 lea edi, [esi+0Ch] .text:0040166A dw 5 ; 6605606D MOV AX,WORD PTR DS:[ESI+2] .text:0040166C dw 0 .text:0040166E dw 0 .text:00401670 dw 0 ; 660560F2 TEST BYTE PTR DS:[ECX+8],60 .text:00401672 dw 0 .text:00401674 dw 0 .text:00401676 dw 0 ;end of struct 2 (based on ..x670 flag) .text:00401678 dw 1 dims - raw safe array struct .text:0040167A dw 92h features? FADF_HAVEVARTYPE | FADF_FIXEDSIZE | FADF_STATIC ? 660560B2 MOV AX,WORD PTR DS:[EDI+2] .text:0040167C dw 2 \_ element size - 4 for long .text:0040167E dw 0 / .text:00401680 dw 0 \_locks .text:00401682 dw 0 / .text:00401684 dw 0 \_pvdata .text:00401686 dw 0 / .text:00401688 dd 11111112h ;Elements count (SafeArrayBound struct) .text:0040168C dd 11223344h ;lbound .text:00401690 dw 2 VT_i2 - 3: VT_i4 if long .text:00401692 dw 0 Private Type SafeArray cDims As Integer fFeatures As Integer cbElements As Long cLocks As Long pvData As Long End Type Private Type SAFEARRAYBOUND 'directly follows SafeArray struct, one per dimension cElements As Long lLbound As Long End Type https://learn.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-safearray