Converting VBScript to VB.NET
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:
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).
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.
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.
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.
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 Import
s 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:
And here is the code behind the form:
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.
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:
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
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.