Ps1 Version, Low level System & Admin Check,

Discussion in 'Scripting' started by Dark Vador, Aug 31, 2025.

  1. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,832
    7,114
    150
    #1 Dark Vador, Aug 31, 2025
    Last edited: Aug 31, 2025
    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 :D

    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
    
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  2. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,832
    7,114
    150
    And if you want version, with Pain & Suffer :p
    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
    
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  3. Dark Vador

    Dark Vador X Æ A-12

    Feb 2, 2011
    4,832
    7,114
    150
    #3 Dark Vador, Aug 31, 2025
    Last edited: Sep 1, 2025
    (OP)
    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
    
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...