Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VBScript

Converting VBScript to VB.NET

5.00/5 (1 vote)
22 Nov 2011CPOL5 min read 30.4K  
How to convert VB Script to VB.NET

My last post titled “Event 10 Mystery Solved” (found here.) left me with a question about the binary version of the SID, a returned value of CreatorSID: 1,5,0,0,0,0,0,5,21,0,0,0,190,118,173,34,87,198,105,19,239,226,7,24,244,1,0,0.

I started searching the net to see if anyone has posted a conversion tool to go from the the Binary SID: The Administrators SID in array of bytes format:

VBScript
CreatorSID = {1,2,0,0,0,0,0,5,32,0,0,0,32,2,0,0};

to a String version:

Administrators group S-1–5-32-544

So far, I have not found a formula to do a conversion (that works) but I am getting closer, and learning a few things in the process.

I ran across a blog article (located here) that used PowerShell to search classes by Key Phrase and return a list of Classes that it was located in. The default search location is Cimv2 (System default).

PS-SID-Search

That gave me several locations to search for answers.

The Win32_SID does not return any instances. You have to create an instance, as you will see below in the script. It does show BinaryRepresentation as a property.

Then I ran across this Web Page.

How to convert the SDDL form of an SID to a SAM account name (located here and another KB article here on conversion) which the code appears to be in VB6 version. The code still didn’t do what I wanted. They all required you to access the system for some form of information. It did tell me that the SID is actually stored in the binary form to start with, so all I had to do is figure out how to get at it, which brings me to the next web page.

This page is a script that I found at the Microsoft Script Center (located here) called, “Create Shared Folder and Set Access Permissions”.

After looking through the code, I noted that it had a place where the binary version of the SID was placed into a variable, which was what I wanted.

VBScript
Foldername="c:\KS"   'folder to share
sharename="KS_Share"   'Share Name
strDesc="Test Share"   'Share Description
strUser="mike"      'User to set permissions for

Set  Services = GetObject("winmgmts:{impersonationLevel=impersonate,(Security)}!\\.\root\cimv2")
' Connects to the WMI service with security privileges
Set SecDescClass = Services.Get("Win32_SecurityDescriptor")
' Need an instance of the Win32_SecurityDescriptor 
' so we can create an instance of a Security Descriptor.
Set SecDesc = SecDescClass.SpawnInstance_()
' Create an instance of a Security Descriptor.
Set colWinAcc = Services.ExecQuery("SELECT * 
_FROM Win32_ACCOUNT WHERE Name='" & strUser & "'")
If colWinAcc.Count < 1 Then
   Wscript.echo("User " & strUser & "Not Found - quitting")
   wscript.quit
End If
' Find the WMI representation of a particular Windows Account
For Each refItem in colWinAcc
   Set refSID = Services.Get("Win32_SID='" & refItem.SID & "'")
   ' Get the SID for the choosen Windows account.
Next
Set refTrustee = Services.Get("Win32_Trustee").spawnInstance_()
' Creates an instance of a Windows Security Trustee 
' (usually a user but anything with a SID I guess...)
With refTrustee
   .Domain = refSID.ReferencedDomainName
   .Name = refSID.AccountName
   .SID = refSID.BinaryRepresentation
   .SidLength = refSID.SidLength
   .SIDString = refSID.SID
End With
' Sets the trustee object up with the SID & all that malarkey 
' from the user object we have choosen to work on
Set ACE = Services.Get("Win32_Ace").SpawnInstance_
' Creates an instance of an Access Control Entry Object
' (this will be one entry on the access list on an object)
ACE.Properties_.Item("AccessMask") = 2032127
' This is full Control    ' This is full Control (bitflag) full list here: 
' http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/win32_ace.asp
ACE.Properties_.Item("AceFlags") = 3
' what to apply ACE to inc inhehitance 3 - means files & 
' folders get permssions & pass onto children
ACE.Properties_.Item("AceType") = 0
' 0=allow access 1=deny access
ACE.Properties_.Item("Trustee") = refTrustee
' Set the Trustee (user) that this Access control Entry will refer to.
SecDesc.Properties_.Item("DACL") = Array(ACE)
' Get the DACL property of the Security Descriptor object
' Add the ACE to the Dynamic Access Control List on the object (an array) 
' it will overwrite the old entries 
' unless you retreive & save 'em first & 
' add them to a big array with the new entry as well as 
' the old ones
Set Share = Services.Get("Win32_Share")
' Get a WMI share Object
Set InParam = Share.Methods_("Create").InParameters.SpawnInstance_()
' Create an instance of a WMI input Parameters object
InParam.Properties_.Item("Access") = SecDesc
' Set the Access Parameter to the Security Descriptor Object we configured above
InParam.Properties_.Item("Description") = strDesc
InParam.Properties_.Item("Name") = ShareName
InParam.Properties_.Item("Path") = FolderName
InParam.Properties_.Item("Type") = 0
Set outParams=Share.ExecMethod_("Create", InParam)

' Create the share with all the parameters we have set up
wscript.echo("OUT: " & outParams.returnValue)
If outParams.returnValue <> 0 Then
   wscript.echo("Failed to Create Share, return Code:" & outParams.returnValue)
Else
   wscript.echo("Folder " & Foldername & _
   " sucessfully shared as: " & sharename & " _
                 with FULL CONTROL Permissions for user " & strUser)
End If

The code above as listed on the Script Center.

Next, I had to strip out everything to do with working with the folders. Then, add a way to show the “refTrustee.* ” information, as you see in the next code listing.

VBScript
strUser="David"      'User to Get SID For

Set  Services = GetObject("winmgmts:{impersonationLevel=impersonate,(Security)}!\\.\root\cimv2")
' Connects to the WMI service with security privileges
Set SecDescClass = Services.Get("Win32_SecurityDescriptor")
' Need an instance of the Win32_SecurityDescriptor so we can create an instance of a 
' Security Descriptor.
Set SecDesc = SecDescClass.SpawnInstance_()
' Create an instance of a Security Descriptor.
Set colWinAcc = Services.ExecQuery("SELECT * _
FROM Win32_ACCOUNT WHERE Name='" & strUser & "'")
If colWinAcc.Count < 1 Then
   Wscript.echo("User " & strUser & "Not Found - quitting")
   wscript.quit
End If
' Find the WMI representation of a particular Windows Account
For Each refItem in colWinAcc
   Set refSID = Services.Get("Win32_SID='" & refItem.SID & "'")
   ' Get the SID for the choosen Windows account.
Next
Set refTrustee = Services.Get("Win32_Trustee").spawnInstance_()
' Creates an instance of a Windows Security Trustee 
' (usually a user but anything with a SID I guess...)
With refTrustee
   .Domain = refSID.ReferencedDomainName
   .Name = refSID.AccountName
   .SID = refSID.BinaryRepresentation
   .SidLength = refSID.SidLength
   .SIDString = refSID.SID
End With
' Sets the trustee object up with the SID & all that malarkey from the user object 
' we have choosen to work on

Wscript.echo "Domain Name: " & refTrustee.Domain
Wscript.echo "User Name: " & refTrustee.Name
strSID = Join(refTrustee.SID, ",")
         WScript.Echo "SID: " & strSID

Wscript.echo "SidLength: " & refTrustee.SidLength
Wscript.echo "SIDString: " & refTrustee.SIDString

The only thing you will have to change in the modified script to get it to work is the name of a user. You can also save it as a text file and load it into the Scriptomatic V2 to run it.

The output from the BUILTIN Administrators group looks like this:

Domain Name: BUILTIN
User Name: Administrators
SID: 1,2,0,0,0,0,0,5,32,0,0,0,32,2,0,0
SidLength: 16
SIDString: S-1-5-32-544

To me, one of the most interesting things is the way to handle the array of Bytes, the script used the join function to pull out all of the bytes.

VBScript
strSID = Join(refTrustee.SID, ",") 
         WScript.Echo "SID: " & strSID

This does not work in VB.NET, you get a this function is not supported error.

Now the Conversion

Below is the VB.NET version of the stripped script above, I started by copying the script into a new project then adding the Imports and references, also adding Dim to a few lines and replace the WScript.Echo with a way to get the information to a multi-line Textbox.

Here is what the form looks like:

XtraSID1

And here is the code behind the form:

VB.NET
Imports System.Management
Imports System.Management.Instrumentation
Imports System.Threading
Imports System
Imports System.IO
Imports System.Security
Imports System.Security.Permissions
Imports System.Text

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
                           Handles MyBase.Load
        Dim uan As Integer
        Dim usearcher As New ManagementObjectSearcher( _
                       "root\CIMV2", _
                       "SELECT * FROM Win32_UserAccount")

        For Each queryObj As ManagementObject In usearcher.Get()
            ComboBoxAllUsers.Items.Add(queryObj("Name"))

        Next
        lblUserNumber.Text = "Total Accounts Found: " & ComboBoxAllUsers.Items.Count
        uan = ComboBoxAllUsers.Items.Count
    End Sub

    Private Sub btnGetNfo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
                                Handles btnGetNfo.Click
        Try
            'If ComboBoxAllUsers.SelectedItem = Nothing Then
            '    MsgBox("Please Select A User Name", 
            '    MsgBoxStyle.Information, "User Not Selected")
            'End If
            Dim strUser As String = ComboBoxAllUsers.SelectedItem.ToString
            Dim refSID As System.Object
            refSID = Nothing
            Dim Services = GetObject("winmgmts:{impersonationLevel=impersonate,_
                           (Security)}!\\.\root\cimv2")
            ' Connects to the WMI service with security privileges
            Dim SecDescClass = Services.Get("Win32_SecurityDescriptor")
            ' Need an instance of the Win32_SecurityDescriptor so we can create 
            ' an instance of a Security Descriptor.
            Dim SecDesc = SecDescClass.SpawnInstance_()
            ' Create an instance of a Security Descriptor.
            Dim colWinAcc = Services.ExecQuery_
            ("SELECT * FROM Win32_ACCOUNT WHERE Name='" & _
                            strUser & "'")
            If colWinAcc.Count < 1 Then
                MsgBox("User " & strUser & "Not Found ")

            End If
            ' Find the WMI representation of a particular Windows Account
            For Each refItem In colWinAcc
                refSID = Services.Get_
                ("Win32_SID='" & refItem.SID & "'")
                ' Get the SID for the choosen Windows account.
            Next
            Dim refTrustee = Services.Get("Win32_Trustee").spawnInstance_()
            ' Creates an instance of a Windows Security Trustee 
            ' (usually a user but anything with a SID I guess...)
            With refTrustee
                .Domain = refSID.ReferencedDomainName
                .Name = refSID.AccountName
                .SID = refSID.BinaryRepresentation
                .SidLength = refSID.SidLength
                .SIDString = refSID.SID
            End With
            ' Sets the trustee object up with the SID & all that malarkey from the 
            ' user object we have choosen to work on
            Dim strb As New StringBuilder
            Dim strsid As New StringBuilder
            Dim nstrSid As String
            Dim pSid As String
            For Each pSid In refTrustee.SID
                strsid.Append(pSid & ",")
            Next
            nstrSid = strsid.ToString

            strb.AppendLine("Domain Name: " & refTrustee.Domain)
            strb.AppendLine("User Name: " & refTrustee.Name)
            'strSID = Join(refTrustee.SID, ",")
            strb.AppendLine("SID: " & nstrSid)

            strb.AppendLine("SidLength: " & refTrustee.SidLength)
            strb.AppendLine("SIDString: " & refTrustee.SIDString)
            tbSidNFO.Text = strb.ToString

        Catch ex As Exception
            If ComboBoxAllUsers.SelectedItem = Nothing Then
                MsgBox("Please Select A User Name", _
                MsgBoxStyle.Information, "User Not Selected")
            Else
                MsgBox("An Error Has Occured : " & vbCrLf & ex.Message)
            End If

        End Try
    End Sub

    Private Sub lblAbout_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
                               Handles lblAbout.Click
        My.Forms.AboutBox1.Show()

    End Sub
End Class

The code for the above app only gets the local User Accounts it finds.

The way I handled the array of Bytes in VB.NET is with a foreach loop and a string builder. I’m not sure how others would do it but I kinda like the Stringbuilder now that I have learned how to use it. The other way I used to do it was in 1 long line and several “&” and “vbCrLf” mixed in. When you had several properties to pull out, then it got harder to read and handle the code even using line continuation.

This code below is the way to get the information to the text box from the original code.

VB.NET
Dim strb As New StringBuilder    'Stringbuilder to build the complete String to the Textbox
            Dim strsid As New StringBuilder  'Stringbuilder for the array of bytes
            Dim nstrSid As String            ' string to represent the complete array of bytes
            Dim pSid As String               ' string id to represent each byte in the foreach loop
            For Each pSid In refTrustee.SID
                strsid.Append(pSid & ",")  ' adds each byte 
                                    ' to a string and appends a Comma to each byte
            Next
            nstrSid = strsid.ToString

            strb.AppendLine("Domain Name: " & refTrustee.Domain)
            strb.AppendLine("User Name: " & refTrustee.Name)
            'strSID = Join(refTrustee.SID, ",")
            strb.AppendLine("SID: " & nstrSid)

            strb.AppendLine("SidLength: " & refTrustee.SidLength)
            strb.AppendLine("SIDString: " & refTrustee.SIDString)
            tbSidNFO.Text = strb.ToString

Let's jazz this up a bit and get the System and Group accounts also.

Here is what the final app looks like:

XtraSID2

In this version, I added a group box and the 3 Radio Buttons inside of it for selecting the account type.

I then moved the code to get the names for the account type to its own sub. Then in the radio button on change event, calls the selected user type sub to fill the combo box.

Now the Final Code

VB.NET
Imports System.Management
Imports System.Management.Instrumentation
Imports System.Threading
Imports System
Imports System.IO
Imports System.Security
Imports System.Security.Permissions
Imports System.Text

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
        Handles MyBase.Load
        ComboBoxAllUsers.Items.Clear()
        Get_userAccount()

    End Sub

    Private Sub btnGetNfo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
        Handles btnGetNfo.Click
        Try
            'If ComboBoxAllUsers.SelectedItem = Nothing Then
            '    MsgBox("Please Select A User Name", _
            MsgBoxStyle.Information, "User Not Selected")
            'End If
            Dim strUser As String = ComboBoxAllUsers.SelectedItem.ToString
            Dim refSID As System.Object
            refSID = Nothing
            Dim Services = GetObject("winmgmts:{impersonationLevel=impersonate,_
                           (Security)}!\\.\root\cimv2")
            ' Connects to the WMI service with security privileges
            Dim SecDescClass = Services.Get("Win32_SecurityDescriptor")
            ' Need an instance of the Win32_SecurityDescriptor 
            ' so we can create an instance of a Security Descriptor.
            Dim SecDesc = SecDescClass.SpawnInstance_()
            ' Create an instance of a Security Descriptor.
            Dim colWinAcc = Services.ExecQuery_
            ("SELECT * FROM Win32_ACCOUNT WHERE Name='" _
                            & strUser & "'")
            If colWinAcc.Count < 1 Then
                MsgBox("User " & strUser & "Not Found ")

            End If
            ' Find the WMI representation of a particular Windows Account
            For Each refItem In colWinAcc
                refSID = Services.Get("Win32_SID='" & refItem.SID & "'")
                ' Get the SID for the choosen Windows account.
            Next
            Dim refTrustee = Services.Get("Win32_Trustee").spawnInstance_()
            ' Creates an instance of a Windows Security Trustee 
            ' (usually a user but anything with a SID I guess...)
            With refTrustee
                .Domain = refSID.ReferencedDomainName
                .Name = refSID.AccountName
                .SID = refSID.BinaryRepresentation
                .SidLength = refSID.SidLength
                .SIDString = refSID.SID
            End With
            ' Sets the trustee object up with the SID & all that malarkey 
            ' from the user object we have choosen to work on
            Dim strb As New StringBuilder
            Dim strsid As New StringBuilder
            Dim nstrSid As String
            Dim pSid As String
            For Each pSid In refTrustee.SID
                strsid.Append(pSid & ",")
            Next
            nstrSid = strsid.ToString

            strb.AppendLine("Domain Name: " & refTrustee.Domain)
            strb.AppendLine("User Name: " & refTrustee.Name)
            'strSID = Join(refTrustee.SID, ",")
            strb.AppendLine("SID: " & nstrSid)

            strb.AppendLine("SidLength: " & refTrustee.SidLength)
            strb.AppendLine("SIDString: " & refTrustee.SIDString)
            tbSidNFO.Text = strb.ToString

        Catch ex As Exception
            If ComboBoxAllUsers.SelectedItem = Nothing Then
                MsgBox("Please Select A User Name", _
                MsgBoxStyle.Information, "User Not Selected")
            Else
                MsgBox("An Error Has Occured : " & vbCrLf & ex.Message)
            End If

        End Try
    End Sub

    Private Sub lblAbout_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
                               Handles lblAbout.Click
        My.Forms.AboutBox1.Show()
    End Sub

    Private Sub Get_userAccount()
        tbSidNFO.Clear()
        Dim uan As Integer
        Dim usearcher As New ManagementObjectSearcher( _
                       "root\CIMV2", _
                       "SELECT * FROM Win32_UserAccount")

        For Each queryObj As ManagementObject In usearcher.Get()
            ComboBoxAllUsers.Items.Add(queryObj("Name"))

        Next
        lblUserNumber.Text = "Total User Accounts Found: " & ComboBoxAllUsers.Items.Count
        uan = ComboBoxAllUsers.Items.Count
    End Sub
    Private Sub Get_GroupAccount()
        tbSidNFO.Clear()
        Dim uan As Integer
        Dim usearcher As New ManagementObjectSearcher( _
                       "root\CIMV2", _
                       "SELECT * FROM Win32_Group")

        For Each queryObj As ManagementObject In usearcher.Get()
            ComboBoxAllUsers.Items.Add(queryObj("Name"))

        Next
        lblUserNumber.Text = "Total Group Accounts Found: " & ComboBoxAllUsers.Items.Count
        uan = ComboBoxAllUsers.Items.Count
    End Sub
    Private Sub Get_SystemAccounts()
        tbSidNFO.Clear()

        Dim uan As Integer
        Dim usearcher As New ManagementObjectSearcher( _
                       "root\CIMV2", _
                       "SELECT * FROM Win32_SystemAccount")

        For Each queryObj As ManagementObject In usearcher.Get()
            ComboBoxAllUsers.Items.Add(queryObj("Name"))

        Next
        lblUserNumber.Text = "Total System Accounts Found: " & ComboBoxAllUsers.Items.Count
        uan = ComboBoxAllUsers.Items.Count
    End Sub

    Private Sub RadioButtonUserAccount_CheckedChanged(ByVal sender As System.Object, _
          ByVal e As System.EventArgs) _
          Handles RadioButtonUserAccount.CheckedChanged
        ComboBoxAllUsers.Items.Clear()  'Need to clear or the usernames will keep stacking up.
        ComboBoxAllUsers.Text = _
        "Please Select A User Account"  ' Changes the text property 
                                                  'you see before selecting a user name.
        Get_userAccount()     'Calls the sub for the account type.

    End Sub

    Private Sub RadioButtonGroupAccount_CheckedChanged(ByVal sender As System.Object, _
             ByVal e As System.EventArgs) Handles RadioButtonGroupAccount.CheckedChanged

        ComboBoxAllUsers.Items.Clear()
        ComboBoxAllUsers.Text = "Please Select A Group Account"
        Get_GroupAccount()

    End Sub

    Private Sub RadioButtonSystemAccount_CheckedChanged(ByVal sender As System.Object, _
         ByVal e As System.EventArgs) Handles RadioButtonSystemAccount.CheckedChanged

        ComboBoxAllUsers.Items.Clear()
        ComboBoxAllUsers.Text = "Please Select A System Account"
        Get_SystemAccounts()

    End Sub
End Class

Now that I can get several sample versions of the binary/array of bytes version of the SID, I can get back to the research of being able to do a conversion of a given binary SID and do the conversion without having access to the original system it came from.

The best source I could find that explained how the conversion works is (located here) at selfadsi.org Microsoft Security Descriptor(SID)Attributes.

So far, I can’t get the math & conversion to agree with what this program shows.

Conclusion

If you have access to the systems and have Admin Authority, then this should work for you as an easy way to get the binary and string form for an account. You may also want to check out another tool of mine called All User Account NFO (located here).

Located on my website with a link to download the program.

That’s it for now, hope everyone else learned as much as I did.

Please post any comments or questions.

I think I have figured out the comments control, maybe.

Reposting 11/19/2011 to try and get the RSS to pick it up.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)