When investigating what files a process is currently actively working with, I can use tools from Sysinternals like handle.exe
or procexp.exe
and then look for names looking like file patterns.
But when I query for the open handles of a process using PowerShell, I can only seem to get the number of handles. Not the actual handle references itself.
Get-Process -Id $SomePid | select Handles
The same goes for using .Net calls
[System.Diagnostics.Process]::GetProcessById($SomePid).Handles
And WMI will also strike out, only providing me with the amount of handles...
Get-CimSession Win32_Process -Filter "Process = $SomePid" | select HandleCount
When investigating what files a process is currently actively working with, I can use tools from Sysinternals like handle.exe
or procexp.exe
and then look for names looking like file patterns.
But when I query for the open handles of a process using PowerShell, I can only seem to get the number of handles. Not the actual handle references itself.
Get-Process -Id $SomePid | select Handles
The same goes for using .Net calls
[System.Diagnostics.Process]::GetProcessById($SomePid).Handles
And WMI will also strike out, only providing me with the amount of handles...
Get-CimSession Win32_Process -Filter "Process = $SomePid" | select HandleCount
Share
Improve this question
edited Jan 30 at 3:31
Santiago Squarzon
62k5 gold badges24 silver badges54 bronze badges
asked Jan 29 at 17:56
DennisDennis
1,9072 gold badges17 silver badges41 bronze badges
7
|
Show 2 more comments
2 Answers
Reset to default 1As stated in comments, it would be a big chunk of P/Invoke code, hopefully this help you giving you a start.
The current implementation will list all open handles in your system using NtQuerySystemInformation
function. You can later use PowerShell to filter by the UniqueProcessId
property of these objects. However, note this implementation provides limited information, see the SystemHandle
class in the code.
To get the handle TypeName
the implementation uses NtQueryObject
function, however from here gets more complex as you need handle each type differently and call different Win APIs. For example, for File
handles, you would use GetFileInformationByHandleEx
function and so on.
Usage
Put the code below in a file, call it whatever you want (in this example HandleStuff.cs
), and then you can do:
$code = Get-Content .\HandleStuff.cs -Raw
Add-Type $code -IgnoreWarnings -WA 0
[HandleStuff.Native]::GetSystemHandles()
You can also inline the code in the same PowerShell file if you want, or, in PowerShell 5.1 you can use Add-Type ... -OutputAssembly <path>
if you want to have a compiled version in a .dll
to faster loading.
Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace HandleStuff
{
public class SystemHandle
{
private readonly SYSTEM_HANDLE _handle;
private readonly string _type;
public string TypeName
{
get { return _type; }
}
public IntPtr Object
{
get { return _handle.Object; }
}
public IntPtr UniqueProcessId
{
get { return _handle.UniqueProcessId; }
}
public IntPtr HandleValue
{
get { return _handle.HandleValue; }
}
public uint GrantedAccess
{
get { return _handle.GrantedAccess; }
}
public ushort ObjectTypeIndex
{
get { return _handle.ObjectTypeIndex; }
}
public uint HandleAttributes
{
get { return _handle.HandleAttributes; }
}
internal SystemHandle(string type, SYSTEM_HANDLE handle)
{
_type = type;
_handle = handle;
}
}
internal enum OBJECT_INFORMATION_CLASS
{
ObjectTypeInformation = 2
}
[StructLayout(LayoutKind.Sequential)]
internal struct PUBLIC_OBJECT_TYPE_INFORMATION
{
internal UNICODE_STRING TypeName;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)]
internal uint[] Reserved;
}
[StructLayout(LayoutKind.Sequential)]
internal struct UNICODE_STRING
{
internal ushort Length;
internal ushort MaximumLength;
internal IntPtr Buffer;
}
[StructLayout(LayoutKind.Sequential)]
internal struct SYSTEM_HANDLE
{
internal IntPtr Object;
internal IntPtr UniqueProcessId;
internal IntPtr HandleValue;
internal uint GrantedAccess;
internal ushort CreatorBackTraceIndex;
internal ushort ObjectTypeIndex;
internal uint HandleAttributes;
internal uint Reserved;
}
[StructLayout(LayoutKind.Sequential)]
internal struct SYSTEM_HANDLE_INFORMATION_EX
{
internal UIntPtr HandleCount;
internal UIntPtr Reserved;
internal SYSTEM_HANDLE Handles;
}
internal enum SYSTEM_INFORMATION_CLASS
{
SystemExtendedHandleInformation = 64
}
public static class Native
{
[DllImport("ntdll.dll")]
private static extern uint NtQuerySystemInformation(
[In] SYSTEM_INFORMATION_CLASS SystemInformationClass,
[Out] IntPtr SystemInformation,
[In] int SystemInformationLength,
[Out] out int ReturnLength);
[DllImport("ntdll.dll")]
private static extern uint RtlNtStatusToDosError(
[In] uint Status);
[DllImport("ntdll.dll")]
private static extern uint NtQueryObject(
[In] IntPtr Handle,
[In] OBJECT_INFORMATION_CLASS ObjectInformationClass,
[Out] IntPtr ObjectInformation,
[In] int ObjectInformationLength,
[Out] out int ReturnLength);
public static SystemHandle[] GetSystemHandles()
{
List<SystemHandle> handles;
IntPtr sysInfo = IntPtr.Zero;
try
{
sysInfo = GetSysHandleInfo();
SYSTEM_HANDLE_INFORMATION_EX handleInfo = Marshal
.PtrToStructure<SYSTEM_HANDLE_INFORMATION_EX>(sysInfo);
IntPtr handlesPtr = IntPtr.Add(
sysInfo,
Marshal.OffsetOf<SYSTEM_HANDLE_INFORMATION_EX>("Handles").ToInt32());
uint handleCount = handleInfo.HandleCount.ToUInt32();
int handleSize = Marshal.SizeOf<SYSTEM_HANDLE>();
handles = new List<SystemHandle>((int)handleCount);
for (int i = 0; i < handleCount; i++)
{
IntPtr handlePtr = IntPtr.Add(handlesPtr, i * handleSize);
SYSTEM_HANDLE handle = Marshal.PtrToStructure<SYSTEM_HANDLE>(handlePtr);
handles.Add(new SystemHandle(GetHandleTypeName(handle.HandleValue), handle));
}
}
finally
{
if (sysInfo != IntPtr.Zero)
{
Marshal.FreeHGlobal(sysInfo);
}
}
return handles.ToArray();
}
private static IntPtr GetSysHandleInfo()
{
int size = 512;
IntPtr sysInfo = IntPtr.Zero;
while (true)
{
if (sysInfo != IntPtr.Zero)
{
Marshal.FreeHGlobal(sysInfo);
}
sysInfo = Marshal.AllocHGlobal(size);
uint status = NtQuerySystemInformation(
SYSTEM_INFORMATION_CLASS.SystemExtendedHandleInformation,
sysInfo,
size,
out size);
if (status == 0)
{
break;
}
if (status != 0xc0000004)
{
throw new Win32Exception((int)RtlNtStatusToDosError(status));
}
size *= 2;
}
return sysInfo;
}
private static string GetHandleTypeName(IntPtr handle)
{
int size = 512;
IntPtr objectInfoBuffer = IntPtr.Zero;
try
{
while (true)
{
if (objectInfoBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(objectInfoBuffer);
}
objectInfoBuffer = Marshal.AllocHGlobal(size);
uint status = NtQueryObject(
handle,
OBJECT_INFORMATION_CLASS.ObjectTypeInformation,
objectInfoBuffer,
size,
out size);
if (status == 0)
{
PUBLIC_OBJECT_TYPE_INFORMATION info = Marshal
.PtrToStructure<PUBLIC_OBJECT_TYPE_INFORMATION>(objectInfoBuffer);
if (info.TypeName.Buffer == IntPtr.Zero)
{
return string.Empty;
}
return Marshal.PtrToStringUni(info.TypeName.Buffer, info.TypeName.Length / 2);
}
if (status != 0xc0000004)
{
return string.Empty;
}
size *= 2;
}
}
finally
{
if (objectInfoBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(objectInfoBuffer);
}
}
}
}
}
Did you look at the Modules property of Get-Process?
Get-Process <process name> | Select -expand Modules
It outputs like this:
PS C:\Windows\System32> get-process vds | Select -ExpandProperty modules
Size(K) ModuleName FileName
------- ---------- --------
692 vds.exe C:\windows\System32\vds.exe
1976 ntdll.dll C:\windows\SYSTEM32\ntdll.dll
716 KERNEL32.DLL C:\windows\System32\KERNEL32.DLL
2676 KERNELBASE.dll C:\windows\System32\KERNELBASE.dll
672 ADVAPI32.DLL C:\windows\System32\ADVAPI32.DLL
632 msvcrt.dll C:\windows\System32\msvcrt.dll
...
handle.exe
already exist. – Santiago Squarzon Commented Jan 29 at 18:05handle.exe
if I can't distribute it with my script everywhere I'd like to look for handles (which I can't :/ ) – Dennis Commented Jan 29 at 18:23