VBA DLL explorer

If you want to use DLL from VBA, you’ll need to know which functions are exported on that DLL so that they can be called from within VBA. There is that nirsoft dllExplorer app, but, could that be done with pure VBA code?. For sure, just paste code in normal module, and run listExportedFuncs to get list (in code it’ll show on msgBox -where you can Ctrl+C to copy to clipboard-, or uncomment lines “‘!” to get in file):

' https://renenyffenegger.ch/notes/development/languages/VBA/Win-API/examples/DbgHelp/ListExportedFuncsOfDll
' http://www.tech-archive.net/Archive/VB/microsoft.public.vb.general.discussion/2007-09/msg00228.html
'
Option Explicit

Private Declare Sub MoveMemoryLong Lib "kernel32" Alias "RtlMoveMemory" (Target As Any, ByVal LPointer As Long, ByVal cbCopy As Long)

Private Const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
Private Const IMAGE_DIRECTORY_ENTRY_EXPORT = 0
Private Const IMAGE_DIRECTORY_ENTRY_IMPORT = 1

Declare Function MapAndLoad Lib "Imagehlp.dll" (ByVal ImageName As String, ByVal DLLPath As String, LoadedImage As LOADED_IMAGE, DotDLL As Long, ReadOnly As Long) As Long
Declare Function UnMapAndLoad Lib "Imagehlp.dll" (LoadedImage As LOADED_IMAGE) As Long
Declare Function ImageRvaToVa Lib "Imagehlp.dll" (ByVal NTHeaders As Long, ByVal Base As Long, ByVal RVA As Long, ByVal LastRvaSection As Long) As Long

Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" (ByVal lpsz As Long) As Long

#If VBA7 Then
Declare Sub PrtSafe RtlMoveMemory Lib "kernel32" (ByRef dest As Any, ByRef source As Any, ByVal size As longPtr)
Declare Sub PrtSafe RtlZeroMemory Lib "kernel32" (dest As Any, ByVal length As Long)
#Else
Declare Sub RtlMoveMemory Lib "kernel32" (ByRef dest As Any, ByRef source As Any, ByVal size As Long)
Declare Sub RtlZeroMemory Lib "kernel32" (dest As Any, ByVal length As Long)
#End If

Type LIST_ENTRY ' { Used by LOADED_IMAGE
FLink As Long
Blink As Long
End Type ' }

Type IMAGE_EXPORT_DIRECTORY_TABLE ' WinNT.h {
Characteristics As Long
TimeDateStamp As Long
MajorVersion As Integer
MinorVersion As Integer
Name As Long
Base As Long
NumberOfFunctions As Long
NumberOfNames As Long
AddressOfFunctions As Long ' Relative virtual address (RVA) from base of image. Points to an array of RVAs of functions/symbols in the module
AddressOfNames As Long ' Relative virtual address (RVA) from base of image
AddressOfNameOrdinals As Long ' Relative virtual address (RVA) from base of image
End Type ' }

Private Type IMAGE_DATA_DIRECTORY ' WinNT.h {
RVA As Long ' Relative VA
size As Long
End Type ' }

Type IMAGE_OPTIONAL_HEADER32 ' WinNT.h {
' Standard fields:
Magic As Integer
MajorLinkerVersion As Byte
MinorLinkerVersion As Byte
SizeOfCode As Long
SizeOfInitializedData As Long
SizeOfUninitializedData As Long
AddressOfEntryPoint As Long
BaseOfCode As Long
BaseOfData As Long

' NT additional fields:
ImageBase As Long
SectionAlignment As Long
FileAlignment As Long
MajorOperatingSystemVersion As Integer
MinorOperatingSystemVersion As Integer
MajorImageVersion As Integer
MinorImageVersion As Integer
MajorSubsystemVersion As Integer
MinorSubsystemVersion As Integer
Win32VersionValue As Long
SizeOfImage As Long
SizeOfHeaders As Long
CheckSum As Long
Subsystem As Integer
DllCharacteristics As Integer
SizeOfStackReserve As Long
SizeOfStackCommit As Long
SizeOfHeapReserve As Long
SizeOfHeapCommit As Long
LoaderFlags As Long
NumberOfRvaAndSizes As Long
' Data directories
DataDirectory(0 To IMAGE_NUMBEROF_DIRECTORY_ENTRIES) As IMAGE_DATA_DIRECTORY ' 17*8 + 96 = 232
End Type ' }

Type IMAGE_FILE_HEADER ' { WinNT.h / COFF file header
Machine As Integer
NumberOfSections As Integer
TimeDateStamp As Long
PointerToSymbolTable As Long
NumberOfSymbols As Long
SizeOfOptionalHeader As Integer
Characteristics As Integer
End Type ' }

Type IMAGE_NT_HEADERS32 ' WinNT.h {
' Compare with IMAGE_NT_HEADERS64, also defined in WinNT.h
'
Signature As Long
FileHeader As IMAGE_FILE_HEADER
OptionalHeader As IMAGE_OPTIONAL_HEADER32 ' or IMAGE_OPTIONAL_HEADER64
End Type ' }
'
' LOADED_IMAGE
' Is defined in both ImageHlp.h and DbgHelp.h
'
Type LOADED_IMAGE ' 48 bytes (46 bytes packed ) ' { Used with MapAndLoad
ModuleName As Long
hFile As Long
MappedAddress As Long ' Base address of mapped file
FileHeader As Long ' Pointer to IMAGE_NT_HEADERS32 ' (Compare with IMAGE_NT_HEADERS64) -- Note: the pointed to IMAGE_NT_HEADERS32 also has a member named FileHeader.
LastRvaSection As Long ' Pointer to first COFF section header (section table)?
NumberOfSections As Long
Sections As Long ' Pointer to IMAGE_SECTION_HEADER (First COFF section header (section table)??)
Characteristics As Long ' Image characteristics value
fSystemImage As Byte ' bool
fDOSImage As Byte ' bool
'
' At least in C, the compiler pads the following two (new) members
' with the previous two bytes into 4 byte so that in C, adding
' or omitting them should not change anything.
'
' fReadOnly as byte ' bool
' Version as byte ' UCHAR
'
' ----------------------------------------------------------
Links As LIST_ENTRY ' 2 longs
SizeOfImage As Long
End Type ' }

Function GetOpenFileName(Optional FileFilter As String = "All files", _
Optional FileExtension As String = "*.*", _
Optional Title As String = "Select a file", _
Optional InitialPath As String = "MyDocs") As String
Dim fDialog As FileDialog, result As Integer
Set fDialog = Application.FileDialog(msoFileDialogFilePicker)

'Optional: FileDialog properties
fDialog.AllowMultiSelect = False
fDialog.Title = "Select a file"
If InitialPath = "MyDocs" Then
fDialog.InitialFileName = VBA.Environ$("UserProfile") & "Documents\"
ElseIf InitialPath = "Downloads" Then
fDialog.InitialFileName = VBA.Environ$("UserProfile") & "Downloads\"
Else
fDialog.InitialFileName = VBA.Environ$("SystemDrive") & "Downloads\"
End If
'Optional: Add filters
fDialog.Filters.Clear
fDialog.Filters.Add "DLL files", "*.dll"
If Not FileExtension = "*.*" Then fDialog.Filters.Add "All files", "*.*"

'Show the dialog. -1 means success!
If fDialog.Show = -1 Then
GetOpenFileName = fDialog.SelectedItems(1)
End If
End Function

Sub listExportedFuncs()
Dim img As LOADED_IMAGE
Dim peHeader As IMAGE_NT_HEADERS32
Dim expTable As IMAGE_EXPORT_DIRECTORY_TABLE
Dim strFunctions As String
Dim DLLPath As String

DLLPath = GetOpenFileName 'Application.GetOpenFileName(FileFilter:="DLL files (*.dll), *.dll", Title:="Select DLL file to open", MultiSelect:=True)

If MapAndLoad(DLLPath, DLLPath, img, True, True) = 0 Then
Debug.Print "Could not map " & DLLPath
Exit Sub
End If

On Error GoTo err_

Dim fOut As Integer: fOut = FreeFile
'! Open Environ$("TEMP") & "\exportedFuncs.txt" For Output As #fOut

' Copy PE file header:
RtlMoveMemory ByVal VarPtr(peHeader), ByVal img.FileHeader, LenB(peHeader)

' Get export table offset as relative virtual address (RVAs)
Dim expRVA As Long
expRVA = peHeader.OptionalHeader.DataDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT).RVA

If expRVA = 0 Then
Call UnMapAndLoad(img)
End If

' Convert RVA to VA:
Dim expVA As Long
expVA = ImageRvaToVa(img.FileHeader, img.MappedAddress, expRVA, 0&)

RtlMoveMemory ByVal VarPtr(expTable), ByVal expVA, LenB(expTable)

Dim nofExports As Long
nofExports = expTable.NumberOfNames

Dim ptrToArrayOfExportedFuncNames As Long
Dim ptrToArrayOfExportedFuncAddresses As Long

ptrToArrayOfExportedFuncNames = ImageRvaToVa(img.FileHeader, img.MappedAddress, expTable.AddressOfNames, 0&)
ptrToArrayOfExportedFuncAddresses = ImageRvaToVa(img.FileHeader, img.MappedAddress, expTable.AddressOfFunctions, 0&)

Dim i As Long
For i = 0 To nofExports - 1
Dim RVAfuncName As Long
Dim RVAfuncAddress As Long

MoveMemoryLong RVAfuncName, ptrToArrayOfExportedFuncNames, 4
MoveMemoryLong RVAfuncAddress, ptrToArrayOfExportedFuncAddresses, 4

Dim VAfuncName As Long
Dim VAfuncAddress As Long

VAfuncName = ImageRvaToVa(img.FileHeader, img.MappedAddress, RVAfuncName, 0&)
VAfuncAddress = ImageRvaToVa(img.FileHeader, img.MappedAddress, RVAfuncAddress, 0&)

'! Print #fOut, VAfuncAddress & ": " & LPSTRtoBSTR(VAfuncName)
strFunctions = strFunctions & vbNewLine & VAfuncAddress & ": " & LPSTRtoBSTR(VAfuncName)

ptrToArrayOfExportedFuncNames = ptrToArrayOfExportedFuncNames + 4
ptrToArrayOfExportedFuncAddresses = ptrToArrayOfExportedFuncAddresses + 4
Next i

Call UnMapAndLoad(img)

'! Close #fOut
MsgBox strFunctions
Exit Sub

err_:
Call UnMapAndLoad(img)
Debug.Print "Error occured: " & Err.Description
End Sub

Private Function LPSTRtoBSTR(ByVal lpString As Long) As String
Dim lenS As Long
Dim ptrToZero As Long

lenS = lstrlen(lpString)

LPSTRtoBSTR = String$(lenS, 0)
RtlMoveMemory ByVal StrPtr(LPSTRtoBSTR), ByVal lpString, lenS

LPSTRtoBSTR = StrConv(LPSTRtoBSTR, vbUnicode)

ptrToZero = InStr(1, LPSTRtoBSTR, Chr(0), 0)

If ptrToZero > 0 Then
LPSTRtoBSTR = Left$(LPSTRtoBSTR, ptrToZero - 1)
End If
End Function


Nearly all the code came from Rene Nyffeneger’s outstanding site

Leave a Reply

Your email address will not be published. Required fields are marked *