Enumerating process handles, weird issue

2.4k views Asked by At

I scan for opened handles to my process and print them in the console.

  1. I start my process
  2. I attach cheat engine
  3. I run the enumeration of opened handles
  4. I see which process has a handle to my process

The weird issue at this point is as follows, check the code:

array<Accessor^>^ AntiCheat::ScanHandles()
{
    List<Accessor^>^ accessorList = gcnew List<Accessor^>();

    if (!EnableDebugPrivilege(true))
        printf("EnableDebugPrivilege failed: %d\n", GetLastError());

    tNtQuerySystemInformation oNtQuerySystemInformation = (tNtQuerySystemInformation)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");

    PSYSTEM_HANDLE_INFORMATION handleInfo = new SYSTEM_HANDLE_INFORMATION;
    SYSTEM_INFORMATION_CLASS infoClass = (SYSTEM_INFORMATION_CLASS)16; // SystemHandleInformation
    DWORD size = sizeof(SYSTEM_HANDLE_INFORMATION);
    DWORD needed = 0;
    NTSTATUS status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
    while (!NT_SUCCESS(status))
    {
        if (needed == 0)
            return nullptr;
        // The previously supplied buffer wasn't enough.
        delete handleInfo;
        size = needed + 1024;
        handleInfo = (PSYSTEM_HANDLE_INFORMATION)new BYTE[size];
        status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
    }

    HANDLE currentProcess = GetCurrentProcess();
    DWORD currentProcessId = GetProcessId(currentProcess);
    for (DWORD i = 0; i < handleInfo->dwCount; i++)
    {
        //printf(".");
        SYSTEM_HANDLE handle = handleInfo->Handles[i];

        HANDLE procHandle = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, handle.dwProcessId);
        if (GetLastError() == ERROR_ACCESS_DENIED)
            continue;

        HANDLE dupl = 0;
        if (!DuplicateHandle(procHandle, (HANDLE)handle.wValue, currentProcess, &dupl, 0, false, DUPLICATE_SAME_ACCESS))
            continue;

        DWORD procId = GetProcessId(dupl);
        if (procId == currentProcessId)
        {
            printf("accessing us\n");
            char processName[MAX_PATH];
            GetModuleFileNameEx((HMODULE)procHandle, NULL, processName, MAX_PATH);
            accessorList->Add(gcnew Accessor(gcnew String(processName), handle.GrantedAccess));
        }

        CloseHandle(dupl);
    }

    return accessorList->ToArray();
}

If I uncomment the line with printf(".");, I see 3 opened handles to my process (cheatengine). If it's commented (runs way faster), there is no opened handle. However I don't know why this affects my code. Im surprised, does anyone know why this happens? Or how to find out how to find the handles without my printf("."); line?

Another issue is: each time I call the function, the number of allocated bytes duplicates. And I don't know why.

1

There are 1 answers

1
Remy Lebeau On

I see logic problems with your code.

You are not ignoring array items where handle.dwProcessId equals currentProcessId, so you end up opening handles to your own process. Since you are only interested in looking for other processes, you should be ignoring items where handle.dwProcessId is equal to currentProcessId.

You are not checking if OpenProcess() fails for any reason other than ERROR_ACCESS_DENIED. Do not call GetLastError() unless OpenProcess() actually returns NULL first.

You are not closing an opened handle if DuplicateHandle() fails. And why are you duplicating each source handle just to call GetProcessId() on it? You already have their process IDs from the array, so the whole DuplicateHandle()+GetProcessId() is completely unnecessary.

You are taking the wrong approach anyway. Have a look at this discussion:

Enumerating the processes referencing an object

Use NtQuerySystemInformation with SystemInformationClass set to SystemHandleInformation. This fills in an array of SYSTEM_HANDLE_INFORMATION structures, which are defined as:

typedef struct _SYSTEM_HANDLE_INFORMATION {
    ULONG ProcessId;
    UCHAR ObjectTypeNumber;
    UCHAR Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
 } SYSTEM_HANDLE_INFORMATION;

Search for the entry corresponding to the handle you opened with ProcessID equal to GetCurrentProcessId(), then find all entries with the same Object pointer.

Although the discussion shows the wrong declaration for SYSTEM_HANDLE_INFORMATION. The following article shows the correct one:

HOWTO: Enumerate handles

#define SystemHandleInformation 16

typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)(
    ULONG SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
    );

/* The following structure is actually called SYSTEM_HANDLE_TABLE_ENTRY_INFO, but SYSTEM_HANDLE is shorter. */
typedef struct _SYSTEM_HANDLE
{
    ULONG ProcessId;
    BYTE ObjectTypeNumber;
    BYTE Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
 } SYSTEM_HANDLE, *PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
  ULONG HandleCount; /* Or NumberOfHandles if you prefer. */
  SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

With that said, try something more like this:

array<Accessor^>^ AntiCheat::ScanHandles()
{
    List<Accessor^>^ accessorList = gcnew List<Accessor^>();

    if (!EnableDebugPrivilege(true))
        printf("EnableDebugPrivilege failed: %d\n", GetLastError());

    tNtQuerySystemInformation oNtQuerySystemInformation = (tNtQuerySystemInformation) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");

    DWORD currentProcessId = GetCurrentProcessId();
    HANDLE currentProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, currentProcessId);
    PVOID currentProcessAddr = nullptr;

    DWORD size = sizeof(SYSTEM_HANDLE_INFORMATION);
    DWORD needed = 0;
    PSYSTEM_HANDLE_INFORMATION handleInfo = (PSYSTEM_HANDLE_INFORMATION) new BYTE[size];
    SYSTEM_INFORMATION_CLASS infoClass = (SYSTEM_INFORMATION_CLASS) 16; // SystemHandleInformation
    NTSTATUS status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
    while (status == STATUS_INFO_LENGTH_MISMATCH)
    {
        // The previously supplied buffer wasn't enough.
        delete[] handleInfo;
        size += 1024;
        handleInfo = (PSYSTEM_HANDLE_INFORMATION) new BYTE[size];
        status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
    }
    if (status != 0)
    {
        delete[] handleInfo;
        return nullptr;
    }    


    for (DWORD i = 0; i < handleInfo->dwCount; i++)
    {
        SYSTEM_HANDLE &handle = handleInfo->Handles[i];

        if ((handle.dwProcessId == currentProcessId) &&
            (currentProcess == (HANDLE)handle.wValue))
        {
            currentProcessAddr = handle.pAddress;
            break;
        }
    }

    for (DWORD i = 0; i < handleInfo->dwCount; i++)
    {
        SYSTEM_HANDLE &handle = handleInfo->Handles[i];

        if ((handle.dwProcessId != currentProcessId) &&
            (handle.pAddress == currentProcessAddr))
        {
            printf("accessing us\n");

            HANDLE procHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, handle.dwProcessId);
            if (procHandle != 0)
            {
                char processName[MAX_PATH+1];
                DWORD len = GetModuleFileNameEx((HMODULE)procHandle, NULL, processName, MAX_PATH);
                CloseHandle(procHandle);
                processName[len] = '\0';
                accessorList->Add(gcnew Accessor(gcnew String(processName), handle.GrantedAccess));
            }
            else
                accessorList->Add(gcnew Accessor(gcnew String("unknown"), handle.GrantedAccess));
        }
    }

    CloseHandle(currentProcess);

    delete[] handleInfo;
    return accessorList->ToArray();
}