如何读取计算机账户安全令牌以获取其组成员身份
获取计算机账户安全令牌的组成员身份
要获取计算机账户的组成员身份,不能直接用[Security.Principal.WindowsIdentity]::GetCurrent()——这个方法返回的是当前用户线程的安全令牌,而非计算机账户的令牌。DeviceClaims返回空也是因为当前令牌不属于设备(计算机)身份。
以下是不依赖Active Directory的PowerShell解决方案,通过调用Windows原生API获取计算机账户的令牌并枚举组:
实现步骤
- 定义所需的Windows API和结构体(用于P/Invoke)
- 获取系统核心进程(如
lsass.exe)的句柄,它运行在本地系统上下文,包含计算机账户的组信息 - 打开该进程的安全令牌
- 查询令牌中的组信息并解析输出
完整代码
# 定义API和结构体 Add-Type @" using System; using System.Runtime.InteropServices; public class TokenUtils { public enum TOKEN_INFORMATION_CLASS { TokenGroups = 2 } [StructLayout(LayoutKind.Sequential)] public struct SID_AND_ATTRIBUTES { public IntPtr Sid; public uint Attributes; } [StructLayout(LayoutKind.Sequential)] public struct TOKEN_GROUPS { public uint GroupCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public SID_AND_ATTRIBUTES[] Groups; } [DllImport("advapi32.dll", SetLastError = true)] public static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CloseHandle(IntPtr hObject); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool ConvertSidToStringSid(IntPtr Sid, out IntPtr StringSid); } "@ # 定义令牌访问权限 $TOKEN_QUERY = 0x0008 # 获取lsass进程句柄(本地系统运行的进程) $lsassProcess = Get-Process lsass -ErrorAction Stop $processHandle = $lsassProcess.Handle # 打开进程令牌 if (-not [TokenUtils]::OpenProcessToken($processHandle, $TOKEN_QUERY, [ref]$tokenHandle)) { throw "OpenProcessToken failed: $([System.ComponentModel.Win32Exception][System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" } try { # 先获取所需的缓冲区大小 [TokenUtils]::GetTokenInformation($tokenHandle, [TokenUtils+TOKEN_INFORMATION_CLASS]::TokenGroups, [IntPtr]::Zero, 0, [ref]$returnLength) | Out-Null # 分配缓冲区 $buffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($returnLength) try { if (-not [TokenUtils]::GetTokenInformation($tokenHandle, [TokenUtils+TOKEN_INFORMATION_CLASS]::TokenGroups, $buffer, $returnLength, [ref]$returnLength)) { throw "GetTokenInformation failed: $([System.ComponentModel.Win32Exception][System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" } # 解析TOKEN_GROUPS结构体 $tokenGroups = [System.Runtime.InteropServices.Marshal]::PtrToStructure($buffer, [Type][TokenUtils+TOKEN_GROUPS]) $groupCount = $tokenGroups.GroupCount # 遍历所有组 for ($i = 0; $i -lt $groupCount; $i++) { # 计算每个SID_AND_ATTRIBUTES的偏移 $sidOffset = $buffer + [System.Runtime.InteropServices.Marshal]::SizeOf([Type][TokenUtils+TOKEN_GROUPS]) + ($i * [System.Runtime.InteropServices.Marshal]::SizeOf([Type][TokenUtils+SID_AND_ATTRIBUTES])) $sidAndAttr = [System.Runtime.InteropServices.Marshal]::PtrToStructure($sidOffset, [Type][TokenUtils+SID_AND_ATTRIBUTES]) # 将SID转换为字符串 if ([TokenUtils]::ConvertSidToStringSid($sidAndAttr.Sid, [ref]$stringSid)) { $sidString = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($stringSid) # 获取SID对应的账户名 $account = New-Object System.Security.Principal.SecurityIdentifier($sidString) $groupName = $account.Translate([System.Security.Principal.NTAccount]).Value Write-Output "组名: $groupName | SID: $sidString | 属性: $($sidAndAttr.Attributes)" [System.Runtime.InteropServices.Marshal]::FreeHGlobal($stringSid) } } } finally { [System.Runtime.InteropServices.Marshal]::FreeHGlobal($buffer) } } finally { [TokenUtils]::CloseHandle($tokenHandle) }
代码说明
- 借助
lsass.exe进程(系统核心进程,运行在本地系统上下文)获取包含计算机账户组信息的令牌 - 通过
OpenProcessToken和GetTokenInformationAPI提取令牌中的组数据 - 将SID转换为可读的组名,全程无需查询Active Directory
内容的提问来源于stack exchange,提问作者user2871239
相关产品推荐
相关产品推荐

