-
Jun 30th, 2021, 03:24 PM
#1
Thread Starter
New Member
Injecting a VB6 dll and manipulating the UI
Hi guys.
I am new here. I'll try to keep it simple.
Mission: Manipulate a legacy VB6 app UI.
The strategy:
1. Create a VB6 ActiveXDll
2. inject it into the process using SetWindowHookex
3. set a timer for handling execution outside the handler itself
4. Do as I please with the UI
I have reached step 4 after much effort, Yet I find no way to access the forms.
I have read that the 'Forms' collection is not accessible via activex dll. as a matter of fact - the entire VB.Global is not accessible.
So my friends - any VB6 guru here can show me the way?
Given that I have an dll that is running - is it possible for me to gain access to the open forms?
(I thought about obtaining the IAccessible pointer and then try to manipulate it into a form "pointer" yet I find no clues for that, nor do I know if the form class itself is COM accessible, or what is it's COM GUID if any.)
Thank you!
-
Jun 30th, 2021, 04:43 PM
#2
Re: Injecting a VB6 dll and manipulating the UI
Do you have the source code for the VB6 app? I assume you don't, but you didn't say. Is the point the manipulation, or is the point the ActiveXDll? It seems like it would be the former via the latter, in which case the latter may be irrelevant, and it may be easier to do things with the form without the ActiveX approach, so the objective is kind of important rather than the particular mechanism used. Finally, what kind of manipulation? Some are much easier than others, and have simpler means available.
My usual boring signature: Nothing
-
Jun 30th, 2021, 06:32 PM
#3
Re: Injecting a VB6 dll and manipulating the UI
the standard approach is to use the FindWindow API, enumerate children handles, then sendwindow message to do stuff.
here are some scrapers: https://github.com/dzzie/libs/blob/m...indow.cls#L195
Code:
Function CopyListBox(Optional toListBox As Object) As Collection
Function CopyComboBox(Optional toCombo As Object) As Collection
Function CopyConsole(Optional hwnd As Long) As String
Function IEDOMFromhWnd() As Object 'HTMLDocument
Function FindChild(ByVal className As String) As Cwindow
Function CopyRemoteListView(Optional destLV As Object, Optional colSeperator As String = vbTab) As Collection
Function CopyRemoteTreeView(Optional destTV As Object) As Collection
Something like
Code:
Dim c2 As Collection, wTv As Cwindow, tmp As String
Dim c As Collection, w As Cwindow, kanal as CWindow
Set c = ChildWindows()
For Each w In c
If VBA.Left(w.Caption, 5) = "KANAL" Then
'Debug.Print Now & " - found kanal window attaching to " & w.hwnd & " (" & Hex(w.hwnd) & " )"
Set kanal = w
If kanal.isValid Then
Set wTv = kanal.FindChild("SysTreeView32")
If wTv.isValid Then
Set c2 = wTv.CopyRemoteTreeView()
tmp = ColToStr(c2)
Clipboard.Clear
Clipboard.SetText tmp
This example isnt exactly the same, but is as close as I have. Adds new functionality to an existing C form using vb6:
http://sandsprite.com/blogs/index.ph...=407&year=2016
overview:
- load a c dll which is a plugin for another app and launch it as if we were the normal parent app
- modify the plugins res file form definition to add a button
- subclass the window looking for button clicks
- on click of new button find the tree view on the form
- copy its contents to clipboard
enumerating windows and sending messages or sendkeys can do a lot without having to be in process.
if you have to subclass controls then you will want to be in process. I wouldnt really do it in vb6 though.
I have never explored trying to access the main exe COM objects from within an injection only from legit plugins
started by the host app. It would be interesting, I am sure there is a way to do it, but you will have to reach into the
runtime itself to find pointers to live com objects. offsets will vary based on runtime version number
Last edited by dz32; Jun 30th, 2021 at 06:39 PM.
-
Jul 1st, 2021, 08:39 AM
#4
Thread Starter
New Member
Re: Injecting a VB6 dll and manipulating the UI
Wow guys...It was a shot in the dark and here I get people to converse with.
I was so lonely, so thank you so much for your answers!
back to business
"Do you have the source code for the VB6 App?"
you assume correctly. I have not.
"Is the point the manipulation, or is the point the ActiveXDll?"
The manipulation. I ellaborated on the mechanism just to give some context and to show I have put the effort before I reached out.
"Finally, what kind of manipulation?"
I am developing a 3rd party "addon" to a legacy app which is supposed to allow "enhancement".
The code itself cannot be changed, but there are many processes that must be changed to allow incorporation of external data.
first off, there is the issue of reading\extracting data from controls.
Of course for simple controls, there are other ways to go (UIA, Accessibility, Win32 etc.)
BUT - for more complex third party controls (like MSFlexGrid) reading data is much more complex.
Things get interesting (nice word for hard) when I want to write back data into the program.
using methods such as RPA for clicks just doesn't cut it. nor does subclassing.
I wish to have true access to the components, and have all the VB6 capabilities at my disposal.
when I think about it : It goes even beyond "GUI" manipulation.
There are places where I want to completely ovveride the behaviour, and instead of an old form opening, being filled, and then the resulting object is passed downstream,
I want to hijack the form even being opened, create my own data object based on external data, and pass THAT downstream of the flow.
And if I may revisit part my question again - assuming I am inside the app with a DLL, how can I grab hold of any pointer to an open form, or even all the open forms.
Regarding dz32 super details answer - this approach is a "fallback" for me and will leave me with limited functionality I believe.
because I intend on using VB6 decompiler to locate the methods I wish (dream?) to invoke public method of forms to hijack the flow and insert my own data.
Thank you guys.
This kind of help is not to be taken for granted.
-
Jul 1st, 2021, 12:00 PM
#5
Re: Injecting a VB6 dll and manipulating the UI
how can I grab hold of any pointer to an open form, or even all the open forms.
The simpliest way is to access the VB.Global.Forms collection of a project and then get all the forms. To get VB.Global object you can inspect VBHeader.lpProjectData->lpExternalTable items and search for items with the tag (at 0 offset) equals to 6. The item value (at 4 offset) is a pointer to an object descriptor which consists of a pointer to the object CLSID (at zero offset) and pointer to the Global object itself (at 4 offset).
So you can extract this pointer and call AddRef method then you can cast this pointer to a VB.Global object variable. From this moment, the project methods and properties (like App/Screen/Forms etc.) are available to you.
-
Jul 1st, 2021, 02:45 PM
#6
Thread Starter
New Member
Re: Injecting a VB6 dll and manipulating the UI
WOW...
Will do.
I started implementing the code as best as I can, following your instructions.
I have a problem following this one:
" search for items with the tag (at 0 offset) equals to 6"
I was unable to find in the forums or in google the structure of such "items" so I do not know how to iterate over them.
can you direct me to the structure? if not, what size is it so I can iterate it?
Here is what I got so far:
Code:
Private Sub Form_Load()
Dim vbHeader As Long
vbHeader = GetVBHeader()
MsgBox CStr(vbHeader)
Dim lpProjectData As Long
lpProjectData = vbHeader + &H30
Dim lpExternalTable As Long
lpExternalTable = lpProjectData + &H234
'What next???
End Sub
' // Get VBHeader structure
Private Function GetVBHeader() As Long
Dim ptr As Long
Dim hModule As Long
hModule = GetModuleHandle(ByVal "Project1.exe")
' Get e_lfanew
GetMem4 ByVal hModule + &H3C, ptr
' Get AddressOfEntryPoint
GetMem4 ByVal ptr + &H28 + hModule, ptr
' Get VBHeader
GetMem4 ByVal ptr + hModule + 1, GetVBHeader
End Function
I will be so happy if it works.
Thank you thank you thank you.
-
Jul 1st, 2021, 02:52 PM
#7
Re: Injecting a VB6 dll and manipulating the UI
Okay. I'll prepare small example for you. BTW, what's the method of creating native dll you use?
-
Jul 1st, 2021, 03:43 PM
#8
Thread Starter
New Member
Re: Injecting a VB6 dll and manipulating the UI
ohhh man. I have been programing for 15 years. I never reached out to a community like this.
I feel like I missed so much. this is a game changer for me.
I will describe my approach for injecting the dll.
not sure it will go to production, but this is what I use now for closing a loop and making sure all the parts work.
1. Created a C++ (using VS2019) native dll. lets call it NativeHook.dll
2. Created a C# project that calls SetWindowsHookEx for WM_GETMESSAGE, and using the NativeHook.dll as it's module. the target process is the VB6 App.
3. once the NativeHook.dll is loaded, it loads a VB6 activeX dll (using LoadLibrary), and invokes a function in it. lets call it InjectedVb.dll.
4. the invoked function in InjectedVb.dll starts a timer to postpone the initialization of the stuff needed later, so not to hold the msg queue for too long.
5. once the timer is raised, I have my own nice VB6 code that runs in the same thread of the VB6 App ui thread, with a runtime that looks well alive.
that code in the injected dll will soon be replaced with your code and let the magic begin!!!!!!!!
Last edited by Broder; Jul 1st, 2021 at 03:47 PM.
-
Jul 1st, 2021, 04:15 PM
#9
Re: Injecting a VB6 dll and manipulating the UI
Wouldn't it be far easier just to rewrite the program from scratch?
-
Jul 3rd, 2021, 06:43 AM
#10
Re: Injecting a VB6 dll and manipulating the UI
so I played around a bit with this based on the tips from trick. I will describe the complete process.
first I made an exe which set a textbox to hex(objptr(vb.global)) so I knew what value I should be searching for.
then I opened that exe up in vbdec to look at the vb headers and offsets.
then I attached ollydbg to the process and started probing those offsets
I ended up finding the path trick mentioned to the vb.global pointer. set a memory write breakpoint on that offset and reloaded.
its set in MSVBVM60.6601802F 8908 MOV DWORD PTR DS:[EAX],ECX which is _TipRegAppObject
(vbruntime: EEBEB73979D0AD3C74B248EBF1B6E770 with symbols: http://sandsprite.com/vb-reversing/files/msvbvm60.zip )
Then I loaded up a complex project set a breakpoint at my runtime offset (6601802F) to catch the value it will write
for me it was (These offsets are relative to my exe but included for clarity)
offset to write to: 0047D11C (eax)
value (ecx): 02BF1294 -> 660130D0 MSVBVM60.??_7CVBApplication@@6BVBGlobal@@@
Next I examine the structures in vbdec to get offsets to walk in olly
Code:
VB Header
VA rel offset name value
4048A8 0x30 aProjectInfo 40540C
VB Header.aProjectInfo @40540C
405640 0x234 aExternalTable 404FF8
405644 0x238 ExternalCount 3E
at 404FF8 I find data such as
00404FF8 00000007
00404FFC 00429470 PDFStrea.00429470
00405000 00000007
00405004 0042942C PDFStrea.0042942C
00405008 00000007
0040500C 004293E8 PDFStrea.004293E8
... many more entries ....
004051A8 00000006 <--- flag we are looking for
004051AC 0041CC7C PDFStrea.0041CC7C <---
Looking at 0041CC7C
0041CC7C 0041C95C PDFStrea.0041C95C -> offset + 0 points to clsid/IID
0041CC80 0047D11C PDFStrea.0047D11C <-- offset+ 4 value we saw written to in _TipRegAppObject
looking at 47D11C
0047D11C = 02BF1294 -> 660130D0 MSVBVM60.??_7CVBApplication@@6BVBGlobal@@@
Then from there you can work with the vb.global object (sorry didnt switch to relative offsets before closing process)
660130D0 >660E2074 MSVBVM60.?QueryInterface@CVBApplication@@UAGJABU_GUID@@PAPAX@Z
660130D4 6601808A MSVBVM60.?AddRef@CVBApplication@@UAGKXZ
660130D8 66028553 MSVBVM60.?Release@CVBApplication@@UAGKXZ
660130DC 6605CE6B MSVBVM60.?Load@CVBApplication@@UAGJPAUIDispatch@@@Z
660130E0 6605CEE2 MSVBVM60.?Unload@CVBApplication@@UAGJPAUIDispatch@@@Z
660130E4 66026F9D MSVBVM60.?get_App@CVBApplication@@UAGJPAPAV_AppObject@@@Z
660130E8 6603D750 MSVBVM60.?get_Screen@CVBApplication@@UAGJPAPAV_ScreenObject@@@Z
660130EC 660489C7 MSVBVM60.?get_Clipboard@CVBApplication@@UAGJPAPAV_ClipboardObject@@@Z
660130F0 660E1EC6 MSVBVM60.?get_Printer@CVBApplication@@UAGJPAPAV_PrinterObject@@@Z
660130F4 660E1EF8 MSVBVM60.?putref_Printer@CVBApplication@@UAGJPAV_PrinterObject@@@Z
660130F8 66048EAE MSVBVM60.?get_Forms@CVBApplication@@UAGJPAPAUIDispatch@@@Z
660130FC 660E1EE0 MSVBVM60.?get_Printers@CVBApplication@@UAGJPAPAUIDispatch@@@Z
66013100 660E1FA0 MSVBVM60.?LoadResStringOld@CVBApplication@@UAGJFPAPAG@Z
66013104 6604A7C3 MSVBVM60.?LoadResPicture@CVBApplication@@UAGJUtagVARIANT@@FPAPAUIPictureDisp@@@Z
66013108 6604AB06 MSVBVM60.?LoadResData@CVBApplication@@UAGJUtagVARIANT@@0PAU2@@Z
6601310C 660E1FD9 MSVBVM60.?LoadPictureOld@CVBApplication@@UAGJUtagVARIANT@@PAPAUIPictureDisp@@@Z
66013110 6604776E MSVBVM60.?SavePicture@CVBApplication@@UAGJPAUIPictureDisp@@PAG@Z
66013114 6602C9D8 MSVBVM60.?LoadPicture@CVBApplication@@UAGJUtagVARIANT@@0000PAPAUIPictureDisp@@@Z
66013118 6604A683 MSVBVM60.?LoadResString@CVBApplication@@UAGJJPAPAG@Z
6601311C 660E1F71 MSVBVM60.?get_Licenses@CVBApplication@@UAGJPAPAULicenses@@@Z
0041C95C 23 3D FB FC FA A0 68 10 A7 38 08 00 2B 33 71 B5 = {FCFB3D23-A0FA-1068-A738-08002B3371B5} = VBEGlobal Clsid
0041C96C 22 3D FB FC FA A0 68 10 A7 38 08 00 2B 33 71 B5 = {FCFB3D22-A0FA-1068-A738-08002B3371B5} = VBGlobal IID
Misc notes:
Since you are working with a static exe that wont be recompiled, your VB Header.aProjectInfo.aExternalTable offset and count will be static
VbHeader.aExternalComponentTable was = VB Header.aProjectInfo.aExternalTable for a small native exe, but not for a large pcode exe
so I would stick with the latter
In the large exe my runtime breakpoint hit twice, once for the exe, and once again for another vb6 ocx control it loaded. values written were different
in my test exe I could not set tmp as object = vb.global got type mismatch
Last edited by dz32; Jul 3rd, 2021 at 11:21 AM.
-
Jul 3rd, 2021, 01:00 PM
#11
Thread Starter
New Member
Re: Injecting a VB6 dll and manipulating the UI
Originally Posted by dilettante
Wouldn't it be far easier just to rewrite the program from scratch?
only in a utopia
Not a possibility. 3rd party App.
-
Jul 3rd, 2021, 01:06 PM
#12
Thread Starter
New Member
Re: Injecting a VB6 dll and manipulating the UI
Originally Posted by dz32
so I played around a bit with this based on the tips from trick. I will describe the complete process...
Thanks man.
To be honest - I need to catch up with some material in order to reproduce the process you laid out.
What Trick suggested I understand better, plus it seems to be more dynamic in the sense that it will still work in the presence of binary changes - and that is a must.
Although the App is legacy, it has several versions out there in the ether AND once in a while it gets a tiny update, while moving parts of it to web wrappers.
-
Jul 3rd, 2021, 03:27 PM
#13
Re: Injecting a VB6 dll and manipulating the UI
the test app, debugger and runtime breakpoint stuff was just to validate the results the rest is exactly as trick outlined.
I only included it so you can validate your results as well.
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.
-
Jul 3rd, 2021, 03:46 PM
#14
Re: Injecting a VB6 dll and manipulating the UI
and you can test your lookup code in your own compiled test executable comparing the final value to objptr(vb.global) before you attempt to do it from an injection
-
Jul 3rd, 2021, 04:02 PM
#15
Thread Starter
New Member
Re: Injecting a VB6 dll and manipulating the UI
followed your advice with AWE.
but I am a newbie here. what am I missing?
code attached:
Code:
Option Explicit
Dim WithEvents btn As CommandButton
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 Sub Form_Load()
DoTheTrickStuff
End Sub
Private Sub DoTheTrickStuff()
MsgBox "The pointer I am after :" + CStr(ObjPtr(VB.Global))
Dim vbHeader As Long
vbHeader = GetVBHeader()
MsgBox "Header is at : " + CStr(vbHeader)
Dim lpProjectData As Long
lpProjectData = vbHeader + &H30
Dim projectDataPointerValue As Long
GetMem4 ByVal lpProjectData, projectDataPointerValue
Dim lpExternalTable As Long
lpExternalTable = projectDataPointerValue + &H234
Dim lpExternalcount As Long
lpExternalcount = projectDataPointerValue + &H238
Dim externalCount As Long
GetMem4 ByVal lpExternalcount, externalCount
Dim externalTablePointerValue As Long
GetMem4 ByVal lpExternalTable, externalTablePointerValue
Dim tag As Long
Dim index As Integer
Dim pExtEntryTarget As Long
For index = 0 To externalCount
GetMem4 ByVal externalTablePointerValue + (index * 8), tag
GetMem4 ByVal externalTablePointerValue + (index * 8) + 4, pExtEntryTarget
If tag = 6 Then 'Tag of global object
MsgBox "Found Tag=6"
Dim globalPtr As Long
GetMem4 ByVal pExtEntryTarget + 4, globalPtr
MsgBox "Global Pointer = " + CStr(globalPtr)
Dim globalClone As VB.Global
Set globalClone = GlobalFromPointer(ByVal globalPtr)
MsgBox "Got back the object. will it work?"
MsgBox "WELL WELL WELL : " + globalClone.Forms(0).Caption 'this crashes!
End If
Next
End Sub
Private Function GlobalFromPointer(ByVal ptr&) As VB.Global
' this function returns a reference to our form class
' dimension an object variable that we can copy the
' passed pointer into
Dim globalClone As VB.Global
' use the CopyMemory API function to copy the
' long pointer into the object variable.
CopyMemory globalClone, ptr, 4&
Set GlobalFromPointer = globalClone
CopyMemory ptr&, 0&, 4&
End Function
Public Function ObjFromPtr(ByVal pObj As Long) As Object
Dim obj As Object
CopyMemory obj, pObj, LenB(obj) 'not size 4!
Set ObjFromPtr = obj
CopyMemory obj, 0&, LenB(obj) 'not size 4!
End Function
' // Get VBHeader structure
Private Function GetVBHeader() As Long
Dim ptr As Long
Dim hModule As Long
hModule = GetModuleHandle(ByVal "Project1.exe")
' Get e_lfanew
GetMem4 ByVal hModule + &H3C, ptr
' Get AddressOfEntryPoint
GetMem4 ByVal ptr + &H28 + hModule, ptr
' Get VBHeader
GetMem4 ByVal ptr + hModule + 1, GetVBHeader
End Function
-
Jul 3rd, 2021, 04:03 PM
#16
Thread Starter
New Member
Re: Injecting a VB6 dll and manipulating the UI
The pointer I get back is not equal to the value I get from ObjPtr(VB.Global)
no wonder it crashes...
-
Jul 3rd, 2021, 05:17 PM
#17
Re: Injecting a VB6 dll and manipulating the UI
add a listbox and 2 command buttons to your form
fully working
Code:
Option Explicit
Dim WithEvents btn As CommandButton
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
Private Type extEntryTarget
clsidIIDStructOffset As Long
lpValue 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
Private Sub DoTheTrickStuff()
Dim vbHeader As Long, lpProjectData As Long, projectDataPointerValue As Long
Dim lpExternalTable As Long, externalTablePointerValue As Long
Dim externalCount As Long, lpExternalCount As Long
Dim i As Integer, e As extEntry, myCaption As String
Dim globalPtr As Long, lpGlobalPtr As Long, globalClone As VB.Global
Me.Visible = True
xx "The pointer I am after", ObjPtr(VB.Global)
vbHeader = GetVBHeader()
xx "Header", vbHeader
lpProjectData = vbHeader + &H30
GetMem4 ByVal lpProjectData, projectDataPointerValue
xx "lpProject", projectDataPointerValue
lpExternalCount = projectDataPointerValue + &H238
xx "lpExternalCount", lpExternalCount
GetMem4 ByVal lpExternalCount, externalCount
xx "ExtCount", externalCount
lpExternalTable = projectDataPointerValue + &H234
xx "lpExtTable", lpExternalTable
GetMem4 ByVal lpExternalTable, externalTablePointerValue
xx "ExtTable", externalTablePointerValue
For i = 0 To externalCount
CopyMemory ByVal VarPtr(e), ByVal externalTablePointerValue, 8
Debug.Print i & ") " & Hex(externalTablePointerValue) & " " & e.flag & " " & Hex(e.offset)
If e.flag = 6 Then 'Tag of global object
xx "Found flag", e.flag
GetMem4 ByVal e.offset + 4, lpGlobalPtr
xx "lpGlobalPointer", lpGlobalPtr
GetMem4 ByVal lpGlobalPtr, globalPtr
xx "GlobalPointer", globalPtr
If globalPtr = ObjPtr(VB.Global) Then
List1.AddItem "Success!!"
Else
List1.AddItem "FAIL!!"
End If
Set globalClone = GlobalFromPointer(ByVal globalPtr)
xx "ObjPtr(globalClone)", ObjPtr(globalClone) ', True
myCaption = globalClone.Forms(0).Caption
List1.AddItem "globalClone.Forms(0).Caption = " & myCaption
globalClone.Forms(0).Caption = "Are you sure?!"
Exit For
End If
externalTablePointerValue = externalTablePointerValue + 8
Next
List1.AddItem "Complete"
End Sub
Private Function GlobalFromPointer(ByVal ptr&) As VB.Global
' this function returns a reference to our form class
' dimension an object variable that we can copy the
' passed pointer into
Dim globalClone As VB.Global
' use the CopyMemory API function to copy the
' long pointer into the object variable.
CopyMemory globalClone, ptr, 4&
Set GlobalFromPointer = globalClone
CopyMemory ptr&, 0&, 4&
End Function
Public Function ObjFromPtr(ByVal pObj As Long) As Object
Dim obj As Object
CopyMemory obj, pObj, LenB(obj) 'not size 4!
Set ObjFromPtr = obj
CopyMemory obj, 0&, LenB(obj) 'not size 4!
End Function
' // Get VBHeader structure
Private Function GetVBHeader() As Long
Dim ptr As Long
Dim hModule As Long
hModule = GetModuleHandle(ByVal "Project1.exe")
' Get e_lfanew
GetMem4 ByVal hModule + &H3C, ptr
' Get AddressOfEntryPoint
GetMem4 ByVal ptr + &H28 + hModule, ptr
' Get VBHeader
GetMem4 ByVal ptr + hModule + 1, GetVBHeader
End Function
Private Sub Form_Resize()
On Error Resume Next
List1.Width = Me.Width
End Sub
Last edited by dz32; Jul 3rd, 2021 at 07:25 PM.
-
Jul 4th, 2021, 02:16 AM
#18
Thread Starter
New Member
Re: Injecting a VB6 dll and manipulating the UI
lots of Emojis? elaborated CAP'd words ?
nahhh... we are old school here.
Thank you both.
Thank you dz32.
Thank you Trick.
I will post a polished solution later for next generations to come. this is gold what you guys laid out here.
-
Jul 4th, 2021, 03:38 AM
#19
Thread Starter
New Member
-
Jul 4th, 2021, 09:48 AM
#20
Re: Injecting a VB6 dll and manipulating the UI
If this is still being updated, and with multiple versions, then it doesn't sound so much like legacy. In that case, you need to be sure that you are not violating any legal agreement with the approach you are taking.
My usual boring signature: Nothing
-
Jul 4th, 2021, 08:14 PM
#21
Re: Injecting a VB6 dll and manipulating the UI
I've made special example where you can access to Forms collection (and controls) from your code like that:
Code:
' //
' // Access Forms collection of a VB6 executable
' // by The trick 2021
' //
Option Explicit
Private Type STARTUPINFO
cb As Long
lpReserved As Long
lpDesktop As Long
lpTitle As Long
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
Private Declare Function CreateProcess Lib "kernel32" _
Alias "CreateProcessW" ( _
ByVal lpApplicationName As Long, _
ByVal lpCommandLine As Long, _
ByRef lpProcessAttributes As Any, _
ByRef lpThreadAttributes As Any, _
ByVal bInheritHandles As Long, _
ByVal dwCreationFlags As Long, _
ByRef lpEnvironment As Any, _
ByVal lpCurrentDirectory As Long, _
ByRef lpStartupInfo As STARTUPINFO, _
ByRef lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function CloseHandle Lib "kernel32" ( _
ByVal hObject As Long) As Long
Private Declare Function WaitForInputIdle Lib "user32" ( _
ByVal hProcess As Long, _
ByVal dwMilliseconds As Long) As Long
Private Declare Sub GetStartupInfo Lib "kernel32" _
Alias "GetStartupInfoW" ( _
ByRef lpStartupInfo As STARTUPINFO)
Sub Main()
Dim tSI As STARTUPINFO
Dim tPI As PROCESS_INFORMATION
Dim cVBGetGlobal As Object
Dim cForms As Object
Dim frmMain As Object
InitializeInjectLibrary
tSI.cb = Len(tSI)
GetStartupInfo tSI
If CreateProcess(StrPtr(App.Path & "\..\dummy\dummy.exe"), 0, ByVal 0&, ByVal 0&, 0, 0, ByVal 0&, 0, tSI, tPI) = 0 Then
MsgBox "CreateProcess failed"
Exit Sub
End If
WaitForInputIdle tPI.hProcess, -1
CloseHandle tPI.hProcess
CloseHandle tPI.hThread
Set cVBGetGlobal = CreateVBObjectInThread(tPI.dwThreadId, App.Path & "\..\dll\GetVBGlobal.dll", "CExtractor")
Set cForms = cVBGetGlobal.Forms
Set frmMain = cForms(0)
' // Change back color of picturebox
frmMain.Controls("picTest").BackColor = vbRed
' // Draw line on picturebox
frmMain.Controls("picTest").Line (0, 0)-Step(100, 50), vbGreen, BF
UninitializeInjectLibrary
End Sub
The executable contains a PictureBox (picTest) and you can acccess to it. You can access to a VB.Global class from a AX-Dll without any restrictions (because no marshaling is needed).
https://github.com/thetrik/InjectAndManipulateVB
-
Jul 4th, 2021, 08:18 PM
#22
Re: Injecting a VB6 dll and manipulating the UI
Originally Posted by Broder
The pointer I get back is not equal to the value I get from ObjPtr(VB.Global)
no wonder it crashes...
The proper code:
Code:
' //
' // Access to Forms collection of a VB6-EXE
' // By The trick 2021
' //
Option Explicit
Private Type MODULEINFO
lpBaseOfDll As Long
SizeOfImage As Long
EntryPoint As Long
End Type
Private Declare Function GetModuleHandle Lib "kernel32" _
Alias "GetModuleHandleW" ( _
ByVal lpModuleName As Long) As Long
Private Declare Function GetModuleInformation Lib "psapi" ( _
ByVal hProcess As Long, _
ByVal hModule As Long, _
ByRef lpmodinfo As MODULEINFO, _
ByVal cb As Long) As Long
Private Declare Function GetCurrentProcess Lib "kernel32" () As Long
Private Declare Function vbaObjSetAddref Lib "MSVBVM60.DLL" _
Alias "__vbaObjSetAddref" ( _
ByRef dstObject As Any, _
ByRef srcObjPtr As Any) As Long
Private Declare Sub GetMem4 Lib "msvbvm60" ( _
ByRef Addr As Any, _
ByRef Dst As Any)
Public Property Get Forms() As Object
Dim hModule As Long
Dim pVbHdr As Long
Dim tModInfo As MODULEINFO
Dim pProjData As Long
Dim pExtTable As Long
Dim lExtCount As Long
Dim lIndex As Long
Dim lTag As Long
Dim pObjDesc As Long
Dim pVBGlobal As Long
Dim cVBGlobal As VB.Global
hModule = GetModuleHandle(0)
If GetModuleInformation(GetCurrentProcess(), hModule, tModInfo, Len(tModInfo)) = 0 Then
MsgBox "GetModuleInformation failed", vbCritical
Exit Property
End If
GetMem4 ByVal tModInfo.EntryPoint + 1, pVbHdr
GetMem4 ByVal pVbHdr + &H30, pProjData
GetMem4 ByVal pProjData + &H234, pExtTable
GetMem4 ByVal pProjData + &H238, lExtCount
For lIndex = 0 To lExtCount - 1
GetMem4 ByVal pExtTable + lIndex * 8, lTag
If lTag = 6 Then
GetMem4 ByVal pExtTable + lIndex * 8 + 4, pObjDesc
GetMem4 ByVal pObjDesc + 4, pVBGlobal
GetMem4 ByVal pVBGlobal, pVBGlobal
vbaObjSetAddref cVBGlobal, ByVal pVBGlobal
Set Forms = cVBGlobal.Forms
End If
Next
End Property
-
Jul 5th, 2021, 02:44 PM
#23
Hyperactive Member
Re: Injecting a VB6 dll and manipulating the UI
wauuu you never stop hallucinating The Trick.
You could access other objects that are not forms, for example classes, modules
the test program works perfectly, but I have problems when trying with an existing program.
in Set Forms = cVBGlobal.Forms in Forms ().
error '-2147417851'
error in the method object
a greeting
-
Jul 5th, 2021, 03:19 PM
#24
Re: Injecting a VB6 dll and manipulating the UI
You could access other objects that are not forms, for example classes, modules
It's possible as well but it requires another technique.
in Set Forms = cVBGlobal.Forms in Forms ().
error '-2147417851'
error in the method object
What's the application exactly? Does it have the Global object initialized?
-
Jul 5th, 2021, 04:43 PM
#25
Hyperactive Member
Re: Injecting a VB6 dll and manipulating the UI
works perfectly and CreateVBObjectInThread perfectly
Code:
Sub Main()
Dim tSI As STARTUPINFO
Dim tPI As PROCESS_INFORMATION
Dim cVBGetGlobal As Object
Dim cForms As Object
Dim frmMain As Object
InitializeInjectLibrary
tSI.cb = Len(tSI)
GetStartupInfo tSI
If CreateProcess(StrPtr("F:\0VB6\TheBestTrick\prj.exe"), 0, ByVal 0&, ByVal 0&, 0, 0, ByVal 0&, 0, tSI, tPI) = 0 Then
MsgBox "CreateProcess failed"
Exit Sub
End If
WaitForInputIdle tPI.hProcess, -1
CloseHandle tPI.hProcess
CloseHandle tPI.hThread
Set cVBGetGlobal = CreateVBObjectInThread(tPI.dwThreadId, App.Path & "\..\dll\GetVBGlobal.dll", "CExtractor")
but from here on, it is where the error occurs
Code:
Set cForms = cVBGetGlobal.Forms
Set frmMain = cForms(0)
UninitializeInjectLibrary
End Sub
thank you very much
sorry for bothering the thread, I love this topic very much
-
Jul 5th, 2021, 09:52 PM
#26
Addicted Member
Re: Injecting a VB6 dll and manipulating the UI
Originally Posted by Shaggy Hiker
If this is still being updated, and with multiple versions, then it doesn't sound so much like legacy. In that case, you need to be sure that you are not violating any legal agreement with the approach you are taking.
Exactly.
-
Jul 6th, 2021, 04:12 AM
#27
Re: Injecting a VB6 dll and manipulating the UI
The O.P. does need to comment on this.
-
Jul 6th, 2021, 04:34 AM
#28
Re: Injecting a VB6 dll and manipulating the UI
Originally Posted by yokesee
Code:
If CreateProcess(StrPtr("F:\0VB6\TheBestTrick\prj.exe"), 0, ByVal 0&, ByVal 0&, 0, 0, ByVal 0&, 0, tSI, tPI) = 0 Then
MsgBox "CreateProcess failed"
Exit Sub
End If
but from here on, it is where the error occurs
What's the prj.exe?
-
Jul 6th, 2021, 09:33 AM
#29
Re: Injecting a VB6 dll and manipulating the UI
The OP hasn't returned to this thread in a few days. Admittedly, a few days that were a US holiday, so they may have had other priorities, depending on where they are located (I didn't go looking).
My view is that there are a lot of abandoned VB6 programs around that still work, after a fashion. I have a few, myself, most of which should have been retired/replaced decades back, but that's a different story. Trying to extend an abandoned VB6 program is understandable, if far from the ideal solution. However, if the application is still being maintained, then extending it in this fashion could be illegal. I'd like the OP to clarify that (I can be reached via PM) before this discussion goes on further. I'm leaning towards it being fine, in this case, but I'd like a bit of clarity.
My usual boring signature: Nothing
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|