# encoding: ascii
# api: powershell
# title: Handles
# description: Original github link https://github.com/gregzakh/alt-ps/blob/master/Get-Handles.ps1
# version: 0.1
# type: function
# author: greg zakharov
# license: CC0
# function: Get-Handles
# x-poshcode-id: 6836
# x-archived: 2017-04-09T13:59:19
# x-published: 2017-04-08T18:14:00
#
#
function Get-Handles {
<#
.SYNOPSIS
Enumarates handles of the specified process.
.EXAMPLE
PS C:\> Get-Handles 3828
.NOTES
Author: greg zakharov
PROCESS_DUP_HANDLE = 0x40
STATUS_INFO_LENGTH_MISMATCH = 0xC0000004
STATUS_SUCCESS = 0x00000000
SystemHandleInformation = 16
ObjectNameInformation = 1
ObjectTypeInformation = 2
#>
param(
[Parameter(Mandatory=$true)]
[ValidateScript({($script:proc = Get-Process -Id $_ -ea 0) -ne $null})]
[Int32]$Id
)
begin {
@(
[Runtime.InteropServices.Marshal],
[Runtime.InteropServices.GCHandle],
[Runtime.InteropServices.CharSet],
[Runtime.InteropServices.CallingConvention],
[Reflection.BindingFlags]
) | ForEach-Object {
$keys = ($ta= [PSObject].Assembly.GetType(
'System.Management.Automation.TypeAccelerators'
))::Get.Keys
$collect = @()
}{
if ($keys -notcontains $_.Name) { $ta::Add($_.Name, $_) }
$collect += $_.Name
}
function private:New-DllImport {
param(
[Parameter(Mandatory=$true, Position=0)]
[ValidateNotNullOrEmpty()]
[String]$Module,
[Parameter(Mandatory=$true, Position=1)]
[ValidateNotNull()]
[Hashtable]$Signature,
[Parameter(Position=2)]
[String]$Type = ${Module}
)
begin {
$mod = if (!($m = $ExecutionContext.SessionState.PSVariable.Get(
'PowerShellDllImport'
))) {
$mb = ([AppDomain]::CurrentDomain.DefineDynamicAssembly(
(New-Object Reflection.AssemblyName('PowerShellDllImport')), 'Run'
)).DefineDynamicModule('PowerShellDllImport', $false)
Set-Variable PowerShellDllImport -Value $mb -Option Constant `
-Scope Global -Visibility Private
$mb # first execution
}
else { $m.Value }
($dllimport = [Runtime.InteropServices.DllImportAttribute]).GetFields() |
Where-Object { $_.Name -cmatch '\A(C|En|S)' } | ForEach-Object {
Set-Variable "_$($_.Name)" $_
}
}
process {}
end {
try { $pin = $mod.GetType("${Type}Handles") }
catch {}
finally {
if (!$pin) {
$pin = $mod.DefineType("${Type}Handles", 'Public, BeforeFieldInit')
foreach ($func in $Signature.Keys) {
<#
arr[0] - return type
arr[1] - parameters
arr[2] - other (SetLastError, CharSet and etc.)
#>
$arr = $Signature[$func]
$fun = $pin.DefineMethod(
$func, 'Public, Static, PinvokeImpl', [Type]$arr[0], [Type[]]$arr[1]
)
# parameters
$arr[1] | ForEach-Object { $i = 1 }{
if ($_.IsByRef) { [void]$fun.DefineParameter($i, 'Out', $null) }
}
$ErrorValue = if ($arr[2]) { $true } else { $false }
$EntryPoint = if ($arr[3]) { $EntryPoint } else { $func }
$atr = New-Object Reflection.Emit.CustomAttributeBuilder(
$dllimport.GetConstructor([String]), $Module,
[Reflection.PropertyInfo[]]@(), [Object[]]@(),
[Reflection.FieldInfo[]]@(
$_SetlastError, $_CallingConvention, $_CharSet, $_EntryPoint
), [Object[]]@(
$ErrorValue, [CallingConvention]::WinApi, [CharSet]::Auto,
$EntryPoint
)
)
$fun.SetCustomAttribute($atr)
}
$pin = $pin.CreateType()
} # if
$pin
}
}
}
function private:Move-Next([IntPtr]$ptr) {
$hes = switch ([IntPtr]::Size) {4 {0x10} 8 {0x18}} # next entry
$mov = switch ([IntPtr]::Size) {4 {$ptr.ToInt32()} 8 {$ptr.ToInt64()}}
[IntPtr]($mov + $hes)
}
$kernel32 = New-DllImport kernel32 -Signature @{
CloseHandle = [Boolean], @([IntPtr]), $true
OpenProcess = [IntPtr], @([UInt32], [Boolean], [Int32]), $true
GetCurrentProcess = [IntPtr], @(), $true
}
$ntdll = New-DllImport ntdll -Signature @{
NtDuplicateObject = [Int32], @(
[IntPtr], [IntPtr], [IntPtr], [IntPtr].MakeByRefType(),
[UInt32], [UInt32], [UInt32]
)
NtQueryObject = [Int32], @(
[IntPtr], [UInt32], [IntPtr], [UInt32], [UInt32].MakeByRefType()
)
NtQuerySystemInformation = [Int32], @(
[UInt32], [IntPtr], [UInt32], [UInt32].MakeByRefType()
)
}
$page, $sz = 0x400, 0x10000 # page size and minimum buffer size
# UNICODE_STRING and its size
$usz = [Marshal]::SizeOf((
$UNICODE_STRING = [Activator]::CreateInstance(
[Object].Assembly.GetType(
'Microsoft.Win32.Win32Native+UNICODE_STRING'
)
)
))
}
process {
$ret = 0 # dummy
try {
if ((
$hndl = $kernel32::OpenProcess(0x40, $false, $proc.Id
)) -eq [IntPtr]::Zero) {
throw New-Object InvalidOperationException
}
# SYSTEM_HANDLE_INFORMATION
$shi = [Marshal]::AllocHGlobal($sz)
while ($ntdll::NtQuerySystemInformation(
16, $shi, $sz, [ref]$ret
) -eq 0xC0000004) {
$shi = [Marshal]::ReAllocHGlobal(
$shi, [IntPtr]($sz *= 2)
)
}
# number of handles
$noh = [Marshal]::ReadInt32($shi)
$mov = switch ([IntPtr]::Size) {4 {$shi.ToInt32()} 8 {$shi.ToInt64()}}
# first handle
$tmp = [IntPtr]($mov + [Marshal]::SizeOf([Type][IntPtr]))
# granted access field offset on both x86 and x64
$gao = switch ([IntPtr]::Size) {4 {0x0C} 8 {0x10}}
# walk around
$handles = for ($i = 0; $i -lt $noh; $i++) {
# SYSTEM_HANDLE_TABLE_ENTRY_INFO
$hte = New-Object PSObject -Property @{
UniqueProcessId = [Marshal]::ReadInt32($tmp)
ObjectTypeIndex = [Marshal]::ReadByte($tmp, 0x04)
HandleAttributes = [Marshal]::ReadByte($tmp, 0x05)
HandleValue = [Marshal]::ReadInt16($tmp, 0x06)
Object = [Marshal]::ReadIntPtr($tmp, 0x08)
GrantedAccess = [Marshal]::ReadInt32($tmp, $gao)
}
# temporary vars
[IntPtr]$duple = [IntPtr]::Zero # duplicated handle (should be released)
[IntPtr]$obj = [IntPtr]::Zero # object type
[IntPtr]$name = [IntPtr]::Zero # name info
$ret = 0 # flush previous $ret data
if ($hte.UniqueProcessId -ne $Id) {
$tmp = Move-Next $tmp
continue
}
# duplicate for query
if ($ntdll::NtDuplicateObject(
$hndl, [IntPtr]$hte.HandleValue, $kernel32::GetCurrentProcess(),
[ref]$duple, 0, 0, 0
) -ne 0) {
$tmp = Move-Next $tmp
continue
}
try {
# getting handle type
$obj = [Marshal]::AllocHGlobal($page)
if ($ntdll::NtQueryObject($duple, 2, $obj, $page, [ref]$ret) -ne 0) {
throw New-Object InvalidOperationException
}
[Byte[]]$bytes = 0..($usz - 1) | ForEach-Object {$ofb = 0}{
[Marshal]::ReadByte($obj, $ofb)
$ofb++
}
$gch = [GCHandle]::Alloc($bytes, 'Pinned')
$uni = [Marshal]::PtrToStructure(
$gch.AddrOfPinnedObject(), [Type]$UNICODE_STRING.GetType()
)
$gch.Free()
$obj_type = $uni.GetType().GetField('Buffer', [BindingFlags]36).GetValue($uni)
if ($hte.GrantedAccess -eq 0x12019F) {
throw New-Object InvalidOperationException
}
# getting handle name
$ret = 0
$name = [Marshal]::AllocHGlobal($page)
if ($ntdll::NtQueryObject($duple, 1, $name, $page, [ref]$ret) -ne 0) {
$name = [Marshal]::ReAllocHGlobal($name, [IntPtr]$ret)
if ($ntdll::NtQueryObject($duple, 1, $name, $page, [ref]$ret) -ne 0) {
throw New-Object InvalidOperationException
}
}
$uni = [Marshal]::PtrToStructure($name, [Type]$UNICODE_STRING.GetType())
$obj_name = $uni.GetType().GetField('Buffer', [BindingFlags]36).GetValue($uni)
# print available data
if (![String]::IsNullOrEmpty($obj_name)) {
New-Object PSObject -Property @{
Handle = '0x{0:X}' -f $hte.HandleValue
Type = $obj_type
Name = $obj_name
}
}
}
catch {}
finally {
if ($name -ne [IntPtr]::Zero) { [Marshal]::FreeHGlobal($name) }
if ($obj -ne [IntPtr]::Zero) { [Marshal]::FreeHGlobal($obj) }
}
[void]$kernel32::CloseHandle($duple)
$tmp = Move-Next $tmp
}
}
catch { $_.Exception }
finally {
if ($shi) { [Marshal]::FreeHGlobal($shi) }
if ($hndl) { [void]$kernel32::CloseHandle($hndl) }
}
}
end {
if ($handles) { $handles | Format-List }
$collect | ForEach-Object { [void]$ta::Remove($_) }
}
}