Summary: Sid Vicious is a tool developed to get the Security Identifier of Active Directory Users and objects without the need to install or use ADSI edit. This tool was originally designed to assist my brother who heads the systems access team of a large company, but I think it also makes for a great tutorial on connection to and query information from Active Directory using C# .NET

The Code: This is broken down in to 4 parts; 1) Required .NET Namespaces/References 2) The connection to Active Directory, 3) The collection/handling of the SID, and 4) The Interface/glue code.

Step One: .NET Prerequisites

Connections to Active Directory are managed using a reference to the System.DirectoryServices.dll. Though there are other methods available, such as using the ActiveDS com library, for this purpose we will be using System.DirectoryServices. Secondly, to translate the SID from a byte array to usable text, we need to reference the System.Security.Principal namespace in our codefile.

Step Two: Connecting to Active Directory

Using the Directory Services library, connecting to AD is quick and easy. While using the app.config is the normal route for configuring the connection, with just slightly more effort, you can make your code completely portable. To do this, you need to connect to the global catalog to discover and get the RootDse. RootDse is the root of a directory server, and is included with LDAP 3.0 and provides a handy method for getting metadata about the directory server. For more information about RootDse, see here or here.

The Code:

Connecting to the Global Catalog: First you need to create a Directory Entry object pointing to the root path of the Global catalog.

DirectoryEntry ADConnection = null;
using (DirectoryEntry RootDse = new DirectoryEntry("GC:"))
    {
        foreach (DirectoryEntry root in RootDse.Children)
        {
            ADConnection = root;
            break;
        }
    }

So here we get the first Directory Entry object in the global catalog("GC:") and assign it to the Directory Entry "ADConnection" we have created. This is our connection to Active Directory. As you can see, no LDAP path is necessary and this code should work for any environment that has a Global Catalog server.

Querying Active Directory: Now that we have a Directory Entry connection to Active Directory, we can query the catalog.

The Code:

using (ADConnection)
    {
        DirectorySearcher DS = new DirectorySearcher();
        SearchResult SR;
        DS.SearchRoot = ADConnection;
        DS.SearchScope = SearchScope.Subtree;
        DS.Filter = "(&(|(objectClass=User)(objectClass=Group)
        (objectClass=Computer))
        (|(samAccountName=" + SearchString + ")
        (name=" + SearchString + ")(dn=" + SearchString + ")
        (displayName=" + SearchString + ")))";
        DS.SizeLimit = 1000;
        DS.PageSize = 1000;
        SR = DS.FindOne();
        if (SR != null)
        {
            _deUser = SR.GetDirectoryEntry();
            string sid = GetSid(_deUser);
            if (!String.IsNullOrEmpty(sid))
            {
                textBox1.Text = sid;
            }
            else
            {
                textBox1.Text = "null sid returned";
            }
        }
        else
        {
            textBox1.Text = "Object Not Found";
        }
    }

Here is the play by play: 1) Wrap the ADConnection in a using statement, this is the syntactic equivalent of a try/finally statement.

2) Create a Directory Searcher object- this object performs our query.

3) Create a Search Result object- this will be populated by our query should it produce a result.

4) Initialize the Directory Searcher parameters-

SearchRoot = the catalog root

SearchScope = the depth of the search of the catalog. There is an enumeration that provides the 3 values available: base(the root object, and ONLY the root object), OneLevel(the base object plus one hiearchical level down), Subtree(recursively through the entire directory tree)

Filter = this specifies the types of objects and text filters we want to apply to the search. The syntax of the search string is a function of And Or operators, grouped by parenthesis. In this case, we want a pretty broad filter that will return Computer, Group and User objects. We then will query based on several directory attributes: samAccountName, name, distinguishedName, or displayName. The format is as such:(&(|(ObjectType1)(ObjectType2)(ObjectType3))(|(SearchAttribute1)(SearchAttribute2) (SearchAttribute3)(SearchAttribute4)))

Here is the filter string I used for this example:

 DS.Filter = "(&(|(objectClass=User)(objectClass=Group)
        (objectClass=Computer))
        (|(samAccountName=" + SearchString + ")
        (name=" + SearchString + ")(dn=" + SearchString + ")
        (displayName=" + SearchString + ")))";

So notice that we are using the OR qualifier for the object class portion of the filter, we want Users, Groups and Computers. We use the same OR qualifier for the Attributes we are searching. The samAccountName is more commonly known as the User ID, the name attribute is the same as common name(cn) example Craig Albright, the distinguished name(dn) attribute is basically the fully qualified name of the AD object. (example CN=Craig Albright, OU=User Accounts, DC=domain, DC=com) Display Name is typically used to set a name value if a user has a preferred name to his/her given name.

DS.SizeLimit = 1000;

This sets the number of objects that should be returned by the directory searcher. In this case we are only dealing with 1 object at a time but it is nice to be aware of this setting, for the cases when wildcard searches are needed. Also note that Domain Controllers have a similar setting on the server side that can limit the number of returned results.

DS.PageSize = 1000;

This property is used if you want to Page the result set from the directory search. This sets a "cursor" of sorts to the Domain Controller to let it know where it left off. (useful for returning results in conjunction with the "refreshCache" method for paging.)

SR = DS.FindOne();

This method returns the first object returned from active directory as a "SearchResult" object. All we need to do to get the sid is get the Directory Entry for this object, which is accomplished thusly:

if (SR != null)
	{
	  _deUser = SR.GetDirectoryEntry();
	   string sid = GetSid(_deUser);
	}

So we first check to make sure the SearchResult is not null, then call the GetDirectoryEntry() method which returns a DirectoryEntry object based on the SearchResult context. the next line is where we get the objects SID. I created the GetSid method, which converts the byte array stored in the DirectoryEntry "objectSid" property. The code for GetSid is listed below in Part Three.

Step Three: getting the SID.

This is the method used to get the string value of the Sid.

private string GetSid(DirectoryEntry _deUser)
	{
	   byte[] sidBytes = (byte[])_deUser.Properties["objectSid"].Value;
	   SecurityIdentifier userSid = new SecurityIdentifier(sidBytes, 0);
	   return userSid.ToString();
	}

This is a pretty simple method: it accepts the directory entry object, gets/casts the value of the objectSid property to a byte array, and finally creates a SecurityIdentifier object based on this byte array. This particular constructor uses a byte array and offset parameter signifying the start index of the SID. For our purposes, an offset of 0 is passed. Finally, we use the ToString() method of userSid to return the SID value in text form.

Step Four: The Glue/GUI code

Most of the code listed above comes from the event handler delegate named SearchButton_Click. SearchButton is the trigger for all the activity in this application. To make the form, open the designer, drag a text box onto the form, name it SearchStringTxt. Drag a button to the form, name it SearchButton, set the button text to "Get Sid!". Drag another TextBox to the form, name it SidResultBox. Optionally you can drag a label above each text box and describe their functions. When finished, the GUI should look like this:

ScreenShot

So thats it, now you can create an application that connects to Active Directory and retrieves a user/computer/group. Here is the full source:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.DirectoryServices;
using System.Security.Principal;

namespace SIDVicious
{
    public partial class SidVicious : Form
    {
        public SidVicious()
        {
            InitializeComponent();
        }

        private void SearchButton_Click(object sender, EventArgs e)
        {
            try
            {
                string SearchString = SearchStringTxt.Text;
                DirectoryEntry _deUser;
                DirectoryEntry AD = null;
                using (DirectoryEntry RootDse = new DirectoryEntry("GC:"))
                {
                    foreach (DirectoryEntry root in RootDse.Children)
                    {
                        AD = root;
                        break;
                    }
                }
                using (AD)
                {
                    DirectorySearcher DS = new DirectorySearcher();
                    SearchResult SR;
                    DS.SearchRoot = AD;
                    DS.SearchScope = SearchScope.Subtree;
                    DS.Filter = "(&(|(objectClass=User)(objectClass=Group)
                    (objectClass=Computer))(|(samAccountName=" + SearchString + ")
                    (name=" + SearchString + ")(dn=" + SearchString + ")
                    (displayName=" + SearchString + ")))";
                    DS.SizeLimit = 1000;
                    DS.PageSize = 1000;
                    SR = DS.FindOne();
                    if (SR != null)
                    {
                        _deUser = SR.GetDirectoryEntry();
                        string sid = GetSid(_deUser);
                        if (!String.IsNullOrEmpty(sid))
                        {
                            SidResultBox.Text = sid;
                        }
                        else
                        {
                            SidResultBox.Text = "null sid returned";
                        }
                    }
                    else
                    {
                        SidResultBox.Text = "Object Not Found";
                    }
                }
            }
            catch (Exception ex)
            {
                SidResultBox.Text = ex.Message;
            }
            

        }

        private string GetSid(DirectoryEntry _deUser)
        {
           byte[] sidBytes = (byte[])_deUser.Properties["objectSid"].Value;
           SecurityIdentifier userSid = new SecurityIdentifier(sidBytes, 0);
           return userSid.ToString();
        }
    }
}

Compile, run, modify, expand upon, and/or enjoy to your hearts delight. Hopefully this has been a helpful tutorial for doing some basic Active Directory Querying. Feel free to let me know what you think.

If you just want a SID gathering tool without the trouble, download it Here

Your Name:

Comment: