Introduction
I developed a threaded Visual Basic 2010 application that verifies if a Microsoft security issue (MS08-070: Vulnerabilities
in Visual Basic 6.0 Runtime Extended Files (ActiveX Controls) could allow remote code execution) exists on a computer.
The application checks both registered ActiveX controls and ActiveX controls that exist in your file system (but not registered).
Microsoft states you need to contact the software developer (that installed these exploitable ActiveX controls) and request an updated
software package (that will install updated ActiveX controls). How do you know which software installed the ActiveX controls? Why can’t the individual files just be updated?
My software provides an easy method to check your systems and fix the security issues without having to do all this research and major software upgrades.
- This application shows you how to determine which ActiveX (OCX) files are registered on your (32/64 bit OS) computer, by reading the system Registry.
- Provides a utility to resolve a potential security issue that exists on many computers.
- Demonstrates how to use threading to read Registry entries and check files (via
System.Management
) at the same time.
- Demonstrates how to allow threads to update a progress bar and
DataGridView
on the main form.
- Demonstrates how to apply a style to reformat
DataGridView
entries (row color) as they are added, based upon data entry.
- Demonstrates how to embed files into your compiled application and extract files as required.
What problem does this solution solve?
This Visual Basic (2010) utility allows a system administrator to check their systems for exploitable VB6 ActiveX controls and generate
a fix action [a batch file that unregisters the exploitable OCX file, renames it, copies an updated {Microsoft signed} OCX file {that was extracted
out of this utility}, and registers the new OCX file]. No need to guess which application installed the outdated OCX files or upgrade existing software.
How does the code work?
This application starts two threads. The first thread scans the Registry for registered OCX files.
Private Sub Read_Reg()
Thread.CurrentThread.Name = "Read_Registry"
Dim x As Integer = 1
Dim myAL As New ArrayList() myAL.Clear()
Dim strProgID As String = String.Empty
Dim strInprocServer32 As String = String.Empty
Dim strVersion As String = String.Empty
Dim defaultValue As Array
If My.Computer.FileSystem.DirectoryExists("C:\Windows\SysWOW64") Then
defaultValue = Registry.ClassesRoot.OpenSubKey("Wow6432Node\CLSID").GetSubKeyNames()
Else
defaultValue = Registry.ClassesRoot.OpenSubKey("CLSID").GetSubKeyNames()
End If
Dim EntryCount As Integer = defaultValue.Length
Delegates are added to initialize the main form progress bar based upon Registry count and advance it while reading entries.
Private Delegate Sub DelInitializePB(ByVal pMin As Integer, _
ByVal pMax As Integer, ByVal pValue As Integer)
Private Delegate Sub MethodInvoker(ByVal pVal As Integer)
Private Sub InitializePB(ByVal pMin As Integer, _
ByVal pMax As Integer, ByVal pValue As Integer)
progressBar1.Minimum = pMin
progressBar1.Maximum = pMax
progressBar1.Value = pValue
End Sub
Private Sub IncrementPB(ByVal PVal As Integer)
If Me.progressBar1.InvokeRequired Then
Me.progressBar1.Invoke(New MethodInvoker(AddressOf IncrementPB), _
New Object() {PVal})
Else
progressBar1.Value = PVal
End If
End Sub
The second thread scans for OCX files on all hard drives.
Private Sub Check_for_OCX_on_HD()
Thread.CurrentThread.Name = "Check_Drives_for_OCX"
Dim t2 As New System.Management.SelectQuery(_
"Select DeviceID from Win32_LogicalDisk Where drivetype = '3' ")
Dim r2 As New System.Management.ManagementObjectSearcher(t2)
Dim Drv_Name As System.Management.ManagementObject
Dim Drv_Ltrs As System.Management.ManagementObjectCollection = r2.Get()
Dim Drv_Ltrs_Count As Integer = Drv_Ltrs.Count
Dim DrvLtrArray As New ArrayList
For Each Drv_Name In Drv_Ltrs
DrvLtrArray.Add("Drive='" & _
Drv_Name("DeviceID").ToString & "'").ToString()
Next Drv_Name
Count_File_Entry("(Querying Files)")
Dim myArr4 As String() = CType(DrvLtrArray.ToArray(GetType(String)), String())
Dim DrvSearch As String = String.Join(" Or ", myArr4)
Dim q As New System.Management.SelectQuery("Select * from " & _
"CIM_DataFile Where Extension = 'ocx' and (" & DrvSearch & ")")
Dim s As New System.Management.ManagementObjectSearcher(q)
Dim OCX_Lst As System.Management.ManagementObjectCollection = s.Get()
Dim OCX_Count As Integer = OCX_Lst.Count
Count_File_Entry("(Processing " & OCX_Count & " Files)")
Delegates are added to allow the threads to add a row to the DataGridView
s on the main form.
Private Sub AddRow_2_Reg_DataGridView(ByVal This_Key As String, _
ByVal OCX_Ver As String, ByRef OCXPath As String)
If Me.Reg_DataGridView.InvokeRequired Then
Me.Reg_DataGridView.Invoke(New add_2_Reg_DataGridView(AddressOf _
AddRow_2_Reg_DataGridView), New Object() {This_Key, OCX_Ver, OCXPath})
Else
Reg_index = Reg_DataGridView.Rows.Add(This_Key, OCX_Ver, OCXPath)
Me.Reg_DataGridView.FirstDisplayedScrollingRowIndex = Me.Reg_DataGridView.RowCount - 1
Me.Reg_DataGridView.Update()
End If
End Sub
Changes in this revision
2 Nov 2010
The Read_Reg
subroutine was modified (old lines 316 - 341).
I now check for a Registry entry and test if a null entry exists before trying to read Registry values. Many null exceptions were being thrown
in the old code. I also now close the Registry entry after reading it.
Old code:
For Each subKeyName As String In defaultValue strProgID = String.Empty
Try
If My.Computer.FileSystem.DirectoryExists("C:\Windows\SysWOW64") Then
strProgID = Registry.ClassesRoot.OpenSubKey("Wow6432Node\CLSID\" & _
subKeyName & "\ProgID").GetValue("").ToString()
Else
strProgID = Registry.ClassesRoot.OpenSubKey("CLSID\" & _
subKeyName & "\ProgID").GetValue("").ToString()
End If
Catch ex As Exception
End Try
strInprocServer32 = String.Empty
Dim InprocServer32_Path As String = String.Empty
Try
If My.Computer.FileSystem.DirectoryExists("C:\Windows\SysWOW64") Then
InprocServer32_Path = Registry.ClassesRoot.OpenSubKey("Wow6432Node\CLSID\" & _
subKeyName & "\InprocServer32").GetValue("").ToString()
Else
InprocServer32_Path = Registry.ClassesRoot.OpenSubKey("CLSID\" & _
subKeyName & "\InprocServer32").GetValue("").ToString()
End If
strInprocServer32 = System.IO.Path.GetFullPath(InprocServer32_Path)
Catch ex As Exception
End Try
New code:
For Each subKeyName As String In defaultValue
strProgID = String.Empty
Try
If My.Computer.FileSystem.DirectoryExists("C:\Windows\SysWOW64") Then
Using key As RegistryKey = _
Registry.ClassesRoot.OpenSubKey("Wow6432Node\CLSID\" & _
subKeyName & "\ProgID")
If key IsNot Nothing Then
strProgID = key.GetValue("").ToString()
key.Close()
End If
End Using
Else
Using key As RegistryKey = Registry.ClassesRoot.OpenSubKey("CLSID\" & subKeyName & "\ProgID")
If key IsNot Nothing Then
strProgID = key.GetValue("").ToString()
key.Close()
End If
End Using
End If
Catch ex As Exception
End Try
Points of interest
I learned a lot while developing this application and always welcome comments to improve my code. This was my first threaded application.
Also my first application that embedded resources (the new OCX files) and extracted them as required.
The first use of applying styles to format DataGridView
rows (colors rows based on not a VB6 OCX, exploitable OCX, current OCX).
I used this utility to check at least 80 computers and most had a few VB6 OCX files that required updating.
Read the PDF documents at the top of this article to see how this software is used. The compiled utility is only about 8 megs even though
it has 26 VB6 OCX files embedded as resources. The compiled utility is not included with the source code due to posting size limitations.
History
- Version 1 posted 23 Oct 2011.
- Version 2 posted 2 Nov 2011.