So, that is intersting way, build from scratch SID structure And call some native ntdll s**t and than manual compare if needed (for system Sid only) i was lazy here .. > [Process]::GetCurrentProcess().Handle save me call 2 function, GetCurrentProcessId, NtOpenProcess Code: using namespace System.Reflection using namespace System.Diagnostics using namespace System.Management.Automation using namespace System.Runtime.InteropServices using namespace System.Security.Principal Clear-Host Write-Host <# Well-known SIDs https://learn.microsoft.com/en-us/windows/win32/secauthz/well-known-sids The SECURITY_NT_AUTHORITY (S-1-5) predefined identifier authority produces SIDs that are not universal but are meaningful only on Windows installations. You can use the following RID values with SECURITY_NT_AUTHORITY to create well-known SIDs. SECURITY_LOCAL_SYSTEM_RID String value: S-1-5-18 A special account used by the operating system. The following table has examples of domain-relative RIDs that you can use to form well-known SIDs for **local groups** (aliases). For more information about local and global groups, see Local Group Functions and Group Functions. DOMAIN_ALIAS_RID_ADMINS Value: 0x00000220 String value: S-1-5-32-544 A local group used for administration of the domain. ~~~~~~~~~~~~~~~~~~~ $isSystem = ValidateAccount -AccType System $isAdmin = ValidateAccount -AccType Administrator Write-Host "is Admin* Acc ? $isAdmin" Write-Host "is System Acc ? $isSystem" Write-Host #> function ValidateAccount { param ( [ValidateSet("System","Administrator")] [ValidateNotNullOrEmpty()] [string]$AccType ) if (!([PSTypeName]'TOKEN').Type) { Add-Type @' using System; using System.Runtime.InteropServices; using System.Security.Principal; public class TOKEN { [DllImport("ntdll.dll")] public static extern int RtlCheckTokenMembershipEx( IntPtr TokenHandle, IntPtr Sid, Int32 Flags, ref Boolean IsMember); [DllImport("ntdll.dll")] public static extern int NtOpenProcessToken( IntPtr NtOpenProcessToken, uint DesiredAccess, out IntPtr TokenHandle); [DllImport("ntdll.dll")] public static extern int NtQueryInformationToken( IntPtr TokenHandle, int TokenInformationClass, IntPtr TokenInformation, UInt32 TokenInformationLength, out uint ReturnLength ); } '@ } $isMember = $false $pSid = [Marshal]::AllocHGlobal(8+(4*10)) @(1,0,0,0,0,0,0,5) | ForEach -Begin { $i = 0 } -Process { [Marshal]::WriteByte($pSid, $i++, $_) } switch ($AccType) { "System" { # S-1-5-[18] // @([1],Count,0,0,0,0,0,[5] && 18) $hToken = [IntPtr]::Zero $hproc = [Process]::GetCurrentProcess().Handle if (0 -ne [TOKEN]::NtOpenProcessToken($hproc, 0x00000008, [ref]$hToken)) { throw "NtOpenProcessToken fail." } [UInt32]$ReturnLength = 0 $TokenInformation = [marshal]::AllocHGlobal(100) if (0 -ne [TOKEN]::NtQueryInformationToken($hToken,1,$TokenInformation, 100, [ref]$ReturnLength)) { throw "NtQueryInformationToken fail." } [Marshal]::WriteByte($pSid, 1, 1) [Marshal]::WriteInt32($pSid, 8, 18) $pUserSid = [Marshal]::ReadIntPtr($TokenInformation) $isMember = ([Marshal]::ReadByte($pUserSid,0) -eq [Marshal]::ReadByte($pSid,0)) -and ([Marshal]::ReadByte($pUserSid,1) -eq [Marshal]::ReadByte($pSid,1)) -and ([Marshal]::ReadByte($pUserSid,7) -eq [Marshal]::ReadByte($pSid,7)) -and ([Marshal]::ReadInt32($pUserSid,8) -eq [Marshal]::ReadInt32($pSid,8)) } "Administrator" { # S-1-5-[32]-[544] // @([1],Count,0,0,0,0,0,[5] && 32,544) [Marshal]::WriteByte($pSid, 1, 2) [Marshal]::WriteInt32($pSid, 8, 32) [Marshal]::WriteInt32($pSid, 12, 544) $ret = [TOKEN]::RtlCheckTokenMembershipEx(0, $pSid, 0, [ref]$isMember) } } [Marshal]::FreeHGlobal($pSid) return $isMember } $isSystem = ValidateAccount -AccType System $isAdmin = ValidateAccount -AccType Administrator Write-Host "is Admin* Acc ? $isAdmin" Write-Host "is System Acc ? $isSystem" Write-Host and if you like to also use NtOpenProcess, you can also use something like Code: $ProcID === > Call GetCurrentProcessId $handle = [IntPtr]::Zero $clientIdPtr = New-IntPtr -Size 100 -InitialValue $ProcID $attributesPtr = New-IntPtr -Size 100 -WriteSizeAtZero $ntStatus = $Global:ntdll::NtOpenProcess( [ref]$handle, $PROCESS_ALL_ACCESS, $attributesPtr, $clientIdPtr) if ($ntStatus -ne 0) { Start-Sleep 1 write-warning "get handle using PROCESS_ALL_ACCESS failed, re-try with PROCESS_CREATE_PROCESS" $ntStatus = $Global:ntdll::NtOpenProcess( [ref]$handle, $PROCESS_CREATE_PROCESS, $attributesPtr, $clientIdPtr) } return $handle
And if you want version, with Pain & Suffer Code: using namespace System.Reflection using namespace System.Diagnostics using namespace System.Management.Automation using namespace System.Runtime.InteropServices using namespace System.Security.Principal Clear-Host Write-Host <# Well-known SIDs https://learn.microsoft.com/en-us/windows/win32/secauthz/well-known-sids The SECURITY_NT_AUTHORITY (S-1-5) predefined identifier authority produces SIDs that are not universal but are meaningful only on Windows installations. You can use the following RID values with SECURITY_NT_AUTHORITY to create well-known SIDs. SECURITY_LOCAL_SYSTEM_RID String value: S-1-5-18 A special account used by the operating system. The following table has examples of domain-relative RIDs that you can use to form well-known SIDs for **local groups** (aliases). For more information about local and global groups, see Local Group Functions and Group Functions. DOMAIN_ALIAS_RID_ADMINS Value: 0x00000220 String value: S-1-5-32-544 A local group used for administration of the domain. ~~~~~~~~~~~~~~~~~~~ $isSystem = ValidateAccount -AccType System $isAdmin = ValidateAccount -AccType Administrator Write-Host "is Admin* Acc ? $isAdmin" Write-Host "is System Acc ? $isSystem" Write-Host #> function ValidateAccount { param ( [ValidateSet("System","Administrator")] [ValidateNotNullOrEmpty()] [string]$AccType ) if (!([PSTypeName]'TOKEN').Type) { Add-Type @' using System; using System.Runtime.InteropServices; using System.Security.Principal; public class TOKEN { [DllImport("kernelbase.dll")] public static extern IntPtr GetCurrentProcessId(); [DllImport("ntdll.dll")] public static extern void RtlZeroMemory( IntPtr Destination, UIntPtr Length); [DllImport("ntdll.dll")] public static extern Int32 RtlCheckTokenMembershipEx( IntPtr TokenHandle, IntPtr Sid, Int32 Flags, ref Boolean IsMember); [DllImport("ntdll.dll")] public static extern Int32 NtOpenProcess( ref IntPtr ProcessHandle, UInt32 DesiredAccess, IntPtr ObjectAttributes, IntPtr ClientId); [DllImport("ntdll.dll")] public static extern Int32 NtOpenProcessToken( IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); [DllImport("ntdll.dll")] public static extern Int32 NtQueryInformationToken( IntPtr TokenHandle, int TokenInformationClass, IntPtr TokenInformation, UInt32 TokenInformationLength, out uint ReturnLength ); } '@ } $isMember = $false $pSid = [Marshal]::AllocHGlobal(8+(4*10)) @(1,0,0,0,0,0,0,5) | ForEach -Begin { $i = 0 } -Process { [Marshal]::WriteByte($pSid, $i++, $_) } switch ($AccType) { "System" { # S-1-5-[18] // @([1],Count,0,0,0,0,0,[5] && 18) $hproc = [IntPtr]::Zero $procID = [TOKEN]::GetCurrentProcessId() $clientIdPtr = [marshal]::AllocHGlobal(0x64) $attributesPtr = [marshal]::AllocHGlobal(0x64) @($clientIdPtr, $attributesPtr) |% { [TOKEN]::RtlZeroMemory($_, [uIntPtr]::new(0x64))} [marshal]::WriteInt32($clientIdPtr,0x0,$procID) [marshal]::WriteInt32($attributesPtr,0x0,0x64) if (0 -ne [TOKEN]::NtOpenProcess( [ref]$hproc, 0x1F0FFF, $attributesPtr, $clientIdPtr)) { throw "NtOpenProcess fail." } $hToken = [IntPtr]::Zero if (0 -ne [TOKEN]::NtOpenProcessToken($hproc, 0x00000008, [ref]$hToken)) { throw "NtOpenProcessToken fail." } [UInt32]$ReturnLength = 0 $TokenInformation = [marshal]::AllocHGlobal(100) if (0 -ne [TOKEN]::NtQueryInformationToken($hToken,1,$TokenInformation, 100, [ref]$ReturnLength)) { throw "NtQueryInformationToken fail." } [Marshal]::WriteByte($pSid, 1, 1) [Marshal]::WriteInt32($pSid, 8, 18) $pUserSid = [Marshal]::ReadIntPtr($TokenInformation) $isMember = ([Marshal]::ReadByte($pUserSid,0) -eq [Marshal]::ReadByte($pSid,0)) -and ([Marshal]::ReadByte($pUserSid,1) -eq [Marshal]::ReadByte($pSid,1)) -and ([Marshal]::ReadByte($pUserSid,7) -eq [Marshal]::ReadByte($pSid,7)) -and ([Marshal]::ReadInt32($pUserSid,8) -eq [Marshal]::ReadInt32($pSid,8)) } "Administrator" { # S-1-5-[32]-[544] // @([1],Count,0,0,0,0,0,[5] && 32,544) [Marshal]::WriteByte($pSid, 1, 2) [Marshal]::WriteInt32($pSid, 8, 32) [Marshal]::WriteInt32($pSid, 12, 544) $ret = [TOKEN]::RtlCheckTokenMembershipEx(0, $pSid, 0, [ref]$isMember) } } [Marshal]::FreeHGlobal($pSid) return $isMember } $isSystem = ValidateAccount -AccType System $isAdmin = ValidateAccount -AccType Administrator Write-Host "is Admin* Acc ? $isAdmin" Write-Host "is System Acc ? $isSystem" Write-Host
Darki was nice, if you want modular option, to validate group or account, you can expend it So, main action is here >> Edit. Latest most updated version Add support for x86, x64, x86 in x64 Code: #SECURITY_NT_AUTHORITY (S-1-5) $isMember = $false $Rev, $Auth, $Count, $MaxCount = 1,5,0,10 $pSid = [Marshal]::AllocHGlobal(8+(4*$MaxCount)) @($Rev, $Count, 0,0,0,0,0, $Auth) | ForEach -Begin { $i = 0 } -Process { [Marshal]::WriteByte($pSid, $i++, $_) } try { switch ($AccType) { "System" { # S-1-5-[18] // @([1],Count,0,0,0,0,0,[5] && 18) $isMember = Check -pSid $pSid -Subs @(18) -Type Account } "Administrator" { # S-1-5-[32]-[544] // @([1],Count,0,0,0,0,0,[5] && 32,544) $isMember = Check -pSid $pSid -Subs @(32, 544) -Type Group } } } catch {} [Marshal]::FreeHGlobal($pSid) return $isMember ............. Code: using namespace System.Reflection using namespace System.Diagnostics using namespace System.Management.Automation using namespace System.Runtime.InteropServices using namespace System.Security.Principal Clear-Host Write-Host <# SID structure (winnt.h) https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid typedef struct _SID { BYTE Revision; BYTE SubAuthorityCount; SID_IDENTIFIER_AUTHORITY IdentifierAuthority; #if ... DWORD *SubAuthority[]; #else DWORD SubAuthority[ANYSIZE_ARRAY]; #endif } SID, *PISID; ~~~~~~~~~~~~~~~~~~~ Well-known SIDs https://learn.microsoft.com/en-us/windows/win32/secauthz/well-known-sids The SECURITY_NT_AUTHORITY (S-1-5) predefined identifier authority produces SIDs that are not universal but are meaningful only on Windows installations. You can use the following RID values with SECURITY_NT_AUTHORITY to create well-known SIDs. SECURITY_LOCAL_SYSTEM_RID String value: S-1-5-18 A special account used by the operating system. The following table has examples of domain-relative RIDs that you can use to form well-known SIDs for **local groups** (aliases). For more information about local and global groups, see Local Group Functions and Group Functions. DOMAIN_ALIAS_RID_ADMINS Value: 0x00000220 String value: S-1-5-32-544 A local group used for administration of the domain. ~~~~~~~~~~~~~~~~~~~ TOKEN_INFORMATION_CLASS enumeration (winnt.h) https://learn.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-token_information_class typedef enum _TOKEN_INFORMATION_CLASS { TokenUser = 1, TokenGroups, TokenPrivileges, TokenOwner, TokenPrimaryGroup, TokenDefaultDacl, TokenSource, TokenType, TokenImpersonationLevel, TokenStatistics, TokenRestrictedSids, TokenSessionId, TokenGroupsAndPrivileges, TokenSessionReference, ... ... ... ~~~~~~~~~~~~~~~~~~~ $isSystem = ValidateAccount -AccType System $isAdmin = ValidateAccount -AccType Administrator Write-Host "is Admin* Acc ? $isAdmin" Write-Host "is System Acc ? $isSystem" Write-Host #> function ValidateAccount { param ( [ValidateSet("System","Administrator")] [ValidateNotNullOrEmpty()] [string]$AccType ) $isMember = $false if (!([PSTypeName]'TOKEN').Type) { Add-Type @' using System; using System.Runtime.InteropServices; using System.Security.Principal; public class TOKEN { [DllImport("kernelbase.dll")] public static extern IntPtr GetCurrentProcessId(); [DllImport("ntdll.dll")] public static extern void RtlZeroMemory( IntPtr Destination, UIntPtr Length); [DllImport("ntdll.dll")] public static extern Int32 NtClose( IntPtr hObject); [DllImport("ntdll.dll")] public static extern Int32 RtlCheckTokenMembershipEx( IntPtr TokenHandle, IntPtr Sid, Int32 Flags, ref Boolean IsMember); [DllImport("ntdll.dll")] public static extern Int32 NtOpenProcess( ref IntPtr ProcessHandle, UInt32 DesiredAccess, IntPtr ObjectAttributes, IntPtr ClientId); [DllImport("ntdll.dll")] public static extern Int32 NtOpenProcessToken( IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); [DllImport("ntdll.dll")] public static extern Int32 NtQueryInformationToken( IntPtr TokenHandle, int TokenInformationClass, IntPtr TokenInformation, UInt32 TokenInformationLength, out uint ReturnLength ); } '@ } function Check { param ( [Parameter(Mandatory)] [IntPtr]$pSid, [Parameter(Mandatory)] [int[]]$Subs, [Parameter(Mandatory)] [ValidateSet("Account", "Group")] [string]$Type ) if ($null -eq $Subs -or $Subs.Length -le 0 -or $pSid -eq [IntPtr]::Zero) { throw "Invalid parameters: pSid or Subs is empty." } [Marshal]::WriteByte($pSid, 1, $Subs.Length) for ($i = 0; $i -lt $Subs.Length; $i++) { [Marshal]::WriteInt32($pSid, 8 + 4 * $i, $Subs[$i]) } switch ($Type) { "Group" { $ret = [TOKEN]::RtlCheckTokenMembershipEx( 0, $pSid, 0, [ref]$isMember) } "Account" { if ([IntPtr]::Size -eq 8) { # 64-bit sizes and layout $clientIdSize = 16 $objectAttrSize = 48 } else { # 32-bit sizes and layout (WOW64) $clientIdSize = 8 $objectAttrSize = 24 } $hproc = [IntPtr]::Zero $procID = [TOKEN]::GetCurrentProcessId() $clientIdPtr = [marshal]::AllocHGlobal($clientIdSize) $attributesPtr = [marshal]::AllocHGlobal($objectAttrSize) [TOKEN]::RtlZeroMemory($clientIdPtr, [Uintptr]::new($clientIdSize)) [TOKEN]::RtlZeroMemory($attributesPtr, [Uintptr]::new($objectAttrSize)) [marshal]::WriteInt32($attributesPtr, 0x0, $objectAttrSize) if ([IntPtr]::Size -eq 8) { [Marshal]::WriteInt64($clientIdPtr, 0, [Int64]$procID) } else { [Marshal]::WriteInt32($clientIdPtr, 0x0, $procID) } try { if (0 -ne [TOKEN]::NtOpenProcess( [ref]$hproc, 0x0400, $attributesPtr, $clientIdPtr)) { throw "NtOpenProcess fail." } } finally { @($clientIdPtr, $attributesPtr) | % {[Marshal]::FreeHGlobal($_)} } $hToken = [IntPtr]::Zero if (0 -ne [TOKEN]::NtOpenProcessToken( $hproc, 0x00000008, [ref]$hToken)) { throw "NtOpenProcessToken fail." } try { [UInt32]$ReturnLength = 0 $TokenInformation = [marshal]::AllocHGlobal(100) if (0 -ne [TOKEN]::NtQueryInformationToken( $hToken,1,$TokenInformation, 100, [ref]$ReturnLength)) { throw "NtQueryInformationToken fail." } $pUserSid = [Marshal]::ReadIntPtr($TokenInformation) $isMember = ($Subs.Length -eq [Marshal]::ReadByte($pUserSid,1)) -and ([Marshal]::ReadByte($pUserSid,0) -eq [Marshal]::ReadByte($pSid,0)) -and ([Marshal]::ReadByte($pUserSid,7) -eq [Marshal]::ReadByte($pSid,7)) if ($isMember) { for ($i=0; $i -lt $Subs.Length; $i++) { if ([Marshal]::ReadInt32($pUserSid, 8 + 4*$i) -ne $Subs[$i]) { $isMember = $false break }}} } finally { [marshal]::FreeHGlobal($TokenInformation) @($hproc, $hToken) | % { [TOKEN]::NtClose($_) | Out-Null } } } } return $isMember } #SECURITY_NT_AUTHORITY (S-1-5) $isMember = $false $Rev, $Auth, $Count, $MaxCount = 1,5,0,10 $pSid = [Marshal]::AllocHGlobal(8+(4*$MaxCount)) @($Rev, $Count, 0,0,0,0,0, $Auth) | ForEach -Begin { $i = 0 } -Process { [Marshal]::WriteByte($pSid, $i++, $_) } try { switch ($AccType) { "System" { # S-1-5-[18] // @([1],Count,0,0,0,0,0,[5] && 18) $isMember = Check -pSid $pSid -Subs @(18) -Type Account } "Administrator" { # S-1-5-[32]-[544] // @([1],Count,0,0,0,0,0,[5] && 32,544) $isMember = Check -pSid $pSid -Subs @(32, 544) -Type Group } } } catch { Write-warning "An error occurred: $_" if (-not [Environment]::Is64BitProcess -and [Environment]::Is64BitOperatingSystem) { Write-warning "This script could fail on x86 PowerShell in a 64-bit system." } $isMember = $null } [Marshal]::FreeHGlobal($pSid) return $isMember } $isSystem = ValidateAccount -AccType System $isAdmin = ValidateAccount -AccType Administrator Write-Host "is Admin* Acc ? $isAdmin" Write-Host "is System Acc ? $isSystem" Write-Host