Get User SID From Logon ID (Windows XP and Up)
Asked Answered
M

4

5

I have a Windows service that needs to access registry hives under HKEY_USERS when users log on, either locally or via Terminal Server. I'm using a WMI query on win32_logonsession to receive events when users log on, and one of the properties I get from that query is a LogonId. To figure out which registry hive I need to access, now, I need the users's SID, which is used as a registry key name beneath HKEY_USERS.

In most cases, I can get this by doing a RelatedObjectQuery like so (in C#):

RelatedObjectQuery relatedQuery = new RelatedObjectQuery( "associators of {Win32_LogonSession.LogonId='" + logonID + "'} WHERE AssocClass=Win32_LoggedOnUser Role=Dependent" );

where "logonID" is the logon session ID from the session query. Running the RelatedObjectQuery will generally give me a SID property that contains exactly what I need.

There are two issues I have with this. First and most importantly, the RelatedObjectQuery will not return any results for a domain user that logs in with cached credentials, disconnected from the domain. Second, I'm not pleased with the performance of this RelatedObjectQuery --- it can take up to several seconds to execute.

Here's a quick and dirty command line program I threw together to experiment with the queries. Rather than setting up to receive events, this just enumerates the users on the local machine:

using System;
using System.Collections.Generic;
using System.Text;
using System.Management;

namespace EnumUsersTest
{
    class Program
    {
        static void Main( string[] args )
        {
            ManagementScope scope = new ManagementScope( "\\\\.\\root\\cimv2" );

            string queryString = "select * from win32_logonsession";                          // for all sessions
            //string queryString = "select * from win32_logonsession where logontype = 2";     // for local interactive sessions only

            ManagementObjectSearcher sessionQuery = new ManagementObjectSearcher( scope, new SelectQuery( queryString ) );
            ManagementObjectCollection logonSessions = sessionQuery.Get();
            foreach ( ManagementObject logonSession in logonSessions )
            {
                string logonID = logonSession["LogonId"].ToString();
                Console.WriteLine( "=== {0}, type {1} ===", logonID, logonSession["LogonType"].ToString() );
                RelatedObjectQuery relatedQuery = new RelatedObjectQuery( "associators of {Win32_LogonSession.LogonId='" + logonID + "'} WHERE AssocClass=Win32_LoggedOnUser Role=Dependent" );
                ManagementObjectSearcher userQuery = new ManagementObjectSearcher( scope, relatedQuery );
                ManagementObjectCollection users = userQuery.Get();
                foreach ( ManagementObject user in users )
                {
                    PrintProperties( user.Properties );
                }
            }

            Console.WriteLine( "\nDone! Press a key to exit..." );
            Console.ReadKey( true );
        }


        private static void PrintProperty( PropertyData pd )
        {
            string value = "null";
            string valueType = "n/a";
            if ( pd.Value != null )
            {
                value = pd.Value.ToString();
                valueType = pd.Value.GetType().ToString();
            }

            Console.WriteLine( "  \"{0}\" = ({1}) \"{2}\"", pd.Name, valueType, value );
        }


        private static void PrintProperties( PropertyDataCollection properties )
        {
            foreach ( PropertyData pd in properties )
            {
                PrintProperty( pd );
            }
        }
    }
}

So... is there way to quickly and reliably obtain the user SID given the information I retrieve from WMI, or should I be looking at using something like SENS instead?

Menthol answered 28/4, 2010 at 7:1 Comment(1)
possible duplicate of Convert a username to a SID string in C#/.NETAylsworth
C
3

I asked a very similar question a while back and got this answer: how to get a SID from a windows username.

I was planning on using SystemEvents to detect when a user logs on to windows, then looping through the logged on users list at that point to detect all the logged on users. (Here's my question, about all this including references for detecting logons and current users.)

If you decide on an approach please post an update - I'd be interested to hear what you find works well.

Celesta answered 28/4, 2010 at 13:8 Comment(0)
M
2

Another simple way: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion \ProfileList

Modernity answered 4/1, 2011 at 10:21 Comment(1)
That'll only work where a local profile exists for the user. Consider server in domain: may have many users authenticate, but only a few admins will have profiles.Aylsworth
M
1

Another working answer (code in VB.Net)

Public Function GetSIDfromAccName(ByVal strAccName As String) As String
        Debug.WriteLine("***WMI-GetSIDfromAccName***")
        Dim strSID As String = ""
        Try
            Dim wmiClass As System.Management.SelectQuery = New System.Management.SelectQuery(("Select * from Win32_UserAccount where Name='" _
              + (strAccName + "'")))
            Dim wmiSearcher As System.Management.ManagementObjectSearcher = New System.Management.ManagementObjectSearcher(wmiClass)
            For Each val As System.Management.ManagementBaseObject In wmiSearcher.Get
                strSID = val("SID").ToString
            Next
        Catch e As Exception
            Debug.WriteLine(e.ToString)
        End Try
        Return strSID
    End Function
Modernity answered 4/1, 2011 at 10:46 Comment(0)
S
1

Powershell is easier.

Function GetSIDfromAcctName()
{
$myacct = Get-WmiObject Win32_UserAccount -filter "Name = '$env:USERNAME " 
write-host Name: $myacct.name
Write-Host SID : $myacct.sid
}
Sauter answered 2/7, 2012 at 11:7 Comment(1)
I don't think writing a Windows service in Powershell is an option, at present. I'll certainly keep this in mind if I ever have this need in a scripting situation, though.Menthol

© 2022 - 2024 — McMap. All rights reserved.