NtTokenFunctions.ps1

# Copyright 2021 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

<#
.SYNOPSIS
Duplicates a token to a new token.
.DESCRIPTION
This cmdlet duplicates a token to another with specified
.PARAMETER Token
Specify the token to duplicate. If not specified will use the current process token.
.PARAMETER ImpersonationLevel
If specified will duplicate the token as an impersonation token.
.PARAMETER Primary
If specified will duplicate the token as a primary token.
.PARAMETER Access
Specify the access to the new token object.
.PARAMETER Inherit
Specify the token handle is inheritable.
.PARAMETER SecurityDescriptor
Specify the new token's security descriptor.
.INPUTS
None
.OUTPUTS
NtApiDotNet.NtToken
.EXAMPLE
Copy-NtToken -Primary
Copy the current token as a primary token.
.EXAMPLE
Copy-NtToken -ImpersonationLevel Impersonation
Copy the current token as a primary token.
.EXAMPLE
Copy-NtToken -Primary -Token $token
Copy an existing token as a primary token.
#>

function Copy-NtToken {
    [CmdletBinding(DefaultParameterSetName = "Impersonation")]
    Param(
        [NtApiDotNet.NtToken]$Token,
        [parameter(Mandatory, ParameterSetName = "Impersonation", Position = 0)]
        [NtApiDotNet.SecurityImpersonationLevel]$ImpersonationLevel,
        [parameter(Mandatory, ParameterSetName = "Primary")]
        [switch]$Primary,
        [NtApiDotNet.TokenAccessRights]$Access = "MaximumAllowed",
        [switch]$Inherit,
        [NtApiDotNet.SecurityDescriptor]$SecurityDescriptor
    )

    switch ($PSCmdlet.ParameterSetName) {
        "Impersonation" {
            $tokentype = "Impersonation"
        }
        "Primary" {
            $tokentype = "Primary"
            $ImpersonationLevel = "Anonymous"
        }
    }

    if ($null -eq $Token) {
        $Token = Get-NtToken -Effective
    }
    else {
        $Token = $Token.Duplicate()
    }

    $attributes = "None"
    if ($Inherit) {
        $attributes = "Inherit"
    }

    Use-NtObject($Token) {
        $Token.DuplicateToken($tokentype, $ImpersonationLevel, $Access, $attributes, $SecurityDescriptor)
    }
}

<#
.SYNOPSIS
Get a token's ID values.
.DESCRIPTION
This cmdlet will get Token's ID values such as Authentication ID and Origin ID.
.PARAMETER Authentication
Specify to get authentication Id.
.PARAMETER Origin
Specify to get origin Id.
.PARAMETER Modified
Specify to get modified Id.
.PARAMETER Token
Optional token object to use to get ID. Must be accesible for Query right.
.INPUTS
None
.OUTPUTS
NtApiDotNet.Luid
.EXAMPLE
Get-NtTokenId
Get the Token ID field.
.EXAMPLE
Get-NtTokenId -Token $token
Get Token ID on an explicit token object.
.EXAMPLE
Get-NtTokenId -Authentication
Get the token's Authentication ID.
.EXAMPLE
Get-NtTokenId -Origin
Get the token's Origin ID.
#>

function Get-NtTokenId {
    [CmdletBinding(DefaultParameterSetName="FromId")]
    Param(
        [NtApiDotNet.NtToken]$Token,
        [Parameter(Mandatory, ParameterSetName="FromOrigin")]
        [switch]$Origin,
        [Parameter(Mandatory, ParameterSetName="FromAuth")]
        [switch]$Authentication,
        [Parameter(Mandatory, ParameterSetName="FromModified")]
        [switch]$Modified
    )
    if ($null -eq $Token) {
        $Token = Get-NtToken -Effective -Access Query
    }
    elseif (!$Token.IsPseudoToken) {
        $Token = $Token.Duplicate()
    }

    Use-NtObject($Token) {
        if ($Origin) {
            $Token.Origin | Write-Output
        } elseif ($Authentication) {
            $Token.AuthenticationId
        } elseif ($Modified) {
            $Token.ModifiedId
        } else {
            $Token.Id
        }
    }
}

<#
.SYNOPSIS
Enables virtualization on a Access Token or Process.
.DESCRIPTION
This cmdlet enables virtualization on an Access Token or Process.
.PARAMETER Token
Specify the token to modify.
.PARAMETER Process
Specify the process to modify.
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
Enable-NtTokenVirtualization
Enable virtualization on the current primary token.
.EXAMPLE
Enable-NtTokenVirtualization -Token $token
Enable virtualization on a specific token.
.EXAMPLE
Enable-NtTokenVirtualization -Process $proc
Enable virtualization on a specific process.
#>

function Enable-NtTokenVirtualization {
    [CmdletBinding(DefaultParameterSetName = "FromProcess")]
    Param(
        [parameter(Mandatory, Position = 0, ParameterSetName="FromToken")]
        [NtApiDotNet.NtToken]$Token,
        [parameter(Position = 0, ParameterSetName="FromProcess")]
        [NtApiDotNet.NtProcess]$Process
    )
    switch($PSCmdlet.ParameterSetName) {
        "FromProcess" {
            if ($null -EQ $Process) {
                $Process = Get-NtProcess -Current
            }
            $Process.VirtualizationEnabled = $true
        }
        "FromToken" {
            $Token.VirtualizationEnabled = $true
        }
    }
}

<#
.SYNOPSIS
Disables virtualization on a Access Token or Process.
.DESCRIPTION
This cmdlet disables virtualization on an Access Token or Process.
.PARAMETER Token
Specify the token to modify.
.PARAMETER Process
Specify the process to modify.
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
Disable-NtTokenVirtualization
Disable virtualization on the current primary token.
.EXAMPLE
Disable-NtTokenVirtualization -Token $token
Disable virtualization on a specific token.
.EXAMPLE
Disable-NtTokenVirtualization -Process $proc
Disable virtualization on a specific process.
#>

function Disable-NtTokenVirtualization {
    [CmdletBinding(DefaultParameterSetName = "FromProcess")]
    Param(
        [parameter(Mandatory, Position = 0, ParameterSetName="FromToken")]
        [NtApiDotNet.NtToken]$Token,
        [parameter(Position = 0, ParameterSetName="FromProcess")]
        [NtApiDotNet.NtProcess]$Process
    )
    switch($PSCmdlet.ParameterSetName) {
        "FromProcess" {
            if ($null -EQ $Process) {
                $Process = Get-NtProcess -Current
            }
            $Process.VirtualizationEnabled = $false
        }
        "FromToken" {
            $Token.VirtualizationEnabled = $false
        }
    }
}

<#
.SYNOPSIS
Check if a token has a specified capability.
.DESCRIPTION
This cmdlet checks if a token has a specified capability. This is primarily for checking AppContainer tokens.
.PARAMETER Token
Specify the token to check. If you do not specify the token then the effective token is used.
.PARAMETER Name
The name of the capability to check.
.INPUTS
None
.OUTPUTS
Boolean
#>

function Test-NtTokenCapability {
    [CmdletBinding()]
    param (
        [parameter(Mandatory, Position = 0)]
        [string]$Name,
        [NtApiDotNet.NtToken]$Token
    )

    if ($null -eq $Token) {
        [NtApiDotNet.NtSecurity]::CapabilityCheck($null, $Name)
    } else {
        $Token.CapabilityCheck($Name)
    }
}

<#
.SYNOPSIS
Set the state of a token's privileges.
.DESCRIPTION
This cmdlet will set the state of a token's privileges. This is commonly used to enable debug/backup privileges to perform privileged actions.
If no token is specified then the current effective token is used.
.PARAMETER Privilege
A list of privileges to set their state.
.PARAMETER Token
Optional token object to use to set privileges. Must be accesible for AdjustPrivileges right.
.PARAMETER Attribute
Specify the actual attributes to set. Defaults to Enabled.
.PARAMETER All
Set attributes for all privileges in the token.
.PARAMETER PassThru
Passthrough the updated privilege results.
.PARAMETER Disable
Disable the specified privileges.
.INPUTS
None
.OUTPUTS
List of TokenPrivilege values indicating the new state of all privileges successfully modified.
.EXAMPLE
Set-NtTokenPrivilege SeDebugPrivilege
Enable SeDebugPrivilege on the current effective token
.EXAMPLE
Set-NtTokenPrivilege SeDebugPrivilege -Attributes Disabled
Disable SeDebugPrivilege on the current effective token
.EXAMPLE
Set-NtTokenPrivilege SeBackupPrivilege, SeRestorePrivilege -Token $token
Enable SeBackupPrivilege and SeRestorePrivilege on an explicit token object.
#>

function Set-NtTokenPrivilege {
    [CmdletBinding(DefaultParameterSetName = "FromPrivilege")]
    Param(
        [NtApiDotNet.NtToken]$Token,
        [Parameter(Mandatory, Position = 0, ParameterSetName = "FromPrivilege")]
        [alias("Privileges")]
        [NtApiDotNet.TokenPrivilegeValue[]]$Privilege,
        [alias("Attributes")]
        [NtApiDotNet.PrivilegeAttributes]$Attribute = "Enabled",
        [switch]$Disable,
        [Parameter(Mandatory, ParameterSetName = "FromAllAttributes")]
        [switch]$All,
        [switch]$PassThru
    )

    if ($null -eq $Token) {
        $Token = Get-NtToken -Effective
    }
    else {
        $Token = $Token.Duplicate()
    }

    if ($Disable) {
        $Attribute = "Disabled"
    }

    if ($All) {
        $Privilege = $Token.Privileges.Value
    }

    Use-NtObject($Token) {
        $result = @()
        foreach ($priv in $Privilege) {
            if ($Token.SetPrivilege($priv, $Attribute)) {
                $result += @($Token.GetPrivilege($priv))
            }
            else {
                Write-Warning "Couldn't set privilege $priv"
            }
        }
        if ($PassThru) {
            $result | Write-Output
        }
    }
}

<#
.SYNOPSIS
Enable a token's privileges.
.DESCRIPTION
This cmdlet will enable a token's privileges. This is commonly used to enable debug/backup privileges to perform privileged actions.
If no token is specified then the current effective token is used.
.PARAMETER Privilege
A list of privileges to enable.
.PARAMETER Token
Optional token object to use to enable privileges. Must be accesible for AdjustPrivileges right.
.PARAMETER PassThru
Passthrough the updated privilege results.
.INPUTS
None
.OUTPUTS
List of TokenPrivilege values indicating the new state of all privileges successfully modified.
.EXAMPLE
Enable-NtTokenPrivilege SeDebugPrivilege
Enable SeDebugPrivilege on the current effective token
.EXAMPLE
Enable-NtTokenPrivilege SeBackupPrivilege, SeRestorePrivilege -Token $token
Enable SeBackupPrivilege and SeRestorePrivilege on an explicit token object.
#>

function Enable-NtTokenPrivilege {
    [CmdletBinding(DefaultParameterSetName = "FromPrivilege")]
    Param(
        [NtApiDotNet.NtToken]$Token,
        [Parameter(Mandatory, Position = 0, ParameterSetName = "FromPrivilege")]
        [alias("Privileges")]
        [NtApiDotNet.TokenPrivilegeValue[]]$Privilege,
        [switch]$PassThru
    )

    Set-NtTokenPrivilege -Token $Token -Privilege $Privilege -PassThru:$PassThru -Attribute Enabled
}

<#
.SYNOPSIS
Disable a token's privileges.
.DESCRIPTION
This cmdlet will disable a token's privileges. If no token is specified then the current effective token is used.
.PARAMETER Privilege
A list of privileges to disable.
.PARAMETER Token
Optional token object to use to disable privileges. Must be accesible for AdjustPrivileges right.
.PARAMETER PassThru
Passthrough the updated privilege results.
.INPUTS
None
.OUTPUTS
List of TokenPrivilege values indicating the new state of all privileges successfully modified.
.EXAMPLE
Disable-NtTokenPrivilege SeDebugPrivilege
Disable SeDebugPrivilege on the current effective token
.EXAMPLE
Disable-NtTokenPrivilege SeBackupPrivilege, SeRestorePrivilege -Token $token
Disable SeBackupPrivilege and SeRestorePrivilege on an explicit token object.
#>

function Disable-NtTokenPrivilege {
    [CmdletBinding(DefaultParameterSetName = "FromPrivilege")]
    Param(
        [NtApiDotNet.NtToken]$Token,
        [Parameter(Mandatory, Position = 0, ParameterSetName = "FromPrivilege")]
        [alias("Privileges")]
        [NtApiDotNet.TokenPrivilegeValue[]]$Privilege,
        [switch]$PassThru
    )

    Set-NtTokenPrivilege -Token $Token -Privilege $Privilege -PassThru:$PassThru -Attribute Disabled
}

<#
.SYNOPSIS
Get the state of a token's privileges.
.DESCRIPTION
This cmdlet will get the state of a token's privileges.
.PARAMETER Privilege
A list of privileges to get their state.
.PARAMETER Token
Optional token object to use to get privileges. Must be accesible for Query right.
.INPUTS
None
.OUTPUTS
List of TokenPrivilege values indicating the state of all privileges requested.
.EXAMPLE
Get-NtTokenPrivilege
Get all privileges on the current Effective token.
.EXAMPLE
Get-NtTokenPrivilege -Token $token
Get all privileges on an explicit token.
.EXAMPLE
Get-NtTokenPrivilege -Privilege SeDebugPrivilege
Get state of SeDebugPrivilege on the current process token
.EXAMPLE
Get-NtTokenPrivilege -Privilege SeBackupPrivilege, SeRestorePrivilege -Token $token
Get SeBackupPrivilege and SeRestorePrivilege status on an explicit token object.
#>

function Get-NtTokenPrivilege {
    Param(
        [Parameter(Position = 0, ValueFromPipeline)]
        [NtApiDotNet.NtToken]$Token,
        [alias("Privileges")]
        [NtApiDotNet.TokenPrivilegeValue[]]$Privilege
    )
    if ($null -eq $Token) {
        $Token = Get-NtToken -Effective -Access Query
    }
    elseif (!$Token.IsPseudoToken) {
        $Token = $Token.Duplicate()
    }

    Use-NtObject($Token) {
        if ($null -ne $Privilege -and $Privilege.Count -gt 0) {
            foreach ($priv in $Privilege) {
                $val = $Token.GetPrivilege($priv)
                if ($null -ne $val) {
                    $val | Write-Output
                }
                else {
                    Write-Warning "Couldn't get privilege $priv"
                }
            }
        }
        else {
            $Token.Privileges | Write-Output
        }
    }
}

<#
.SYNOPSIS
Get a token's groups.
.DESCRIPTION
This cmdlet will get the groups for a token.
.PARAMETER Token
Optional token object to use to get groups. Must be accesible for Query right.
.PARAMETER Restricted
Return the restricted SID list.
.PARAMETER Capabilities
Return the capability SID list.
.PARAMETER Attributes
Specify attributes to filter group list on.
.INPUTS
None
.OUTPUTS
List of UserGroup values indicating the state of all groups.
.EXAMPLE
Get-NtTokenGroup
Get all groups on the effective process token
.EXAMPLE
Get-NtTokenGroup -Token $token
Get groups on an explicit token object.
.EXAMPLE
Get-NtTokenGroup -Attributes Enabled
Get groups that are enabled.
#>

function Get-NtTokenGroup {
    [CmdletBinding(DefaultParameterSetName = "Normal")]
    Param(
        [Parameter(Position = 0, ValueFromPipeline)]
        [NtApiDotNet.NtToken]$Token,
        [Parameter(Mandatory, ParameterSetName = "Restricted")]
        [switch]$Restricted,
        [Parameter(Mandatory, ParameterSetName = "Capabilities")]
        [switch]$Capabilities,
        [Parameter(Mandatory, ParameterSetName = "Device")]
        [switch]$Device,
        [NtApiDotNet.GroupAttributes]$Attributes = 0
    )
    if ($null -eq $Token) {
        $Token = Get-NtToken -Effective -Access Query
    }
    elseif (!$Token.IsPseudoToken) {
        $Token = $Token.Duplicate()
    }

    Use-NtObject($Token) {
        $groups = if ($Restricted) {
            $Token.RestrictedSids
        }
        elseif ($Capabilities) {
            $Token.Capabilities
        }
        elseif ($Device) {
            $Token.DeviceGroups
        }
        else {
            $Token.Groups
        }

        if ($Attributes -ne 0) {
            $groups = $groups | Where-Object { ($_.Attributes -band $Attributes) -eq $Attributes }
        }

        $groups | Write-Output
    }
}

<#
.SYNOPSIS
Sets a token's group state.
.DESCRIPTION
This cmdlet will sets the state of groups for a token.
.PARAMETER Token
Optional token object to use to set groups. Must be accesible for AdjustGroups right.
.PARAMETER Sid
Specify the list of SIDs to set.
.PARAMETER Attributes
Specify the attributes to set on the SIDs.
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
Set-NtTokenGroup -Sid "WD" -Attributes 0
Set the Everyone SID to disabled.
.EXAMPLE
Set-NtTokenGroup -Sid "WD" -Attributes Enabled
Set the Everyone SID to enabled.
#>

function Set-NtTokenGroup {
    [CmdletBinding()]
    Param(
        [NtApiDotNet.NtToken]$Token,
        [Parameter(Mandatory, Position = 0)]
        [NtApiDotNet.Sid[]]$Sid,
        [Parameter(Mandatory, Position = 1)]
        [NtApiDotNet.GroupAttributes]$Attributes
    )
    if ($null -eq $Token) {
        $Token = Get-NtToken -Effective -Access AdjustGroups
    }
    else {
        $Token = $Token.Duplicate()
    }

    Use-NtObject($Token) {
        $Token.SetGroups($Sid, $Attributes)
    }
}

<#
.SYNOPSIS
Resets a token's group state.
.DESCRIPTION
This cmdlet will resets the state of groups for a token.
.PARAMETER Token
Optional token object to use to reset groups. Must be accesible for AdjustGroups right.
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
Reset-NtTokenGroup
Reset the groups for the current token.
.EXAMPLE
Reset-NtTokenGroup -Token $token
Reset the groups for the a specified token.
#>

function Reset-NtTokenGroup {
    [CmdletBinding()]
    Param(
        [NtApiDotNet.NtToken]$Token
    )
    if ($null -eq $Token) {
        $Token = Get-NtToken -Effective -Access AdjustGroups
    }
    else {
        $Token = $Token.Duplicate()
    }

    Use-NtObject($Token) {
        $Token.ResetGroups()
    }
}

<#
.SYNOPSIS
Enable a token's group.
.DESCRIPTION
This cmdlet will enable one or more groups on a token. They can't be marked as mandatory.
.PARAMETER Token
Optional token object to use to enable groups. Must be accesible for AdjustGroups right.
.PARAMETER Sid
Specify the list of group SIDs to enable.
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
Enable-NtTokenGroup -Sid "WD"
Enable the Everyone SID for the current token.
.EXAMPLE
Enable-NtTokenGroup -Sid "WD" -Token $token
Enable the Everyone SID on a specified token.
#>

function Enable-NtTokenGroup {
    [CmdletBinding()]
    Param(
        [NtApiDotNet.NtToken]$Token,
        [Parameter(Mandatory, Position = 0)]
        [NtApiDotNet.Sid[]]$Sid
    )

    Set-NtTokenGroup -Token $Token -Sid $Sid -Attributes Enabled
}

<#
.SYNOPSIS
Disable a token's group.
.DESCRIPTION
This cmdlet will disable one or more groups on a token. They can't be marked as mandatory.
.PARAMETER Token
Optional token object to use to disable groups. Must be accesible for AdjustGroups right.
.PARAMETER Sid
Specify the list of group SIDs to disable.
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
Disable-NtTokenGroup -Sid "WD"
Disable the Everyone SID for the current token.
.EXAMPLE
Disable-NtTokenGroup -Sid "WD" -Token $token
Disable the Everyone SID on a specified token.
#>

function Disable-NtTokenGroup {
    [CmdletBinding()]
    Param(
        [NtApiDotNet.NtToken]$Token,
        [Parameter(Mandatory, Position = 0)]
        [NtApiDotNet.Sid[]]$Sid
    )

    Set-NtTokenGroup -Token $Token -Sid $Sid -Attributes Enabled
}

<#
.SYNOPSIS
Get a token's user SID or one of the other single SID values.
.DESCRIPTION
This cmdlet will get user SID for a token. Or one of the other SIDs such as Owner.
.PARAMETER Owner
Specify to get the owner.
.PARAMETER Group
Specify to get the default group.
.PARAMETER Integrity
Specify to get the integrity level.
.PARAMETER TrustLevel
Specify to get the process trust level.
.PARAMETER LogonId
Specify to get the logon SID.
.PARAMETER Package
Specify to get the AppContainer package SID.
.PARAMETER Token
Optional token object to use to get SID. Must be accesible for Query right.
.PARAMETER AsSddl
Specify to convert the SID to SDDL.
.PARAMETER AsName
Specify to convert the SID to a name.
.INPUTS
None
.OUTPUTS
NtApiDotNet.Sid
.EXAMPLE
Get-NtTokenSid
Get user SID on the current effective token
.EXAMPLE
Get-NtTokenSid -Token $token
Get user SID on an explicit token object.
.EXAMPLE
Get-NtTokenSid -Group
Get the default group SID.
.EXAMPLE
Get-NtTokenSid -Owner
Get the default owner SID.
#>

function Get-NtTokenSid {
    [CmdletBinding(DefaultParameterSetName = "User")]
    Param(
        [Parameter(Position = 0, ValueFromPipeline)]
        [NtApiDotNet.NtToken]$Token,
        [Parameter(Mandatory, ParameterSetName = "Owner")]
        [switch]$Owner,
        [Parameter(Mandatory, ParameterSetName = "Group")]
        [switch]$Group,
        [Parameter(Mandatory, ParameterSetName = "TrustLevel")]
        [switch]$TrustLevel,
        [Parameter(Mandatory, ParameterSetName = "Login")]
        [switch]$LogonId,
        [Parameter(Mandatory, ParameterSetName = "Integrity")]
        [switch]$Integrity,
        [Parameter(Mandatory, ParameterSetName = "Package")]
        [switch]$Package,
        [alias("ToSddl")]
        [switch]$AsSddl,
        [alias("ToName")]
        [switch]$AsName
    )
    if ($null -eq $Token) {
        $Token = Get-NtToken -Effective -Access Query
    }
    elseif (!$Token.IsPseudoToken) {
        $Token = $Token.Duplicate()
    }

    Use-NtObject($Token) {
        $sid = switch ($PsCmdlet.ParameterSetName) {
            "User" { $Token.User.Sid }
            "Owner" { $Token.Owner }
            "Group" { $Token.PrimaryGroup }
            "TrustLevel" { $Token.TrustLevel }
            "Login" { $Token.LogonSid.Sid }
            "Integrity" { $Token.IntegrityLevelSid.Sid }
            "Package" { $Token.AppContainerSid }
        }

        if ($AsSddl) {
            $sid.ToString() | Write-Output
        }
        elseif ($AsName) {
            $sid.Name | Write-Output
        }
        else {
            $sid | Write-Output
        }
    }
}

<#
.SYNOPSIS
Set a token SID.
.DESCRIPTION
This cmdlet will set a SID on the token such as default owner or group.
.PARAMETER Owner
Specify to set the default owner.
.PARAMETER Group
Specify to set the default group.
.PARAMETER Integrity
Specify to set the integrity level.
.PARAMETER Token
Optional token object to use to set group. Must be accesible for AdjustDefault right.
.PARAMETER Sid
Specify the SID to set.
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
Set-NtTokenSid -Owner -Sid "S-1-2-3-4"
Set default owner on the current effective token
.EXAMPLE
Set-NtTokenOwner -Owner -Token $token -Sid "S-1-2-3-4"
Set default owner on an explicit token object.
.EXAMPLE
Set-NtTokenOwner -Group -Sid "S-1-2-3-4"
Set the default group.
#>

function Set-NtTokenSid {
    [CmdletBinding(DefaultParameterSetName = "Normal")]
    Param(
        [Parameter(Position = 1)]
        [NtApiDotNet.NtToken]$Token,
        [Parameter(Mandatory, Position = 0)]
        [NtApiDotNet.Sid]$Sid,
        [Parameter(Mandatory, ParameterSetName = "Owner")]
        [switch]$Owner,
        [Parameter(Mandatory, ParameterSetName = "Group")]
        [switch]$Group,
        [Parameter(Mandatory, ParameterSetName = "Integrity")]
        [switch]$Integrity
    )
    if ($null -eq $Token) {
        $Token = Get-NtToken -Effective -Access AdjustDefault
    }
    else {
        $Token = $Token.Duplicate()
    }

    Use-NtObject($Token) {
        switch ($PsCmdlet.ParameterSetName) {
            "Owner" { $Token.Owner = $Sid }
            "Group" { $Token.PrimaryGroup = $Sid }
            "Integrity" { $Token.IntegrityLevelSid = $sid }
        } }
}

<#
.SYNOPSIS
Get a token's default owner or group.
.DESCRIPTION
This cmdlet will get the default owner or group for a token.
.PARAMETER Group
Specify to get the default group rather than default owner.
.PARAMETER Token
Optional token object to use to get group. Must be accesible for Query right.
.INPUTS
None
.OUTPUTS
UserGroup for the owner.
.EXAMPLE
Get-NtTokenOwner
Get default owner on the current effective token
.EXAMPLE
Get-NtTokenOwner -Token $token
Get default owner on an explicit token object.
.EXAMPLE
Get-NtTokenOwner -Group
Get the default group.
#>

function Get-NtTokenOwner {
    [CmdletBinding()]
    Param(
        [NtApiDotNet.NtToken]$Token,
        [switch]$Group
    )
    if ($null -eq $Token) {
        $Token = Get-NtToken -Effective -Access Query
    }
    elseif (!$Token.IsPseudoToken) {
        $Token = $Token.Duplicate()
    }

    Use-NtObject($Token) {
        if ($Group) {
            $Token.PrimaryGroup | Write-Output
        }
        else {
            $Token.Owner | Write-Output
        }
    }
}

<#
.SYNOPSIS
Get a token's mandatory policy.
.DESCRIPTION
This cmdlet will get the token's mandatory policy.
.PARAMETER Group
Specify to get the default group rather than default owner.
.PARAMETER Token
Optional token object to use to get group. Must be accesible for Query right.
.INPUTS
None
.OUTPUTS
The Token Mandatory Policy
.EXAMPLE
Get-NtTokenMandatoryPolicy
Get the mandatory policy for the current effective token.
.EXAMPLE
Get-NtTokenMandatoryPolicy -Token $token
Get default owner on an explicit token object.
#>

function Get-NtTokenMandatoryPolicy {
    [CmdletBinding()]
    Param(
        [NtApiDotNet.NtToken]$Token
    )
    if ($null -eq $Token) {
        $Token = Get-NtToken -Effective -Access Query
    }
    elseif (!$Token.IsPseudoToken) {
        $Token = $Token.Duplicate()
    }

    Use-NtObject($Token) {
        $Token.MandatoryPolicy
    }
}

<#
.SYNOPSIS
Remove privileges from a token.
.DESCRIPTION
This cmdlet will remove privileges from a token. Note that this completely removes the privilege, not just disable.
.PARAMETER Privileges
A list of privileges to remove.
.PARAMETER Token
Optional token object to use to remove privileges.
.INPUTS
None
.OUTPUTS
List of TokenPrivilege values indicating the new state of all privileges successfully modified.
.EXAMPLE
Remove-NtTokenPrivilege SeDebugPrivilege
Remove SeDebugPrivilege from the current effective token
.EXAMPLE
Remove-NtTokenPrivilege SeBackupPrivilege, SeRestorePrivilege -Token $token
Remove SeBackupPrivilege and SeRestorePrivilege from an explicit token object.
#>

function Remove-NtTokenPrivilege {
    Param(
        [Parameter(Mandatory = $true, Position = 0)]
        [alias("Privileges")]
        [NtApiDotNet.TokenPrivilegeValue[]]$Privilege,
        [NtApiDotNet.NtToken]$Token
    )
    if ($null -eq $Token) {
        $Token = Get-NtToken -Effective
    }
    else {
        $Token = $Token.Duplicate()
    }

    Use-NtObject($Token) {
        $result = @()
        foreach ($priv in $Privilege) {
            if (!$Token.RemovePrivilege($priv)) {
                Write-Warning "Can't remove $priv from token."
            }
        }
        return $result
    }
}

<#
.SYNOPSIS
Set the integrity level of a token.
.DESCRIPTION
This cmdlet will set the integrity level of a token. If you want to raise the level you must have SeTcbPrivilege otherwise you can only lower it.
If no token is specified then the current process token is used.
.PARAMETER IntegrityLevel
Specify the integrity level.
.PARAMETER Token
Optional token object to use to set privileges. Must be accesible for AdjustDefault right.
.PARAMETER Adjustment
Increment or decrement the IL level from the base specified in -IntegrityLevel.
.PARAMETER IntegrityLevelRaw
Specify the integrity level as a raw value.
.INPUTS
None
.EXAMPLE
Set-NtTokenIntegrityLevel Low
Set the current token's integrity level to low.
.EXAMPLE
Set-NtTokenIntegrityLevel Low -Token $Token
Set a specific token's integrity level to low.
.EXAMPLE
Set-NtTokenIntegrityLevel Low -Adjustment -16
Set the current token's integrity level to low minus 16.
.EXAMPLE
Set-NtTokenIntegrityLevel -IntegrityLevelRaw 0x800
Set the current token's integrity level to 0x800.
#>

function Set-NtTokenIntegrityLevel {
    [CmdletBinding(DefaultParameterSetName = "FromIL")]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "FromIL")]
        [NtApiDotNet.TokenIntegrityLevel]$IntegrityLevel,
        [NtApiDotNet.NtToken]$Token,
        [Parameter(ParameterSetName = "FromIL")]
        [Int32]$Adjustment = 0,
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "FromRaw")]
        [Int32]$IntegrityLevelRaw
    )
    switch ($PSCmdlet.ParameterSetName) {
        "FromIL" {
            $il_raw = $IntegrityLevel.ToInt32($null) + $Adjustment
        }
        "FromRaw" {
            $il_raw = $IntegrityLevelRaw
        }
    }

    if ($Token -eq $null) {
        $Token = Get-NtToken -Effective
    }
    else {
        $Token = $Token.Duplicate()
    }

    Use-NtObject($Token) {
        $Token.SetIntegrityLevelRaw($il_raw) | Out-Null
    }
}

<#
.SYNOPSIS
Get the integrity level of a token.
.DESCRIPTION
This cmdlet will gets the integrity level of a token.
.PARAMETER Token
Optional token object to use to get integrity level. Must be accesible for Query right.
.INPUTS
None
.OUTPUTS
NtApiDotNet.TokenIntegrityLevel
.EXAMPLE
Get-NtTokenIntegrityLevel
Get the current token's integrity level.
.EXAMPLE
Get-NtTokenIntegrityLevel -Token $Token
Get a specific token's integrity level.
#>

function Get-NtTokenIntegrityLevel {
    [CmdletBinding(DefaultParameterSetName = "FromIL")]
    Param(
        [Parameter(Position = 0)]
        [NtApiDotNet.NtToken]$Token
    )

    if ($null -eq $Token) {
        $Token = Get-NtToken -Effective
    }
    else {
        $Token = $Token.Duplicate()
    }

    Use-NtObject($Token) {
        $Token.IntegrityLevel | Write-Output
    }
}

<#
.SYNOPSIS
Opens an impersonation token from a process or thread using NtImpersonateThread
.DESCRIPTION
This cmdlet opens an impersonation token from a process using NtImpersonateThread. While SeDebugPrivilege
allows you to bypass the security of processes and threads it doesn't mean you can open the primary token.
This cmdlet allows you to get past that by getting a handle to the first thread and then impersonating it,
as long as the thread isn't impersonating something else you'll get back a copy of the primary token.
.PARAMETER ProcessId
A process to open to get the token from.
.PARAMETER ThreadId
A thread to open to get the token from.
.PARAMETER Access
Access rights for the opened token.
.INPUTS
None
.OUTPUTS
NtApiDotNet.NtToken
.EXAMPLE
Get-NtTokenFromProcess -ProcessId 1234
Gets token from process ID 1234.
.EXAMPLE
Get-NtTokenFromProcess -ProcessId 1234 -Access Query
Gets token from process ID 1234 with only Query access.
.EXAMPLE
Get-NtTokenFromProcess -ThreadId 1234
Gets token from process ID 1234.
#>

function Get-NtTokenFromProcess {
    [CmdletBinding(DefaultParameterSetName = "FromProcess")]
    Param(
        [Parameter(Position = 0, ParameterSetName = "FromProcess", Mandatory = $true)]
        [ValidateScript( { $_ -ge 0 })]
        [int]$ProcessId,
        [Parameter(ParameterSetName = "FromThread", Mandatory = $true)]
        [ValidateScript( { $_ -ge 0 })]
        [int]$ThreadId,
        [NtApiDotNet.TokenAccessRights]$Access = "MaximumAllowed"
    )

    Set-NtTokenPrivilege SeDebugPrivilege
    $t = $null

    try {
        if ($PsCmdlet.ParameterSetName -eq "FromProcess") {
            $t = Use-NtObject($p = Get-NtProcess -ProcessId $ProcessId) {
                $p.GetFirstThread("DirectImpersonation")
            }
        }
        else {
            $t = Get-NtThread -ThreadId $ThreadId -Access DirectImpersonation
        }

        $current = Get-NtThread -Current -PseudoHandle
        Use-NtObject($t, $current.ImpersonateThread($t)) {
            Get-NtToken -Impersonation -Thread $current -Access $Access
        }
    }
    catch {
        Write-Error $_
    }
}

<#
.SYNOPSIS
Prints the details of a token.
.DESCRIPTION
This cmdlet opens prints basic details about it a token.
.PARAMETER Token
Specify the token to format.
.PARAMETER All
Show all information.
.PARAMETER User
Show user information.
.PARAMETER Group
Show group information. Also prints capability sids and restricted sids if a sandboxed token.
.PARAMETER Privilege
Show privilege information.
.PARAMETER Integrity
Show integrity information.
.PARAMETER SecurityAttributes
Show token security attributes.
.PARAMETER UserClaims
Show token user claim attributes.
.PARAMETER DeviceClaims
Show token device claim attributes.
.PARAMETER TrustLevel
Show token trust level.
.PARAMETER Information
Show token information such as type, impersonation level and ID.
.PARAMETER Owner
Show token owner.
.PARAMETER PrimaryGroup
Show token primary group.
.PARAMETER DefaultDacl
Show token default DACL.
.PARAMETER FullDefaultDacl
Show the default DACL in full rather than a summary.
.PARAMETER Basic
Show basic token information, User, Group, Privilege and Integrity.
.PARAMETER MandatoryPolicy
Show mandatory integrity policy.
.OUTPUTS
System.String
.EXAMPLE
Format-NtToken -Token $token
Print the user name of the token.
.EXAMPLE
Format-NtToken -Token $token -Basic
Print basic details for the token.
.EXAMPLE
Format-NtToken -Token $token -All
Print all details for the token.
.EXAMPLE
Format-NtToken -Token $token -User -Group
Print the user and groups of the token.
.EXAMPLE
Format-NtToken -Token $token -DefaultDacl
Print the default DACL of the token.
.EXAMPLE
Format-NtToken -Token $token -FullDefaultDacl
Print the default DACL of the token in full.
#>

function Format-NtToken {
    [CmdletBinding(DefaultParameterSetName = "UserOnly")]
    Param(
        [parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [NtApiDotNet.NtToken]$Token,
        [parameter(ParameterSetName = "Complex")]
        [switch]$All,
        [parameter(ParameterSetName = "Complex")]
        [switch]$Basic,
        [parameter(ParameterSetName = "Complex")]
        [switch]$Group,
        [parameter(ParameterSetName = "Complex")]
        [switch]$Privilege,
        [parameter(ParameterSetName = "Complex")]
        [switch]$User,
        [parameter(ParameterSetName = "Complex")]
        [switch]$Integrity,
        [parameter(ParameterSetName = "Complex")]
        [switch]$SecurityAttributes,
        [parameter(ParameterSetName = "Complex")]
        [switch]$UserClaims,
        [parameter(ParameterSetName = "Complex")]
        [switch]$DeviceClaims,
        [parameter(ParameterSetName = "Complex")]
        [switch]$DeviceGroup,
        [parameter(ParameterSetName = "Complex")]
        [switch]$TrustLevel,
        [parameter(ParameterSetName = "Complex")]
        [switch]$Information,
        [parameter(ParameterSetName = "Complex")]
        [switch]$Owner,
        [parameter(ParameterSetName = "Complex")]
        [switch]$PrimaryGroup,
        [parameter(ParameterSetName = "Complex")]
        [switch]$DefaultDacl,
        [parameter(ParameterSetName = "Complex")]
        [switch]$FullDefaultDacl,
        [parameter(ParameterSetName = "Complex")]
        [switch]$MandatoryPolicy
    )

    if ($All) {
        $Group = $true
        $User = $true
        $Privilege = $true
        $Integrity = $true
        $SecurityAttributes = $true
        $DeviceClaims = $true
        $UserClaims = $true
        $TrustLevel = $true
        $Information = $true
        $Owner = $true
        $PrimaryGroup = $true
        $DefaultDacl = $true
        $DeviceGroup = $true
        $MandatoryPolicy = $true
    }
    elseif ($Basic) {
        $Group = $true
        $User = $true
        $Privilege = $true
        $Integrity = $true
    }

    if ($PSCmdlet.ParameterSetName -eq "UserOnly") {
        $token.User.ToString()
        return
    }

    if ($User) {
        "USER INFORMATION"
        "----------------"
        Format-ObjectTable $token.User.Sid | Write-Output
    }

    if ($Owner) {
        "OWNER INFORMATION"
        "---------------- "
        Format-ObjectTable $token.Owner | Write-Output
    }

    if ($PrimaryGroup) {
        "PRIMARY GROUP INFORMATION"
        "-------------------------"
        Format-ObjectTable $token.PrimaryGroup | Write-Output
    }

    if ($Group) {
        if ($Token.GroupCount -gt 0) {
            "GROUP SID INFORMATION"
            "-----------------"
            Format-ObjectTable $token.Groups | Write-Output
        }

        if ($token.AppContainer -and $token.Capabilities.Length -gt 0) {
            "APPCONTAINER INFORMATION"
            "------------------------"
            Format-ObjectTable $token.AppContainerSid | Write-Output
            "CAPABILITY SID INFORMATION"
            "----------------------"
            Format-ObjectTable $token.Capabilities | Write-Output
        }

        if ($token.Restricted -and $token.RestrictedSids.Length -gt 0) {
            if ($token.WriteRestricted) {
                "WRITE RESTRICTED SID INFORMATION"
                "--------------------------------"
            }
            else {
                "RESTRICTED SID INFORMATION"
                "--------------------------"
            }
            Format-ObjectTable $token.RestrictedSids | Write-Output
        }
    }

    if ($Privilege -and $Token.Privileges.Length -gt 0) {
        "PRIVILEGE INFORMATION"
        "---------------------"
        Format-ObjectTable $token.Privileges | Write-Output
    }

    if ($Integrity) {
        "INTEGRITY LEVEL"
        "---------------"
        Format-ObjectTable $token.IntegrityLevel | Write-Output
    }

    if ($MandatoryPolicy) {
        "MANDATORY POLICY"
        "----------------"
        Format-ObjectTable $token.MandatoryPolicy | Write-Output
    }

    if ($TrustLevel) {
        $trust_level = $token.TrustLevel
        if ($trust_level -ne $null) {
            "TRUST LEVEL"
            "-----------"
            Format-ObjectTable $trust_level | Write-Output
        }
    }

    if ($SecurityAttributes -and $Token.SecurityAttributes.Length -gt 0) {
        "SECURITY ATTRIBUTES"
        "-------------------"
        Format-ObjectTable $token.SecurityAttributes | Write-Output
    }

    if ($UserClaims -and $Token.UserClaimAttributes.Length -gt 0) {
        "USER CLAIM ATTRIBUTES"
        "-------------------"
        Format-ObjectTable $token.UserClaimAttributes | Write-Output
    }

    if ($DeviceClaims -and $Token.DeviceClaimAttributes.Length -gt 0) {
        "DEVICE CLAIM ATTRIBUTES"
        "-------------------"
        Format-ObjectTable $token.DeviceClaimAttributes | Write-Output
    }

    if ($DeviceGroup -and $Token.DeviceGroups.Length -gt 0) {
        "DEVICE GROUP SID INFORMATION"
        "----------------------------"
        Format-ObjectTable $token.DeviceGroups | Write-Output
    }

    if (($DefaultDacl -or $FullDefaultDacl) -and ($null -ne $Token.DefaultDacl)) {
        $summary = !$FullDefaultDacl
        "DEFAULT DACL"
        Format-NtAcl -Acl $Token.DefaultDacl -Type "Directory" -Name "------------" -Summary:$summary | Write-Output
        if ($summary) {
            Write-Output ""
        }
    }

    if ($Information) {
        "TOKEN INFORMATION"
        "-----------------"
        "Type : {0}" -f $token.TokenType
        if ($token.TokenType -eq "Impersonation") {
            "Imp Level : {0}" -f $token.ImpersonationLevel
        }
        "ID : {0}" -f $token.Id
        "Auth ID : {0}" -f $token.AuthenticationId
        "Origin ID : {0}" -f $token.Origin
        "Modified ID : {0}" -f $token.ModifiedId
        "Session ID : {0}" -f $token.SessionId
        "Elevated : {0}" -f $token.Elevated
        "Elevation Type: {0}" -f $token.ElevationType
        "Flags : {0}" -f $token.Flags
    }
}

<#
.SYNOPSIS
Prints the details of the current token.
.DESCRIPTION
This cmdlet opens the current token and prints basic details about it. This is similar to the Windows whoami
command but runs in process and will print information about the current thread token if you're impersonating.
.PARAMETER All
Show all information.
.PARAMETER User
Show user information.
.PARAMETER Group
Show group information. Also prints capability sids and restricted sids if a sandboxed token.
.PARAMETER Privilege
Show privilege information.
.PARAMETER Integrity
Show integrity information.
.PARAMETER SecurityAttributes
Show token security attributes.
.PARAMETER UserClaims
Show token user claim attributes.
.PARAMETER DeviceClaims
Show token device claim attributes.
.PARAMETER TrustLevel
Show token trust level.
.PARAMETER Information
Show token information such as type, impersonation level and ID.
.PARAMETER Owner
Show token owner.
.PARAMETER PrimaryGroup
Show token primary group.
.PARAMETER DefaultDacl
Show token default DACL.
.PARAMETER FullDefaultDacl
Show the default DACL in full rather than a summary.
.PARAMETER Basic
Show basic token information, User, Group, Privilege and Integrity.
.PARAMETER MandatoryPolicy
Show mandatory integrity policy.
.PARAMETER Thread
Specify a thread to use when capturing the effective token.
.OUTPUTS
Text data
.EXAMPLE
Show-NtTokenEffective
Show only the user name of the current token.
.EXAMPLE
Show-NtTokenEffective -All
Show all details for the current token.
.EXAMPLE
Show-NtTokenEffective -Basic
Show basic details for the current token.
.EXAMPLE
Show-NtTokenEffective -User -Group
Show the user and groups of the current token.
#>

function Show-NtTokenEffective {
    [CmdletBinding(DefaultParameterSetName = "UserOnly")]
    Param(
        [parameter(ParameterSetName = "Complex")]
        [switch]$All,
        [parameter(ParameterSetName = "Complex")]
        [switch]$Basic,
        [parameter(ParameterSetName = "Complex")]
        [switch]$Group,
        [parameter(ParameterSetName = "Complex")]
        [switch]$Privilege,
        [parameter(ParameterSetName = "Complex")]
        [switch]$User,
        [parameter(ParameterSetName = "Complex")]
        [switch]$Integrity,
        [parameter(ParameterSetName = "Complex")]
        [switch]$SecurityAttributes,
        [parameter(ParameterSetName = "Complex")]
        [switch]$UserClaims,
        [parameter(ParameterSetName = "Complex")]
        [switch]$DeviceClaims,
        [parameter(ParameterSetName = "Complex")]
        [switch]$TrustLevel,
        [parameter(ParameterSetName = "Complex")]
        [switch]$Information,
        [parameter(ParameterSetName = "Complex")]
        [switch]$Owner,
        [parameter(ParameterSetName = "Complex")]
        [switch]$PrimaryGroup,
        [parameter(ParameterSetName = "Complex")]
        [switch]$DefaultDacl,
        [parameter(ParameterSetName = "Complex")]
        [switch]$FullDefaultDacl,
        [parameter(ParameterSetName = "Complex")]
        [switch]$MandatoryPolicy,
        [NtApiDotNet.NtThread]$Thread
    )

    Use-NtObject($token = Get-NtToken -Effective -Thread $Thread) {
        if ($PsCmdlet.ParameterSetName -eq "UserOnly") {
            Format-NtToken -Token $token
        }
        else {
            $args = @{
                All                = $All
                Basic              = $Basic
                Group              = $Group
                Privilege          = $Privilege
                User               = $User
                Integrity          = $Integrity
                SecurityAttributes = $SecurityAttributes
                UserClaims         = $UserClaims
                DeviceClaims       = $DeviceClaims
                TrustLevel         = $TrustLevel
                Information        = $Information
                Owner              = $Owner
                PrimaryGroup       = $PrimaryGroup
                Token              = $token
                DefaultDacl        = $DefaultDacl
                FullDefaultDacl    = $FullDefaultDacl
                MandatoryPolicy    = $MandatoryPolicy
            }
            Format-NtToken @args
        }
    }
}

function Start-NtTokenViewer {
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [NtApiDotNet.NtObject]$Handle,
        [string]$Text
    )

    Use-NtObject($dup_handle = $Handle.Duplicate()) {
        $dup_handle.Inherit = $true
        $cmdline = [string]::Format("TokenViewer --handle={0}", $dup_handle.Handle.DangerousGetHandle())
        if ($Text -ne "") {
            $cmdline += " ""--text=$Text"""
        }
        $config = New-Win32ProcessConfig $cmdline -ApplicationName "$PSScriptRoot\TokenViewer.exe" -InheritHandles
        $config.InheritHandleList.Add($dup_handle.Handle.DangerousGetHandle())
        Use-NtObject(New-Win32Process -Config $config) { }
    }
}

<#
.SYNOPSIS
Display a UI viewer for a NT token.
.DESCRIPTION
This function will create an instance of the TokenViewer application to display the opened token.
.PARAMETER Token
The token to view.
.PARAMETER Text
Additional text to show in title bar for this token.
.PARAMETER Process
The process to display the token for.
.PARAMETER ProcessId
A process ID of a process to display the token for.
.PARAMETER Name
The name of a process to display the token for.
.PARAMETER MaxTokens
When getting the name/command line only display at most this number of tokens.
.PARAMETER All
Show dialog with all access tokens.
.PARAMETER RunAsAdmin
Specify to elevate the process to admin.
.PARAMETER ServiceName
Specify the name of a service to display the token for.
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
Show-NtToken
Display the primary token for the current process.
.EXAMPLE
Show-NtToken -ProcessId 1234
Display the primary token for the process with PID 1234.
.EXAMPLE
Show-NtToken -Process $process
Display the primary token for the process specified with an NtProcess object.
.EXAMPLE
$ps | Select-Object -First 5 | Show-NtToken
Display the first 5 primary tokens from a list of processes.
.EXAMPLE
Show-NtToken -Token $token
Display the token specified with an NtToken object.
.EXAMPLE
Show-NtToken -Name "notepad.exe"
Display the primary tokens from accessible processes named notepad.exe.
.EXAMPLE
Show-NtToken -Name "notepad.exe" -MaxTokens 5
Display up to 5 primary tokens from accessible processes named notepad.exe.
.EXAMPLE
Show-NtToken -All
Show a list of all accessible tokens to choose from.
.EXAMPLE
Show-NtToken -All -RunAsAdmin
Show a list of all accessible tokens to choose from and run as an administrator.
.EXAMPLE
Show-NtToken -ServiceName "AppInfo"
Display the primary token for the AppInfo service.
#>

function Show-NtToken {
    [CmdletBinding(DefaultParameterSetName = "FromPid")]
    param(
        [Parameter(Mandatory, Position = 0, ParameterSetName = "FromToken", ValueFromPipeline)]
        [NtApiDotNet.NtToken]$Token,
        [Parameter(Mandatory, Position = 0, ParameterSetName = "FromProcess", ValueFromPipeline)]
        [NtApiDotNet.NtProcess]$Process,
        [Parameter(Position = 0, ParameterSetName = "FromPid")]
        [int]$ProcessId = $pid,
        [Parameter(Mandatory, ParameterSetName = "FromName")]
        [string]$Name,
        [Parameter(Mandatory, ParameterSetName = "FromCommandLine")]
        [string]$CommandLine,
        [Parameter(ParameterSetName = "FromName")]
        [Parameter(ParameterSetName = "FromCommandLine")]
        [int]$MaxTokens = 0,
        [Parameter(Mandatory, ParameterSetName = "FromServiceName")]
        [string]$ServiceName,
        [Parameter(ParameterSetName = "All")]
        [switch]$All,
        [Parameter(ParameterSetName = "All")]
        [Parameter(ParameterSetName = "FromPid")]
        [Parameter(ParameterSetName = "FromServiceName")]
        [switch]$RunAsAdmin
    )

    PROCESS {
        if (-not $(Test-Path "$PSScriptRoot\TokenViewer.exe" -PathType Leaf)) {
            Write-Error "Missing token viewer application $PSScriptRoot\TokenViewer.exe"
            return
        }

        $verb = "open"
        if ($RunAsAdmin) {
            $verb = "runas"
        }

        switch ($PSCmdlet.ParameterSetName) {
            "FromProcess" {
                $text = "$($Process.Name):$($Process.ProcessId)"
                Start-NtTokenViewer $Process -Text $text
            }
            "FromName" {
                Use-NtObject($ps = Get-NtProcess -Name $Name -Access QueryLimitedInformation) {
                    $result = $ps
                    if ($MaxTokens -gt 0) {
                        $result = $ps | Select-Object -First $MaxTokens
                    }
                    $result | Show-NtToken
                }
            }
            "FromCommandLine" {
                Use-NtObject($ps = Get-NtProcess -CommandLine $CommandLine -Access QueryLimitedInformation) {
                    $result = $ps
                    if ($MaxTokens -gt 0) {
                        $result = $ps | Select-Object -First $MaxTokens
                    }
                    $result | Show-NtToken
                }
            }
            "FromPid" {
                $cmdline = "--pid={0}" -f $ProcessId
                Start-Process "$PSScriptRoot\TokenViewer.exe" -ArgumentList $cmdline -Verb $verb
            }
            "FromServiceName" {
                $cmdline = """--service={0}""" -f $ServiceName
                Start-Process "$PSScriptRoot\TokenViewer.exe" -ArgumentList $cmdline -Verb $verb
            }
            "FromToken" {
                Start-NtTokenViewer $Token
            }
            "All" {
                Start-Process "$PSScriptRoot\TokenViewer.exe" -Verb $verb
            }
        }
    }
}