最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Access Violation using NTQuerySystemInformation to iterate Windows system handles and filter by ProcessId - Stack Overflow

programmeradmin1浏览0评论

I am trying to create a C# class that is able to return all system handles of a process given by its Process ID. After some research of the mostly undocumented function calls and return values needed, I encountered the following problem.

Handlecount roughly matches the handle count reported by Task Manager (5%-ish less?). However, whenever I try to get the ProcessID's pointer value:

Marshal.ReadInt64(handle.POwnerPID) // in GetHandles()

I get an Access Violation exception --> either memory is protected or corrupted. However, program and debugger are run as admin. My guess is that my structures are not correct? I have searched multiple sources which all point to the same structure size.

An example of a handle from the list:

AccessMask  0   uint
CreatorBackTraceIndex   0   ushort
HandleFlags 3503129008  uint
ObjectType  0   ushort
PHandleValue    0x002c00000012007f  System.IntPtr
PObject 0x0000000000000004  System.IntPtr
POwnerPID   0x00000000000007f8  System.IntPtr
Reserve 4294956941  uint

Sidenotes:

Why SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX is used and not the base function (base only supports PIDs up to ushort size):

Why can't get process id that more than 65535 by 'ntQuerySystemInformation' in Win7 64bit?

Used documentation:

.htm?ts=0,219

Current implementation:

private enum NTSTATUS : uint
{
    STATUS_SUCCESS = 0x00000000,
    STATUS_INFO_LENGTH_MISMATCH = 0xC0000004
}

[Flags]
private enum SYSTEM_INFORMATION_CLASS : uint
{
    SystemHandleInformation = 16,
    SYSTEM_EXTENDED_HANDLE_INFORMATION = 64,
}

[StructLayout(LayoutKind.Sequential)]
public struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX
{
    public IntPtr PObject; //
    public IntPtr POwnerPID;
    public IntPtr PHandleValue;
    public uint AccessMask;
    public ushort CreatorBackTraceIndex;
    public ushort ObjectType;
    public uint HandleFlags;
    public uint Reserve;
}

static List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetAllHandles()
{
    int bufferSize = 0x10000;
    IntPtr buffer = Marshal.AllocHGlobal(bufferSize);
    int requiredSize;

    NTSTATUS status = NtQuerySystemInformation(
        SYSTEM_INFORMATION_CLASS.SYSTEM_EXTENDED_HANDLE_INFORMATION,
        buffer,
        bufferSize,
        out requiredSize
    );

    while (status == NTSTATUS.STATUS_INFO_LENGTH_MISMATCH)
    {
        Marshal.FreeHGlobal(buffer);
        bufferSize = requiredSize;
        buffer = Marshal.AllocHGlobal(bufferSize);
        status = NtQuerySystemInformation(
            SYSTEM_INFORMATION_CLASS.SYSTEM_EXTENDED_HANDLE_INFORMATION,
            buffer,
            bufferSize,
            out requiredSize
        );
    }

    if (status != NTSTATUS.STATUS_SUCCESS)
    {
        Marshal.FreeHGlobal(buffer);
        return new List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX>();
    }

    List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> handles = new List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX>();
    long baseAddress = buffer.ToInt64();
    long handleCount = Marshal.ReadInt64(buffer);
    int structSize = Marshal.SizeOf(typeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX));

    for (long i = 0; i < handleCount; i++)
    {
        IntPtr current = new IntPtr(baseAddress + (2*IntPtr.Size) + (i * structSize)); //EDIT1: Array of handles starts after two pointer sizes in struct, see comments for more info. 
        SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo = Marshal.PtrToStructure<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX>(current);
        handles.Add(handleInfo);
    }

    Marshal.FreeHGlobal(buffer);
    return handles;
}

public static List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetHandles(Process targetProcess, List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> handles)
{
    List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> processHandles = new List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX>();

    foreach (var handle in handles)
    {
        long proid = Marshal.ReadInt64(handle.POwnerPID);
        if (Marshal.ReadInt32(handle.POwnerPID) == targetProcess.Id)
        {
            processHandles.Add(handle);
        }
    }

    return processHandles;
}

I am trying to create a C# class that is able to return all system handles of a process given by its Process ID. After some research of the mostly undocumented function calls and return values needed, I encountered the following problem.

Handlecount roughly matches the handle count reported by Task Manager (5%-ish less?). However, whenever I try to get the ProcessID's pointer value:

Marshal.ReadInt64(handle.POwnerPID) // in GetHandles()

I get an Access Violation exception --> either memory is protected or corrupted. However, program and debugger are run as admin. My guess is that my structures are not correct? I have searched multiple sources which all point to the same structure size.

An example of a handle from the list:

AccessMask  0   uint
CreatorBackTraceIndex   0   ushort
HandleFlags 3503129008  uint
ObjectType  0   ushort
PHandleValue    0x002c00000012007f  System.IntPtr
PObject 0x0000000000000004  System.IntPtr
POwnerPID   0x00000000000007f8  System.IntPtr
Reserve 4294956941  uint

Sidenotes:

Why SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX is used and not the base function (base only supports PIDs up to ushort size):

Why can't get process id that more than 65535 by 'ntQuerySystemInformation' in Win7 64bit?

Used documentation:

https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/handle_table_entry_ex.htm?ts=0,219

Current implementation:

private enum NTSTATUS : uint
{
    STATUS_SUCCESS = 0x00000000,
    STATUS_INFO_LENGTH_MISMATCH = 0xC0000004
}

[Flags]
private enum SYSTEM_INFORMATION_CLASS : uint
{
    SystemHandleInformation = 16,
    SYSTEM_EXTENDED_HANDLE_INFORMATION = 64,
}

[StructLayout(LayoutKind.Sequential)]
public struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX
{
    public IntPtr PObject; //
    public IntPtr POwnerPID;
    public IntPtr PHandleValue;
    public uint AccessMask;
    public ushort CreatorBackTraceIndex;
    public ushort ObjectType;
    public uint HandleFlags;
    public uint Reserve;
}

static List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetAllHandles()
{
    int bufferSize = 0x10000;
    IntPtr buffer = Marshal.AllocHGlobal(bufferSize);
    int requiredSize;

    NTSTATUS status = NtQuerySystemInformation(
        SYSTEM_INFORMATION_CLASS.SYSTEM_EXTENDED_HANDLE_INFORMATION,
        buffer,
        bufferSize,
        out requiredSize
    );

    while (status == NTSTATUS.STATUS_INFO_LENGTH_MISMATCH)
    {
        Marshal.FreeHGlobal(buffer);
        bufferSize = requiredSize;
        buffer = Marshal.AllocHGlobal(bufferSize);
        status = NtQuerySystemInformation(
            SYSTEM_INFORMATION_CLASS.SYSTEM_EXTENDED_HANDLE_INFORMATION,
            buffer,
            bufferSize,
            out requiredSize
        );
    }

    if (status != NTSTATUS.STATUS_SUCCESS)
    {
        Marshal.FreeHGlobal(buffer);
        return new List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX>();
    }

    List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> handles = new List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX>();
    long baseAddress = buffer.ToInt64();
    long handleCount = Marshal.ReadInt64(buffer);
    int structSize = Marshal.SizeOf(typeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX));

    for (long i = 0; i < handleCount; i++)
    {
        IntPtr current = new IntPtr(baseAddress + (2*IntPtr.Size) + (i * structSize)); //EDIT1: Array of handles starts after two pointer sizes in struct, see comments for more info. 
        SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo = Marshal.PtrToStructure<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX>(current);
        handles.Add(handleInfo);
    }

    Marshal.FreeHGlobal(buffer);
    return handles;
}

public static List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetHandles(Process targetProcess, List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> handles)
{
    List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> processHandles = new List<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX>();

    foreach (var handle in handles)
    {
        long proid = Marshal.ReadInt64(handle.POwnerPID);
        if (Marshal.ReadInt32(handle.POwnerPID) == targetProcess.Id)
        {
            processHandles.Add(handle);
        }
    }

    return processHandles;
}
Share Improve this question edited Feb 6 at 17:34 Remy Lebeau 596k36 gold badges498 silver badges843 bronze badges asked Feb 6 at 14:05 mrpiggymrpiggy 234 bronze badges 6
  • DId you compile for 32 or 64 bit? – Charlieface Commented Feb 6 at 14:24
  • IntPtr current = new IntPtr(baseAddress + IntPtr.Size + (i * structSize)); why the extra + IntPtr.Size? – Charlieface Commented Feb 6 at 14:35
  • @Charlieface compiled as 64bit yes. Fixed the additional IntPtr.Size. As you correctly pointed out this was wrong. My issue sadly is however still present – mrpiggy Commented Feb 6 at 15:07
  • @Charlieface i just reevaluated why I had this in the first place. Turns out the informationtable returned (baseAdress) points to the table struct not the start of the array. The actual array of handles starts with an offset of two pointer sizes. Issue however is still not solved .geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/… – mrpiggy Commented Feb 6 at 15:16
  • 2 Why are you trying to read Marshal.ReadInt64(handle.POwnerPID) in the first place? It's not a pointer, it's just a pointer-sized integer. In other words, change that whole loop to return handles.Where(h => h.POwnerPID == targetProcess.Id).ToList(); – Charlieface Commented Feb 6 at 15:35
 |  Show 1 more comment

1 Answer 1

Reset to default 3

POwnerPID isn't a pointer, it's just a pointer-sized handle. You should not de-reference it.

So you can simplify the code significantly as well.

public static IEnumerable<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> GetHandles(Process targetProcess, IEnumerable<SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX> handles)
{
    return handles.Where(h => h.POwnerPID == targetProcess.Id);
}

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论