获取机器SID(包括主域控制器)

发布于 2024-08-20 21:36:34 字数 2916 浏览 5 评论 0原文

我需要在 C# 中获取计算机 SID(不是计算机帐户的 SID)。计算机指定为主机名,不一定是本地计算机,可以是域计算机或工作组计算机。我使用这个帮助程序类来调用 LookupAccountName API 函数:

    private static class Helper
    {
        internal enum SID_NAME_USE
        {
            SidTypeUser = 1,
            SidTypeGroup,
            SidTypeDomain,
            SidTypeAlias,
            SidTypeWellKnownGroup,
            SidTypeDeletedAccount,
            SidTypeInvalid,
            SidTypeUnknown,
            SidTypeComputer
        }

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool LookupAccountName(
            string systemName,
            string accountName,
            byte[] sid,
            ref int sidLen,
            System.Text.StringBuilder domainName,
            ref int domainNameLen,
            out SID_NAME_USE peUse);

        public static SecurityIdentifier LookupAccountName(
            string systemName,
            string accountName,
            out string strDomainName,
            out SID_NAME_USE accountType)
        {
            const int ERROR_INSUFFICIENT_BUFFER = 122;

            int lSidSize = 0;
            int lDomainNameSize = 0;

            //First get the required buffer sizes for SID and domain name.
            LookupAccountName(systemName,
                              accountName,
                              null,
                              ref lSidSize,
                              null,
                              ref lDomainNameSize,
                              out accountType);

            if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
            {
                //Allocate the buffers with actual sizes that are required
                //for SID and domain name.
                byte[] sid = new byte[lSidSize];
                var sbDomainName = new System.Text.StringBuilder(lDomainNameSize);

                if (LookupAccountName(systemName,
                                      accountName,
                                      sid,
                                      ref lSidSize,
                                      sbDomainName,
                                      ref lDomainNameSize,
                                      out accountType))
                {
                    strDomainName = sbDomainName.ToString();
                    return new SecurityIdentifier(sid, 0);
                }
            }

            throw new Win32Exception();
        }
    }

并像这样使用它:

Helper.SID_NAME_USE accountType;
string refDomain;
SecurityIdentifier sid = Helper.LookupAccountName("falcon.mydomain.local", "falcon", out refDomain, out accountType);  //Domain computer

SecurityIdentifier sid = Helper.LookupAccountName("rat", "rat", out refDomain, out accountType);  //Workgroup computer

我唯一的问题是,如果计算机是主域控制器,则这不起作用(在这种情况下我需要获取域 SID)。

I need to get machine SID (not computer account's SID) in C#. Computer is specified be host name, it is't necessarily local computer and it can be domain computer or workgroup computer. I using this helper class to call LookupAccountName API function:

    private static class Helper
    {
        internal enum SID_NAME_USE
        {
            SidTypeUser = 1,
            SidTypeGroup,
            SidTypeDomain,
            SidTypeAlias,
            SidTypeWellKnownGroup,
            SidTypeDeletedAccount,
            SidTypeInvalid,
            SidTypeUnknown,
            SidTypeComputer
        }

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool LookupAccountName(
            string systemName,
            string accountName,
            byte[] sid,
            ref int sidLen,
            System.Text.StringBuilder domainName,
            ref int domainNameLen,
            out SID_NAME_USE peUse);

        public static SecurityIdentifier LookupAccountName(
            string systemName,
            string accountName,
            out string strDomainName,
            out SID_NAME_USE accountType)
        {
            const int ERROR_INSUFFICIENT_BUFFER = 122;

            int lSidSize = 0;
            int lDomainNameSize = 0;

            //First get the required buffer sizes for SID and domain name.
            LookupAccountName(systemName,
                              accountName,
                              null,
                              ref lSidSize,
                              null,
                              ref lDomainNameSize,
                              out accountType);

            if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
            {
                //Allocate the buffers with actual sizes that are required
                //for SID and domain name.
                byte[] sid = new byte[lSidSize];
                var sbDomainName = new System.Text.StringBuilder(lDomainNameSize);

                if (LookupAccountName(systemName,
                                      accountName,
                                      sid,
                                      ref lSidSize,
                                      sbDomainName,
                                      ref lDomainNameSize,
                                      out accountType))
                {
                    strDomainName = sbDomainName.ToString();
                    return new SecurityIdentifier(sid, 0);
                }
            }

            throw new Win32Exception();
        }
    }

and using it like this:

Helper.SID_NAME_USE accountType;
string refDomain;
SecurityIdentifier sid = Helper.LookupAccountName("falcon.mydomain.local", "falcon", out refDomain, out accountType);  //Domain computer

SecurityIdentifier sid = Helper.LookupAccountName("rat", "rat", out refDomain, out accountType);  //Workgroup computer

My only problem is, that this is not working if computer is primary domain controller (i need to obtain domain SID in that case).

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

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

发布评论

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

评论(2

迷爱 2024-08-27 21:36:34

对于大多数计算机来说,您似乎执行以下操作:

LookupAccountName("", "ComputerName", ...);
ConvertSidToStringSid(...)

但对于域控制器,您必须在计算机名称参数中添加美元符号,然后删除返回的 SID 中的最后一段。

It appears that for most computers you do the following:

LookupAccountName("", "ComputerName", ...);
ConvertSidToStringSid(...)

But for domain controllers you have to add a dollar sign to the computer name parameter, and then delete the last segment in the returned SID.

青衫负雪 2024-08-27 21:36:34

您可以通过 PolicyAccountDomainInformation 接收计算机 SID(与 psgetsid 的输出匹配):

using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;

internal class Program
{
    private static void Main(string[] args)
    {
        unsafe
        {
            var lsaOA = new LSA_OBJECT_ATTRIBUTES() { Length = 24 };
            var result = LsaOpenPolicy(IntPtr.Zero, lsaOA, 1 /* FILE_READ_DATA */, out var handle);
            if (result != 0)
            {
                throw new Win32Exception(result);
            }

            using (handle)
            {
                result = LsaQueryInformationPolicy(handle, 5 /* PolicyAccountDomainInformation */, out var policyInfo);
                if (result != 0)
                {
                    throw new Win32Exception(result);
                }

                using (policyInfo)
                {
                    var domInfo = (POLICY_ACCOUNT_DOMAIN_INFO*)policyInfo.DangerousGetHandle();
                    var sid = new SecurityIdentifier(domInfo->DomainSid);
                    Console.WriteLine(sid);
                }
            }
        }
    }

    [DllImport("advapi32.dll", ExactSpelling = true)]
    internal static extern unsafe int LsaOpenPolicy(IntPtr SystemName, in LSA_OBJECT_ATTRIBUTES ObjectAttributes, uint DesiredAccess, out LsaCloseSafeHandle PolicyHandle);

    [DllImport("advapi32.dll", ExactSpelling = true)]
    internal static extern unsafe int LsaQueryInformationPolicy(LsaCloseSafeHandle PolicyHandle, int InformationClass, out LsaFreeSafeHandle Buffer);

    [DllImport("advapi32.dll", ExactSpelling = true)]
    internal static extern int LsaClose(IntPtr ObjectHandle);

    [DllImport("advapi32.dll", ExactSpelling = true)]
    internal static extern int LsaFreeMemory(IntPtr Buffer);

    internal partial struct LSA_OBJECT_ATTRIBUTES
    {
        internal uint Length;
        internal IntPtr RootDirectory;
        internal IntPtr ObjectName;
        internal uint Attributes;
        internal IntPtr SecurityDescriptor;
        internal IntPtr SecurityQualityOfService;
    }

    internal partial struct POLICY_ACCOUNT_DOMAIN_INFO
    {
        internal LSA_UNICODE_STRING DomainName;
        internal IntPtr DomainSid;
    }

    internal partial struct LSA_UNICODE_STRING
    {
        internal ushort Length;
        internal ushort MaximumLength;
        internal IntPtr Buffer;
    }

    internal class LsaCloseSafeHandle : SafeHandleMinusOneIsInvalid
    {
        public LsaCloseSafeHandle() : base(true) { }
        protected override bool ReleaseHandle() => LsaClose(this.handle) == 0;
    }

    internal class LsaFreeSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        public LsaFreeSafeHandle() : base(true) { }
        protected override bool ReleaseHandle() => LsaFreeMemory(this.handle) == 0;
    }
}

如果您想要计算机帐户 sid,请将 LookupAccountName一起使用>MachineName$ (再次匹配 psgetsid):

static void Main(string[] args)
{
    try
    {
        var sid = new byte[1024];
        var cbSid = sid.Length;
        int cbReferencedDomainName = 0;
        var domainName = new StringBuilder(1024);
        uint domainNameLen = (uint)domainName.Capacity;
        if (!LookupAccountName(null, Environment.MachineName + "$", sid, ref cbSid, domainName, ref domainNameLen, out int peUse))
        {
            throw new Win32Exception();
        }

        Console.WriteLine(new System.Security.Principal.SecurityIdentifier(sid, 0));
    }
    catch (Win32Exception ex) when (ex.NativeErrorCode == 1332 /* ERROR_NONE_MAPPED */)
    {
        Console.Error.WriteLine("Not domain joined");
    }
}

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
internal static extern bool LookupAccountName(string machineName, string accountName, byte[] sid,
                            ref int sidLen, [Out] StringBuilder domainName, ref uint domainNameLen, out int peUse);

You can receive the machine SID (matching the output of psgetsid) via PolicyAccountDomainInformation:

using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;

internal class Program
{
    private static void Main(string[] args)
    {
        unsafe
        {
            var lsaOA = new LSA_OBJECT_ATTRIBUTES() { Length = 24 };
            var result = LsaOpenPolicy(IntPtr.Zero, lsaOA, 1 /* FILE_READ_DATA */, out var handle);
            if (result != 0)
            {
                throw new Win32Exception(result);
            }

            using (handle)
            {
                result = LsaQueryInformationPolicy(handle, 5 /* PolicyAccountDomainInformation */, out var policyInfo);
                if (result != 0)
                {
                    throw new Win32Exception(result);
                }

                using (policyInfo)
                {
                    var domInfo = (POLICY_ACCOUNT_DOMAIN_INFO*)policyInfo.DangerousGetHandle();
                    var sid = new SecurityIdentifier(domInfo->DomainSid);
                    Console.WriteLine(sid);
                }
            }
        }
    }

    [DllImport("advapi32.dll", ExactSpelling = true)]
    internal static extern unsafe int LsaOpenPolicy(IntPtr SystemName, in LSA_OBJECT_ATTRIBUTES ObjectAttributes, uint DesiredAccess, out LsaCloseSafeHandle PolicyHandle);

    [DllImport("advapi32.dll", ExactSpelling = true)]
    internal static extern unsafe int LsaQueryInformationPolicy(LsaCloseSafeHandle PolicyHandle, int InformationClass, out LsaFreeSafeHandle Buffer);

    [DllImport("advapi32.dll", ExactSpelling = true)]
    internal static extern int LsaClose(IntPtr ObjectHandle);

    [DllImport("advapi32.dll", ExactSpelling = true)]
    internal static extern int LsaFreeMemory(IntPtr Buffer);

    internal partial struct LSA_OBJECT_ATTRIBUTES
    {
        internal uint Length;
        internal IntPtr RootDirectory;
        internal IntPtr ObjectName;
        internal uint Attributes;
        internal IntPtr SecurityDescriptor;
        internal IntPtr SecurityQualityOfService;
    }

    internal partial struct POLICY_ACCOUNT_DOMAIN_INFO
    {
        internal LSA_UNICODE_STRING DomainName;
        internal IntPtr DomainSid;
    }

    internal partial struct LSA_UNICODE_STRING
    {
        internal ushort Length;
        internal ushort MaximumLength;
        internal IntPtr Buffer;
    }

    internal class LsaCloseSafeHandle : SafeHandleMinusOneIsInvalid
    {
        public LsaCloseSafeHandle() : base(true) { }
        protected override bool ReleaseHandle() => LsaClose(this.handle) == 0;
    }

    internal class LsaFreeSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        public LsaFreeSafeHandle() : base(true) { }
        protected override bool ReleaseHandle() => LsaFreeMemory(this.handle) == 0;
    }
}

If you want the computer account sid, use LookupAccountName with MachineName$ (again matching psgetsid):

static void Main(string[] args)
{
    try
    {
        var sid = new byte[1024];
        var cbSid = sid.Length;
        int cbReferencedDomainName = 0;
        var domainName = new StringBuilder(1024);
        uint domainNameLen = (uint)domainName.Capacity;
        if (!LookupAccountName(null, Environment.MachineName + "
quot;, sid, ref cbSid, domainName, ref domainNameLen, out int peUse))
        {
            throw new Win32Exception();
        }

        Console.WriteLine(new System.Security.Principal.SecurityIdentifier(sid, 0));
    }
    catch (Win32Exception ex) when (ex.NativeErrorCode == 1332 /* ERROR_NONE_MAPPED */)
    {
        Console.Error.WriteLine("Not domain joined");
    }
}

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
internal static extern bool LookupAccountName(string machineName, string accountName, byte[] sid,
                            ref int sidLen, [Out] StringBuilder domainName, ref uint domainNameLen, out int peUse);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文