从登录 ID 获取用户 SID(Windows XP 及更高版本)

发布于 2024-08-31 05:46:02 字数 3063 浏览 3 评论 0原文

我有一个 Windows 服务,当用户本地或通过终端服务器登录时,需要访问 HKEY_USERS 下的注册表配置单元。我在 win32_logonsession 上使用 WMI 查询来接收用户登录时的事件,从该查询获得的属性之一是 LogonId。为了确定我需要访问哪个注册表配置单元,现在我需要用户的 SID,它用作 HKEY_USERS 下的注册表项名称。

在大多数情况下,我可以通过像这样(在 C# 中)执行相关对象查询来获得此信息:

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

其中“logonID”是会话查询中的登录会话 ID。运行相关对象查询通常会给我一个 SID 属性,其中包含我所需要的内容。

我对此有两个问题。首先也是最重要的是,RelatedObjectQuery 不会为使用缓存凭据登录且与域断开连接的域用户返回任何结果。其次,我对这个RelatedObjectQuery 的性能并不满意——执行它可能需要几秒钟的时间。

这是我为了试验查询而编写的一个快速而肮脏的命令行程序。这不是设置接收事件,而是枚举本地计算机上的用户:

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 );
            }
        }
    }
}

那么...有没有办法根据我从 WMI 检索的信息快速可靠地获取用户 SID,或者我应该考虑使用类似的方法换成SENS?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

魔法唧唧 2024-09-07 05:46:02

我问了一个非常类似的问题 不久前得到了这个答案:如何从 Windows 用户名获取 SID

我计划使用 SystemEvents 来检测用户何时登录到 Windows,然后循环遍历此时的登录用户列表以检测所有登录用户。 (这是我的问题,关于所有这些包括用于检测登录和当前用户的参考。)

如果您决定采用一种方法,请发布更新 - 我很想听听您发现哪种方法效果很好。

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.

木落 2024-09-07 05:46:02

另一种简单的方法:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList

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

榕城若虚 2024-09-07 05:46:02

另一个有效的答案(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

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
秋意浓 2024-09-07 05:46:02

Powershell 更容易。

Function GetSIDfromAcctName()
{
$myacct = Get-WmiObject Win32_UserAccount -filter "Name = '$env:USERNAME " 
write-host Name: $myacct.name
Write-Host SID : $myacct.sid
}

Powershell is easier.

Function GetSIDfromAcctName()
{
$myacct = Get-WmiObject Win32_UserAccount -filter "Name = '$env:USERNAME " 
write-host Name: $myacct.name
Write-Host SID : $myacct.sid
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文