NtObjectFunctions.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 Export details about an object to re-import in another process. .DESCRIPTION This function generates a short JSON string which can be used to duplicate into another process using the Import-NtObject function. The handle must be valid when the import function is executed. .PARAMETER Object Specify the object to export. .OUTPUTS string .EXAMPLE Export-NtObject $obj Export an object to a JSON string. #> function Export-NtObject { param( [Parameter(Position = 0, Mandatory = $true)] [NtApiDotNet.NtObject]$Object ) $obj = [PSCustomObject]@{ProcessId = $PID; Handle = $Object.Handle.DangerousGetHandle().ToInt32() } $obj | ConvertTo-Json -Compress } <# .SYNOPSIS Imports an object exported with Export-NtObject. .DESCRIPTION This function accepts a JSON string exported from Export-NtObject which allows an object to be duplicated between PowerShell instances. You can also specify the PID and handle separetly. .PARAMETER Object Specify the object to import as a JSON string. .PARAMETER ProcessId Specify the process ID to import from. .PARAMETER Handle Specify the handle value to import from. .OUTPUTS NtApiDotNet.NtObject (the best available type). .EXAMPLE Import-NtObject '{"ProcessId":3300,"Handle":2660}' Import an object from a JSON string. .EXAMPLE Import-NtObject -ProcessId 3300 -Handle 2660 Import an object from separate PID and handle values. #> function Import-NtObject { [CmdletBinding(DefaultParameterSetName = "FromObject")] param( [Parameter(Position = 0, Mandatory, ParameterSetName = "FromObject")] [string]$Object, [Parameter(Position = 0, Mandatory, ParameterSetName = "FromPid")] [int]$ProcessId, [Parameter(Position = 1, Mandatory, ParameterSetName = "FromPid")] [int]$Handle ) switch ($PSCmdlet.ParameterSetName) { "FromObject" { $obj = ConvertFrom-Json $Object Import-NtObject -ProcessId $obj.ProcessId -Handle $obj.Handle } "FromPid" { Use-NtObject($generic = [NtApiDotNet.NtGeneric]::DuplicateFrom($ProcessId, $Handle)) { $generic.ToTypedObject() } } } } <# .SYNOPSIS Resolve the address of a list of objects. .DESCRIPTION This cmdlet resolves the kernel address for a list of objects. This is an expensive operation so it's designed to be called with a list. .PARAMETER Objects The list of objects to resolve. .PARAMETER PassThru Write the object addresses to the object. Normally no output is generated. .OUTPUTS Int64 - If PassThru specified. .EXAMPLE Resolve-NtObjectAddress $obj1, $obj2; $obj1.Address Resolve the address of two objects. #> function Resolve-NtObjectAddress { [CmdletBinding()] param ( [parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [NtApiDotNet.NtObject[]]$Objects, [switch]$PassThru ) BEGIN { $objs = @() } PROCESS { $objs += $Objects } END { [NtApiDotNet.NtSystemInfo]::ResolveObjectAddress([NtApiDotNet.NtObject[]]$objs) if ($PassThru) { $objs | Select-Object -ExpandProperty Address | Write-Output } } } <# .SYNOPSIS Gets an object from a handle in the current process. .DESCRIPTION This cmdlet creates an object for a handle in the current process. .PARAMETER Handle Specify the handle in the current process. .PARAMETER OwnsHandle Specify the own the handle (closed when object is disposed). .INPUTS None .OUTPUTS NtApiDotNet.NtObject .EXAMPLE Get-NtObjectFromHandle -Handle 0x1234 Get an object from handle 0x1234. .EXAMPLE Get-NtObjectFromHandle -Handle 0x1234 -OwnsHandle Get an object from handle 0x1234 and owns the handle. #> function Get-NtObjectFromHandle { Param( [parameter(Mandatory, Position = 0)] [IntPtr]$Handle, [switch]$OwnsHandle ) $temp_handle = [NtApiDotNet.SafeKernelObjectHandle]::new($Handle, $false) [NtApiDotNet.NtType]::GetTypeForHandle($temp_handle, $true).FromHandle($Handle, $OwnsHandle) } <# .SYNOPSIS Close an object handle. .DESCRIPTION This cmdlet closes an object handle. It supports closing a handle locally or in another process as long as duplicate handle access is granted. .PARAMETER Object Specify the object to close. .PARAMETER Process Specify the process where the handle to close is located. .PARAMETER ProcessId Specify the process ID where the handle to close is located. .PARAMETER Handle Specify the handle value to close in another process. .INPUTS None .OUTPUTS None .EXAMPLE Close-NtObject -Object $obj Close an object in the current process. .EXAMPLE Close-NtObject -Handle 0x1234 -Process $proc Close handle 0x1234 in another process. .EXAMPLE Close-NtObject -Handle 0x1234 -ProcessId 684 Close handle 0x1234 in process with ID 684. .EXAMPLE Close-NtObject -Handle 0x1234 Close handle 0x1234 in process the current process. #> function Close-NtObject { [CmdletBinding(DefaultParameterSetName = "FromProcess")] Param( [parameter(Mandatory, Position = 0, ParameterSetName = "FromObject", ValueFromPipeline)] [NtApiDotNet.NtObject]$Object, [parameter(Mandatory, Position = 0, ParameterSetName = "FromProcess")] [NtApiDotNet.NtProcess]$Process, [parameter(Mandatory, Position = 0, ParameterSetName = "FromProcessId")] [int]$ProcessId, [parameter(Mandatory, Position = 1, ParameterSetName = "FromProcess")] [parameter(Mandatory, Position = 1, ParameterSetName = "FromProcessId")] [parameter(Mandatory, Position = 1, ParameterSetName = "FromCurrentProcess")] [IntPtr]$Handle, [parameter(Mandatory, ParameterSetName = "FromCurrentProcess")] [parameter(Mandatory, ParameterSetName = "FromCurrentProcessSafe")] [switch]$CurrentProcess, [parameter(Mandatory, Position = 0, ParameterSetName = "FromCurrentProcessSafe")] [NtApiDotNet.SafeKernelObjectHandle]$SafeHandle ) PROCESS { switch ($PsCmdlet.ParameterSetName) { "FromObject" { $Object.Close() } "FromProcess" { [NtApiDotNet.NtObject]::CloseHandle($Process, $Handle) } "FromProcessId" { [NtApiDotNet.NtObject]::CloseHandle($ProcessId, $Handle) } "FromCurrentProcess" { [NtApiDotNet.NtObject]::CloseHandle($Handle) } "FromCurrentProcessSafe" { [NtApiDotNet.NtObject]::CloseHandle($SafeHandle) } } } } <# .SYNOPSIS Gets the information classes for a type. .DESCRIPTION This cmdlet gets the list of information classes for a type. You can get the query and set information classes. .PARAMETER Type The NT type to get information classes for. .PARAMETER Object The object to get information classes for. .PARAMETER Set Specify to get the set information classes which might differ. .PARAMETER Volume Specify to get the volume information classes. .INPUTS None .OUTPUTS KeyPair<string, int>[] #> function Get-NtObjectInformationClass { [CmdletBinding(DefaultParameterSetName = "FromType")] Param( [Parameter(Position = 0, Mandatory, ParameterSetName = "FromType")] [NtApiDotNet.NtType]$Type, [Parameter(Position = 0, Mandatory, ParameterSetName = "FromObject")] [NtApiDotNet.NtObject]$Object, [Parameter(ParameterSetName = "FromObject")] [Parameter(ParameterSetName = "FromType")] [switch]$Set, [Parameter(ParameterSetName = "FromVolume")] [switch]$Volume ) if ($Volume) { [NtObjectManager.Utils.PSUtils]::GetFsVolumeInfoClass() | Write-Output } else { if ($PSCmdlet.ParameterSetName -eq "FromObject") { $Type = $Object.NtType } if ($Set) { $Type.SetInformationClass | Write-Output } else { $Type.QueryInformationClass | Write-Output } } } <# .SYNOPSIS Compares two object handles to see if they're the same underlying object. .DESCRIPTION This cmdlet compares two handles to see if they're the same underlying object. On Window 10 this is a supported operation, for downlevel queries the address for the objects and compares that instead. .PARAMETER Left The left hand object to compare. .PARAMETER Right The right hand object to compare. .INPUTS None .OUTPUTS bool #> function Compare-NtObject { Param( [Parameter(Position = 0, Mandatory)] [NtApiDotNet.NtObject]$Left, [Parameter(Position = 1, Mandatory)] [NtApiDotNet.NtObject]$Right ) $Left.SameObject($Right) | Write-Output } <# .SYNOPSIS Test if an object can be opened. .DESCRIPTION This cmdlet tests if an object exists by opening it. This might give false negatives if the reason for not opening it was unrelated to it not existing. .PARAMETER Path Specify an object path to get the security descriptor from. .PARAMETER TypeName Specify the type name of the object at Path. Needed if the module cannot automatically determine the NT type to open. .PARAMETER Root Specify a root object for Path. .INPUTS None .OUTPUTS Boolean .EXAMPLE Test-NtObject \BaseNamedObjects\ABC Test if \BaseNamedObjects\ABC can be opened. .EXAMPLE Test-NtObject ABC -Root $dir Test if ABC can be opened relative to $dir. .EXAMPLE Test-NtObject \BaseNamedObjects\ABC -TypeName Mutant. Test if \BaseNamedObjects\ABC can be opened with a File type. #> function Test-NtObject { [CmdletBinding(DefaultParameterSetName = "FromPath")] param ( [parameter(Mandatory, Position = 0, ParameterSetName = "FromPath")] [string]$Path, [parameter(ParameterSetName = "FromPath")] [string]$TypeName, [parameter(ParameterSetName = "FromPath")] [NtApiDotNet.NtObject]$Root ) switch ($PsCmdlet.ParameterSetName) { "FromPath" { try { Use-NtObject($obj = Get-NtObject -Path $Path -Root $Root -TypeName $TypeName) { } return $true } catch { return $false } } } } <# .SYNOPSIS Create a new object attributes structure. .DESCRIPTION This cmdlet creates a new object attributes structure based on its parameters. Note you should dispose of the object attributes afterwards. .PARAMETER Name Optional NT native name for the object .PARAMETER Root Optional NT object root for relative paths .PARAMETER Attributes Optional object attributes flags .PARAMETER SecurityQualityOfService Optional security quality of service flags .PARAMETER SecurityDescriptor Optional security descriptor .PARAMETER Sddl Optional security descriptor in SDDL format .INPUTS None .EXAMPLE New-NtObjectAttributes \??\c:\windows Create a new object attributes for \??\C:\windows #> function New-NtObjectAttributes { Param( [Parameter(Position = 0)] [string]$Name, [NtApiDotNet.NtObject]$Root, [NtApiDotNet.AttributeFlags]$Attributes = "None", [NtApiDotNet.SecurityQualityOfService]$SecurityQualityOfService, [NtApiDotNet.SecurityDescriptor]$SecurityDescriptor, [string]$Sddl ) $sd = $SecurityDescriptor if ($Sddl -ne "") { $sd = New-NtSecurityDescriptor -Sddl $Sddl } [NtApiDotNet.ObjectAttributes]::new($Name, $Attributes, [NtApiDotNet.NtObject]$Root, $SecurityQualityOfService, $sd) } |