如何在C#中获取登录SID

11

如何在 C# .net 中检索 Windows 登录 SID?(不是用户 SID,而是每个会话的唯一新 SID)


1
在Windows环境中,SID代表安全标识符,而不是会话ID。要获取会话ID,请使用System.Diagnostics.Process.GetCurrentProcess().SessionId。有关更多详细信息,请参见我的答案 - mistika
4个回答

8

恐怕您需要使用P/Invoke。在pinvoke.net网站上有一个示例,演示如何进行操作(请参见页面底部):

Result = GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenSessionId , TokenInformation , TokenInfLength , out TokenInfLength );

请注意,我只改变了一个行的示例,我用 TOKEN_INFORMATION_CLASS.TokenSessionId 替换了 TOKEN_INFORMATION_CLASS.TokenUser,这正是您需要的。
希望这能帮到您。
更新:以下是可工作代码(至少在我的机器上)。
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace LinqTest
{
    public class ClsLookupAccountName
    {
        public const uint SE_GROUP_LOGON_ID = 0xC0000000; // from winnt.h
        public const int TokenGroups = 2; // from TOKEN_INFORMATION_CLASS

        enum TOKEN_INFORMATION_CLASS
        {
            TokenUser = 1,
            TokenGroups,
            TokenPrivileges,
            TokenOwner,
            TokenPrimaryGroup,
            TokenDefaultDacl,
            TokenSource,
            TokenType,
            TokenImpersonationLevel,
            TokenStatistics,
            TokenRestrictedSids,
            TokenSessionId,
            TokenGroupsAndPrivileges,
            TokenSessionReference,
            TokenSandBoxInert,
            TokenAuditPolicy,
            TokenOrigin
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SID_AND_ATTRIBUTES
        {
            public IntPtr Sid;
            public uint Attributes;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct TOKEN_GROUPS
        {
            public int GroupCount;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
            public SID_AND_ATTRIBUTES[] Groups;
        };

        // Using IntPtr for pSID instead of Byte[]
        [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool ConvertSidToStringSid(IntPtr pSID, out IntPtr ptrSid);

        [DllImport("kernel32.dll")]
        static extern IntPtr LocalFree(IntPtr hMem);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool GetTokenInformation(
            IntPtr TokenHandle,
            TOKEN_INFORMATION_CLASS TokenInformationClass,
            IntPtr TokenInformation,
            int TokenInformationLength,
            out int ReturnLength);

        public static string GetLogonId()
        {
            int TokenInfLength = 0;
            // first call gets lenght of TokenInformation
            bool Result = GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenGroups, IntPtr.Zero, TokenInfLength, out TokenInfLength);
            IntPtr TokenInformation = Marshal.AllocHGlobal(TokenInfLength);
            Result = GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenGroups, TokenInformation, TokenInfLength, out TokenInfLength);

            if (!Result)
            {
                Marshal.FreeHGlobal(TokenInformation);
                return string.Empty;
            }

            string retVal = string.Empty;
            TOKEN_GROUPS groups = (TOKEN_GROUPS)Marshal.PtrToStructure(TokenInformation, typeof(TOKEN_GROUPS));
            int sidAndAttrSize = Marshal.SizeOf(new SID_AND_ATTRIBUTES());
            for (int i = 0; i < groups.GroupCount; i++)
            {
                SID_AND_ATTRIBUTES sidAndAttributes = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(
                    new IntPtr(TokenInformation.ToInt64() + i * sidAndAttrSize + IntPtr.Size), typeof(SID_AND_ATTRIBUTES));
                if ((sidAndAttributes.Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
                {
                    IntPtr pstr = IntPtr.Zero;
                    ConvertSidToStringSid(sidAndAttributes.Sid, out pstr);
                    retVal = Marshal.PtrToStringAuto(pstr);
                    LocalFree(pstr);
                    break;
                }
            }

            Marshal.FreeHGlobal(TokenInformation);
            return retVal;
        }
    }
}

注意:我在我的x64机器上进行了测试,请注意TokenInformation.ToInt64()代码片段,也许你应该将其替换为TokenInformation.ToInt32()


很遗憾,它不起作用。它没有返回任何内容。根据这个(http://msdn.microsoft.com/en-us/library/aa379626%28VS.85%29.aspx),它只在终端服务器上工作,所以我可能不需要会话ID。 - Jos
我添加了另一个版本的代码,它在我的机器上运行正常。如果有什么不清楚的地方,请随时问我。 - Igor Korkhov
谢谢!这个完美地运作了。我使用了TokenLogonSid而不是TokenGroups,但那只适用于Windows Vista和7。 - Jos
这非常有帮助,谢谢。鉴于TOKEN_GROUPS.GroupCount是DWORD,为什么您要使用“TokenInformation.ToInt64() + i * sidAndAttrSize + IntPtr.Size”而不是“TokenInformation.ToInt64() + i * sidAndAttrSize + 4”? 这是有意为之吗?(参考我的相关问题:https://dev59.com/zVfUa4cB1Zd3GeqPHWLZ) - Rory

1

我知道这是一篇旧文章。我遇到了这个问题,因为我必须获取ICA会话ID和RDP会话ID,以便程序为每种类型的远程连接收集正确的变量。当前会话ID位于Regedit HKEY_CURRENT_USER\Remote*中。由于我找不到任何替代WTS的方法,所以我在这里发布我的解决方案。

    // Prints out ICA or RDP session ID of current user 

using System;
using Microsoft.Win32;

namespace ViaRegedit
{
    class Program03
    {
        static void Main(string[] args)
        {
            // Obtain an instance of RegistryKey for the CurrentUser registry 
            RegistryKey rkCurrentUser = Registry.CurrentUser;
            // Obtain the test key (read-only) and display it.
            RegistryKey rkTest = rkCurrentUser.OpenSubKey("Remote");
            foreach (string valueName in rkTest.GetSubKeyNames())
            {
                //Getting path to RDP/Citrix session ID
                string RDPICApath = "";
                if (rkTest.OpenSubKey(valueName) != null && rkTest.OpenSubKey(valueName) != null) { RDPICApath = rkTest.OpenSubKey(valueName).ToString(); }
                Console.WriteLine("Getting CurrentUser ICA-RDP path from string = " + RDPICApath);

                //Seperating RDPICApath to get session number
                string RDPICAnumber = RDPICApath.Substring(RDPICApath.LastIndexOf('\\') + 1);
                Console.WriteLine("Current User RDPICAnumber = " + RDPICAnumber);
            }
            rkTest.Close();
            rkCurrentUser.Close();
            Console.ReadLine();
        }
    }

}

1

System.Security.Principal.WindowsIdentity.GetCurrent().User.AccountDomainSid - 这个可能管用吧?


1
这不是SilverSkin所询问的登录会话ID。 - Igor Korkhov
1
我认为用户SID在会话之间保持不变,但每次登录都会创建一个令牌,可以通过...GetCurrent().User.Token访问它吗? - Kalle
这个token是我需要的,但是我必须将其转换为可用的SID。 - Jos
你需要它做什么?冒充和其他操作应该通过令牌实现。 - Kalle
我有一个外部应用程序,它创建了一个以登录 UID 命名的命名管道。没有它,我无法确定管道的名称。 - Jos

1

我刚刚花了很长时间使用TOKEN_USER等方法获取SID,然后在C#中发现了一种快捷方式。您仍需要获取进程句柄(例如https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.handle?view=netcore-3.1),然后使用P/invoke获取令牌:

OpenProcessToken(hProcess, TOKEN_READ, out IntPtr hToken))

但是一旦你获得了令牌,你就不需要执行任何讨厌的GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUser...操作,只需要使用:

var winId = System.Security.Principal.WindowsIdentity(hToken);

...然后你就可以从winId中获取所有想要的信息(包括SID)。

别忘了在使用完hToken后调用CloseHandle(hToken),并在处理完hProcess之后关闭它!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接