Author: David Zimmer
Date: 07.04.21 - 5:04pm
So a forum user wanted to devise a way to automate a VB6 application that did not have a plugin or scripting framework. He wanted to get a hold of the Vb.Global.Forms collection so that he could access the open forms and then automate them as if his dll was loaded as a plugin.
Another forum user knew where to find the cached Vb.Global pointer, and the following code ensued: (you can read the fill thread here)
Option Explicit 'add one listbox and two buttons Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long Private Declare Sub GetMem4 Lib "msvbvm60.dll" (ByVal lAddress As Long, var As Long) Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cBytes&) Private Type extEntry flag As Long offset As Long End Type Dim readyToReturn As Boolean Private Sub Command1_Click() readyToReturn = True End Sub Function IsIde() As Boolean On Error GoTo out Debug.Print 1 / 0 out: IsIde = Err End Function Private Sub Command2_Click() End End Sub Private Sub Form_Load() ' Dim v As VB.Global ' MsgBox v.App.EXEName 'variable not set..this is good, sanity check ' End If IsIde Then MsgBox "Only run compiled" End Else Me.Caption = "It worked!" Command1.Caption = "Continue" Command2.Caption = "End" List1.Move 0, 0, 8000, 8000 Me.Width = 8400 Me.Height = 9100 Command1.Move Me.Width - 3000, 8025 Command2.Move Me.Width - 1500, 8025 DoTheTrickStuff End If End Sub Sub xx(msg As String, v As Long, Optional wait As Boolean = False) List1.AddItem msg & ": " & Hex(v) & IIf(wait, " --- Waiting for continue click...", Empty) List1.Refresh DoEvents If wait Then readyToReturn = False While Not readyToReturn DoEvents Wend End If End Sub
find the VBHeader.aProjectInfo at offset 0x30 of the VBHeader
walk the VBHeader.aProjectInfo.aExternalTable (offset 0x234 in VBHeader.aProjectInfo struct) of which there will be VBHeader.aProjectInfo.ExternalCount entries (offset 0x238 from start of VBHeader.aProjectInfo )
each entry will be an 8 byte structure
private type extEntry flag as long offset as long end type
when you find flag = 6 you found your target. at that target.offset you will find another 8 byte structure
private type extEntryTarget clsidIIDStructOffset as long lpValue as long end type
you can verify the clsidIID values they are actually another struct 32 bytes consecutive of just use the hex dump of the guids the value of the actual vb.globals you want will be found at the lpValue offset.
I havent played with it as in injection yet, but its an interesting concept.
The Trick ended up posting a full example with injection here. (Local snapshot)