From 93e8bc70ca4f57c65761e18f3660447b5f54b357 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 6 Feb 2026 17:10:55 -0500 Subject: [PATCH 01/14] feat: Add handles entry by using undocumented ntdll functions to get the handles --- main.cpp | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) diff --git a/main.cpp b/main.cpp index 20a4477..f8cfb5e 100644 --- a/main.cpp +++ b/main.cpp @@ -1490,6 +1490,198 @@ return WideToString(stringBuffer); #endif } +void ListProcHandles(Handle hproc) { + // this is so that we can get the handles of a process + // the cool thing is, that the original witr doesn't actually display + // handles because either the AI that vibe coded didn't know how to get + // or perhaps it just isn't even possible using Go + // so C++ for the win + // for the win-DOWS!! Haha get it + + // if you're wondering why this code looks like C... that's because... +// because it is. i just stole it from this random old obscure forum by a guy +// who did the same thing i needed in 2013 + +// https://cplusplus.com/forum/windows/95774/ +// one thing i've learned while coding this is that when you want to do +// some weird obscure thing that you're sure nobody has done before, the most likely thing +// is that the same weird obscure thing has been done before, but it's just really obscure + +_NtQuerySystemInformation NtQuerySystemInformation = + GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); + _NtDuplicateObject NtDuplicateObject = + GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject"); + _NtQueryObject NtQueryObject = + GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); + NTSTATUS status; + PSYSTEM_HANDLE_INFORMATION handleInfo; + ULONG handleInfoSize = 0x10000; + ULONG pid; + HANDLE processHandle; + ULONG i; + + if (argc < 2) + { + printf("Usage: handles [pid]\n"); + return 1; + } + + pid = _wtoi(argv[1]); + + if (!(processHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid))) + { + printf("Could not open PID %d! (Don't try to open a system process.)\n", pid); + return 1; + } + + handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize); + + /* NtQuerySystemInformation won't give us the correct buffer size, + so we guess by doubling the buffer size. */ + while ((status = NtQuerySystemInformation( + SystemHandleInformation, + handleInfo, + handleInfoSize, + NULL + )) == STATUS_INFO_LENGTH_MISMATCH) + handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2); + + /* NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH. */ + if (!NT_SUCCESS(status)) + { + printf("NtQuerySystemInformation failed!\n"); + return 1; + } + + for (i = 0; i < handleInfo->HandleCount; i++) + { + SYSTEM_HANDLE handle = handleInfo->Handles[i]; + HANDLE dupHandle = NULL; + POBJECT_TYPE_INFORMATION objectTypeInfo; + PVOID objectNameInfo; + UNICODE_STRING objectName; + ULONG returnLength; + + /* Check if this handle belongs to the PID the user specified. */ + if (handle.ProcessId != pid) + continue; + + /* Duplicate the handle so we can query it. */ + if (!NT_SUCCESS(NtDuplicateObject( + processHandle, + handle.Handle, + GetCurrentProcess(), + &dupHandle, + 0, + 0, + 0 + ))) + { + printf("[%#x] Error!\n", handle.Handle); + continue; + } + + /* Query the object type. */ + objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); + if (!NT_SUCCESS(NtQueryObject( + dupHandle, + ObjectTypeInformation, + objectTypeInfo, + 0x1000, + NULL + ))) + { + printf("[%#x] Error!\n", handle.Handle); + CloseHandle(dupHandle); + continue; + } + + /* Query the object name (unless it has an access of + 0x0012019f, on which NtQueryObject could hang. */ + if (handle.GrantedAccess == 0x0012019f) + { + /* We have the type, so display that. */ + printf( + "[%#x] %.*S: (did not get name)\n", + handle.Handle, + objectTypeInfo->Name.Length / 2, + objectTypeInfo->Name.Buffer + ); + free(objectTypeInfo); + CloseHandle(dupHandle); + continue; + } + + objectNameInfo = malloc(0x1000); + if (!NT_SUCCESS(NtQueryObject( + dupHandle, + ObjectNameInformation, + objectNameInfo, + 0x1000, + &returnLength + ))) + { + /* Reallocate the buffer and try again. */ + objectNameInfo = realloc(objectNameInfo, returnLength); + if (!NT_SUCCESS(NtQueryObject( + dupHandle, + ObjectNameInformation, + objectNameInfo, + returnLength, + NULL + ))) + { + /* We have the type name, so just display that. */ + printf( + "[%#x] %.*S: (could not get name)\n", + handle.Handle, + objectTypeInfo->Name.Length / 2, + objectTypeInfo->Name.Buffer + ); + free(objectTypeInfo); + free(objectNameInfo); + CloseHandle(dupHandle); + continue; + } + } + + /* Cast our buffer into an UNICODE_STRING. */ + objectName = *(PUNICODE_STRING)objectNameInfo; + + /* Print the information! */ + if (objectName.Length) + { + /* The object has a name. */ + printf( + "[%#x] %.*S: %.*S\n", + handle.Handle, + objectTypeInfo->Name.Length / 2, + objectTypeInfo->Name.Buffer, + objectName.Length / 2, + objectName.Buffer + ); + } + else + { + /* Print something else. */ + printf( + "[%#x] %.*S: (unnamed)\n", + handle.Handle, + objectTypeInfo->Name.Length / 2, + objectTypeInfo->Name.Buffer + ); + } + + free(objectTypeInfo); + free(objectNameInfo); + CloseHandle(dupHandle); + } + + free(handleInfo); + CloseHandle(processHandle); + +} + void PrintAncestry(DWORD pid) { // now we're geting the name // we're making it slower by adding a bunch of snapshots @@ -1927,6 +2119,7 @@ std::string FRAM = ""; // fram means formatted ram, i'm so creative at var namin PrintAncestry(pid); FindProcessPorts(pid); + ListProcHandles(hProcess); From ee95597747a5e8c917b7c82804e7f94018636f08 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 6 Feb 2026 20:50:16 -0500 Subject: [PATCH 02/14] fix: Change "Handle" to "HANDLE" --- main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index f8cfb5e..3a5fd89 100644 --- a/main.cpp +++ b/main.cpp @@ -1490,7 +1490,7 @@ return WideToString(stringBuffer); #endif } -void ListProcHandles(Handle hproc) { +void ListProcHandles(HANDLE hproc) { // this is so that we can get the handles of a process // the cool thing is, that the original witr doesn't actually display // handles because either the AI that vibe coded didn't know how to get @@ -1682,6 +1682,7 @@ _NtQuerySystemInformation NtQuerySystemInformation = } + void PrintAncestry(DWORD pid) { // now we're geting the name // we're making it slower by adding a bunch of snapshots From b84c8e4d02a78722c1a89846d450f2f90f81abfd Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:04:13 -0500 Subject: [PATCH 03/14] fix: modify ListProcHandles to be integrated into win-witr and use the existing handles and pids --- main.cpp | 102 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 84 insertions(+), 18 deletions(-) diff --git a/main.cpp b/main.cpp index 3a5fd89..2cb8868 100644 --- a/main.cpp +++ b/main.cpp @@ -72,6 +72,79 @@ typedef NTSTATUS (NTAPI *pNtQueryInformationProcess)(HANDLE, UINT, PVOID, ULONG, typedef NTSTATUS (NTAPI *pNtWow64ReadVirtualMemory64)(HANDLE, ULONG64, PVOID, ULONG64, PULONG64); typedef NTSTATUS (NTAPI *pNtWow64QueryInformationProcess64)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); +#ifndef NT_SUCCESS +#define NT_SUCCESS(x) ((x) >= 0) +#endif + +#ifndef STATUS_INFO_LENGTH_MISMATCH +#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 +#endif + +#ifndef SystemHandleInformation +#define SystemHandleInformation 16 +#endif + +#ifndef ObjectBasicInformation +#define ObjectBasicInformation 0 +#endif + +#ifndef ObjectNameInformation +#define ObjectNameInformation 1 +#endif + +#ifndef ObjectTypeInformation +#define ObjectTypeInformation 2 +#endif + +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; + SYSTEM_HANDLE Handles[1]; +} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; + +typedef enum _POOL_TYPE { + NonPagedPool, + PagedPool, + NonPagedPoolMustSucceed, + DontUseThisType, + NonPagedPoolCacheAligned, + PagedPoolCacheAligned, + NonPagedPoolCacheAlignedMustS +} POOL_TYPE, *PPOOL_TYPE; + +typedef struct _OBJECT_TYPE_INFORMATION { + UNICODE_STRING Name; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccess; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + USHORT MaintainTypeList; + POOL_TYPE PoolType; + ULONG PagedPoolUsage; + ULONG NonPagedPoolUsage; +} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; + /* This is a Windows version of the tool witr, which is a utility for finding details about specific processes. @@ -1490,7 +1563,11 @@ return WideToString(stringBuffer); #endif } -void ListProcHandles(HANDLE hproc) { +PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName) +{ + return GetProcAddress(GetModuleHandleA(LibraryName), ProcName); +} +void ListProcHandles(HANDLE hproc, DWORD pid) { // this is so that we can get the handles of a process // the cool thing is, that the original witr doesn't actually display // handles because either the AI that vibe coded didn't know how to get @@ -1516,23 +1593,13 @@ _NtQuerySystemInformation NtQuerySystemInformation = NTSTATUS status; PSYSTEM_HANDLE_INFORMATION handleInfo; ULONG handleInfoSize = 0x10000; - ULONG pid; - HANDLE processHandle; ULONG i; - if (argc < 2) - { - printf("Usage: handles [pid]\n"); - return 1; - } + - pid = _wtoi(argv[1]); + - if (!(processHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid))) - { - printf("Could not open PID %d! (Don't try to open a system process.)\n", pid); - return 1; - } + handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize); @@ -1550,7 +1617,7 @@ _NtQuerySystemInformation NtQuerySystemInformation = if (!NT_SUCCESS(status)) { printf("NtQuerySystemInformation failed!\n"); - return 1; + return; } for (i = 0; i < handleInfo->HandleCount; i++) @@ -1568,7 +1635,7 @@ _NtQuerySystemInformation NtQuerySystemInformation = /* Duplicate the handle so we can query it. */ if (!NT_SUCCESS(NtDuplicateObject( - processHandle, + hproc, handle.Handle, GetCurrentProcess(), &dupHandle, @@ -1678,7 +1745,6 @@ _NtQuerySystemInformation NtQuerySystemInformation = } free(handleInfo); - CloseHandle(processHandle); } @@ -2120,7 +2186,7 @@ std::string FRAM = ""; // fram means formatted ram, i'm so creative at var namin PrintAncestry(pid); FindProcessPorts(pid); - ListProcHandles(hProcess); + ListProcHandles(hProcess, pid); From 4bec8bea59f516cf949f8f2f80e2c46cd9a60317 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:09:13 -0500 Subject: [PATCH 04/14] fix: make params in getlibraryprocadress const char* --- main.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/main.cpp b/main.cpp index 2cb8868..f48d780 100644 --- a/main.cpp +++ b/main.cpp @@ -1563,10 +1563,15 @@ return WideToString(stringBuffer); #endif } -PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName) -{ - return GetProcAddress(GetModuleHandleA(LibraryName), ProcName); +PVOID GetLibraryProcAddress(const char* LibraryName, const char* ProcName) { + HMODULE hMod = GetModuleHandleA(LibraryName); + if (!hMod) { + hMod = LoadLibraryA(LibraryName); + if (!hMod) return nullptr; + } + return (PVOID)GetProcAddress(hMod, ProcName); } + void ListProcHandles(HANDLE hproc, DWORD pid) { // this is so that we can get the handles of a process // the cool thing is, that the original witr doesn't actually display From b2f7b9543ebc729629c4c03b64a48471d9b997a1 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:15:49 -0500 Subject: [PATCH 05/14] fix: add missing typedefs and casting --- main.cpp | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/main.cpp b/main.cpp index f48d780..e6ff584 100644 --- a/main.cpp +++ b/main.cpp @@ -145,6 +145,31 @@ typedef struct _OBJECT_TYPE_INFORMATION { ULONG NonPagedPoolUsage; } OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; +typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)( + SYSTEM_INFORMATION_CLASS SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength +); + +typedef NTSTATUS (NTAPI *_NtDuplicateObject)( + HANDLE SourceProcessHandle, + HANDLE SourceHandle, + HANDLE TargetProcessHandle, + PHANDLE TargetHandle, + ACCESS_MASK DesiredAccess, + ULONG Attributes, + ULONG Options +); + +typedef NTSTATUS (NTAPI *_NtQueryObject)( + HANDLE ObjectHandle, + OBJECT_INFORMATION_CLASS ObjectInformationClass, + PVOID ObjectInformation, + ULONG ObjectInformationLength, + PULONG ReturnLength +); + /* This is a Windows version of the tool witr, which is a utility for finding details about specific processes. @@ -1562,6 +1587,12 @@ return WideToString(stringBuffer); } #endif } +_NtQuerySystemInformation NtQuerySystemInformation = + (_NtQuerySystemInformation)GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); +_NtDuplicateObject NtDuplicateObject = + (_NtDuplicateObject)GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject"); +_NtQueryObject NtQueryObject = + (_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); PVOID GetLibraryProcAddress(const char* LibraryName, const char* ProcName) { HMODULE hMod = GetModuleHandleA(LibraryName); @@ -1611,7 +1642,7 @@ _NtQuerySystemInformation NtQuerySystemInformation = /* NtQuerySystemInformation won't give us the correct buffer size, so we guess by doubling the buffer size. */ while ((status = NtQuerySystemInformation( - SystemHandleInformation, + (SYSTEM_INFORMATION_CLASS)SystemHandleInformation, handleInfo, handleInfoSize, NULL @@ -1657,7 +1688,7 @@ _NtQuerySystemInformation NtQuerySystemInformation = objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); if (!NT_SUCCESS(NtQueryObject( dupHandle, - ObjectTypeInformation, + (OBJECT_INFORMATION_CLASS)ObjectTypeInformation, objectTypeInfo, 0x1000, NULL @@ -1687,7 +1718,7 @@ _NtQuerySystemInformation NtQuerySystemInformation = objectNameInfo = malloc(0x1000); if (!NT_SUCCESS(NtQueryObject( dupHandle, - ObjectNameInformation, + (OBJECT_INFORMATION_CLASS)ObjectNameInformation, objectNameInfo, 0x1000, &returnLength @@ -1697,7 +1728,7 @@ _NtQuerySystemInformation NtQuerySystemInformation = objectNameInfo = realloc(objectNameInfo, returnLength); if (!NT_SUCCESS(NtQueryObject( dupHandle, - ObjectNameInformation, + (OBJECT_INFORMATION_CLASS)ObjectNameInformation, objectNameInfo, returnLength, NULL From a59d5cf89081910b0fab314c7871d13b050e4117 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:21:02 -0500 Subject: [PATCH 06/14] fix: --- main.cpp | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/main.cpp b/main.cpp index e6ff584..b244577 100644 --- a/main.cpp +++ b/main.cpp @@ -1587,13 +1587,6 @@ return WideToString(stringBuffer); } #endif } -_NtQuerySystemInformation NtQuerySystemInformation = - (_NtQuerySystemInformation)GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); -_NtDuplicateObject NtDuplicateObject = - (_NtDuplicateObject)GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject"); -_NtQueryObject NtQueryObject = - (_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); - PVOID GetLibraryProcAddress(const char* LibraryName, const char* ProcName) { HMODULE hMod = GetModuleHandleA(LibraryName); if (!hMod) { @@ -1603,6 +1596,15 @@ PVOID GetLibraryProcAddress(const char* LibraryName, const char* ProcName) { return (PVOID)GetProcAddress(hMod, ProcName); } +_NtQuerySystemInformation pfnNtQuerySystemInformation = + (_NtQuerySystemInformation)GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); +_NtDuplicateObject pfnNtDuplicateObject = + (_NtDuplicateObject)GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject"); +_NtQueryObject pfnNtQueryObject = + (_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); + + + void ListProcHandles(HANDLE hproc, DWORD pid) { // this is so that we can get the handles of a process // the cool thing is, that the original witr doesn't actually display @@ -1620,12 +1622,12 @@ void ListProcHandles(HANDLE hproc, DWORD pid) { // some weird obscure thing that you're sure nobody has done before, the most likely thing // is that the same weird obscure thing has been done before, but it's just really obscure -_NtQuerySystemInformation NtQuerySystemInformation = - GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); - _NtDuplicateObject NtDuplicateObject = - GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject"); - _NtQueryObject NtQueryObject = - GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); +_NtQuerySystemInformation pfnNtQuerySystemInformation = + (_NtQuerySystemInformation)GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); +_NtDuplicateObject pfnNtDuplicateObject = + (_NtDuplicateObject)GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject"); +_NtQueryObject pfnNtQueryObject = + (_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); NTSTATUS status; PSYSTEM_HANDLE_INFORMATION handleInfo; ULONG handleInfoSize = 0x10000; @@ -1641,7 +1643,7 @@ _NtQuerySystemInformation NtQuerySystemInformation = /* NtQuerySystemInformation won't give us the correct buffer size, so we guess by doubling the buffer size. */ - while ((status = NtQuerySystemInformation( + while ((status = pfnNtQuerySystemInformation( (SYSTEM_INFORMATION_CLASS)SystemHandleInformation, handleInfo, handleInfoSize, @@ -1670,9 +1672,9 @@ _NtQuerySystemInformation NtQuerySystemInformation = continue; /* Duplicate the handle so we can query it. */ - if (!NT_SUCCESS(NtDuplicateObject( + if (!NT_SUCCESS(pfnNtDuplicateObject( hproc, - handle.Handle, + (HANDLE)(ULONG_PTR)handle.Handle, GetCurrentProcess(), &dupHandle, 0, @@ -1686,7 +1688,7 @@ _NtQuerySystemInformation NtQuerySystemInformation = /* Query the object type. */ objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); - if (!NT_SUCCESS(NtQueryObject( + if (!NT_SUCCESS(pfnNtQueryObject( dupHandle, (OBJECT_INFORMATION_CLASS)ObjectTypeInformation, objectTypeInfo, @@ -1716,7 +1718,7 @@ _NtQuerySystemInformation NtQuerySystemInformation = } objectNameInfo = malloc(0x1000); - if (!NT_SUCCESS(NtQueryObject( + if (!NT_SUCCESS(pfnNtQueryObject( dupHandle, (OBJECT_INFORMATION_CLASS)ObjectNameInformation, objectNameInfo, @@ -1726,7 +1728,7 @@ _NtQuerySystemInformation NtQuerySystemInformation = { /* Reallocate the buffer and try again. */ objectNameInfo = realloc(objectNameInfo, returnLength); - if (!NT_SUCCESS(NtQueryObject( + if (!NT_SUCCESS(pfnNtQueryObject( dupHandle, (OBJECT_INFORMATION_CLASS)ObjectNameInformation, objectNameInfo, From 7dc1cce80b77cfecfcf1dc5aa2faf8e94dbcae60 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:27:29 -0500 Subject: [PATCH 07/14] fix: add DUPLICATE_SAME_ACCESS to param and add PROCESS_DUP_HANDLE --- main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index b244577..d9b06b9 100644 --- a/main.cpp +++ b/main.cpp @@ -1679,7 +1679,7 @@ _NtQueryObject pfnNtQueryObject = &dupHandle, 0, 0, - 0 + DUPLICATE_SAME_ACCESS ))) { printf("[%#x] Error!\n", handle.Handle); @@ -2044,7 +2044,7 @@ void PIDinspect(DWORD pid) { // ooh guys look i'm in the void - HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_DUP_HANDLE, FALSE, pid); // The above little handle opener is currently a somwehat "agressive" flag, since it // Requests read access directly to the process' actual memory. This can get us rejected if called // on a very high privilege process, such as lsass.exe This means that we can't read the memory From 1e502c931c7e263b3512f7d1a7d660f1eaf2d7f8 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:45:16 -0500 Subject: [PATCH 08/14] fix: check if object name is valid before checking it --- main.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index d9b06b9..12b5ac0 100644 --- a/main.cpp +++ b/main.cpp @@ -1716,7 +1716,26 @@ _NtQueryObject pfnNtQueryObject = CloseHandle(dupHandle); continue; } - + /* Query the object type first */ + objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); + if (!NT_SUCCESS(NtQueryObject(dupHandle, ObjectTypeInformation, objectTypeInfo, 0x1000, NULL))) { + printf("[%#x] Error!\n", handle.Handle); + CloseHandle(dupHandle); + continue; + } + + /* Check if this type is known to hang on name queries */ + WCHAR typeName[64]; + wcsncpy_s(typeName, 64, objectTypeInfo->Name.Buffer, objectTypeInfo->Name.Length / 2); + typeName[objectTypeInfo->Name.Length / 2] = L'\0'; + + if (wcscmp(typeName, L"File") == 0 || wcscmp(typeName, L"ALPC Port") == 0) { + printf("[%#x] %.*S: (name query skipped)\n", handle.Handle, + objectTypeInfo->Name.Length / 2, objectTypeInfo->Name.Buffer); + free(objectTypeInfo); + CloseHandle(dupHandle); + continue; + } objectNameInfo = malloc(0x1000); if (!NT_SUCCESS(pfnNtQueryObject( dupHandle, From cd53d7c16a6bcb843e5bc4602c9c93c918195727 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:50:14 -0500 Subject: [PATCH 09/14] =?UTF-8?q?fix:=F0=9F=A4=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 12b5ac0..294c1f7 100644 --- a/main.cpp +++ b/main.cpp @@ -1716,7 +1716,7 @@ _NtQueryObject pfnNtQueryObject = CloseHandle(dupHandle); continue; } - /* Query the object type first */ + /* Query the object type first */ objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); if (!NT_SUCCESS(NtQueryObject(dupHandle, ObjectTypeInformation, objectTypeInfo, 0x1000, NULL))) { printf("[%#x] Error!\n", handle.Handle); @@ -1736,6 +1736,7 @@ _NtQueryObject pfnNtQueryObject = CloseHandle(dupHandle); continue; } + objectNameInfo = malloc(0x1000); if (!NT_SUCCESS(pfnNtQueryObject( dupHandle, From 8919dfedcbc762a3f3bad4c5b5c8d676c468dc28 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:52:28 -0500 Subject: [PATCH 10/14] =?UTF-8?q?fix:=F0=9F=A4=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/main.cpp b/main.cpp index 294c1f7..90ff4d7 100644 --- a/main.cpp +++ b/main.cpp @@ -1700,22 +1700,6 @@ _NtQueryObject pfnNtQueryObject = CloseHandle(dupHandle); continue; } - - /* Query the object name (unless it has an access of - 0x0012019f, on which NtQueryObject could hang. */ - if (handle.GrantedAccess == 0x0012019f) - { - /* We have the type, so display that. */ - printf( - "[%#x] %.*S: (did not get name)\n", - handle.Handle, - objectTypeInfo->Name.Length / 2, - objectTypeInfo->Name.Buffer - ); - free(objectTypeInfo); - CloseHandle(dupHandle); - continue; - } /* Query the object type first */ objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); if (!NT_SUCCESS(NtQueryObject(dupHandle, ObjectTypeInformation, objectTypeInfo, 0x1000, NULL))) { @@ -1736,6 +1720,22 @@ _NtQueryObject pfnNtQueryObject = CloseHandle(dupHandle); continue; } + /* Query the object name (unless it has an access of + 0x0012019f, on which NtQueryObject could hang. */ + if (handle.GrantedAccess == 0x0012019f) + { + /* We have the type, so display that. */ + printf( + "[%#x] %.*S: (did not get name)\n", + handle.Handle, + objectTypeInfo->Name.Length / 2, + objectTypeInfo->Name.Buffer + ); + free(objectTypeInfo); + CloseHandle(dupHandle); + continue; + } + objectNameInfo = malloc(0x1000); if (!NT_SUCCESS(pfnNtQueryObject( From 42e7281fa790b4ce54d8ac17e804651183af8f6e Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:55:19 -0500 Subject: [PATCH 11/14] =?UTF-8?q?fix:=F0=9F=A4=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 90ff4d7..3018305 100644 --- a/main.cpp +++ b/main.cpp @@ -1702,7 +1702,7 @@ _NtQueryObject pfnNtQueryObject = } /* Query the object type first */ objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); - if (!NT_SUCCESS(NtQueryObject(dupHandle, ObjectTypeInformation, objectTypeInfo, 0x1000, NULL))) { + if (!NT_SUCCESS(pfnNtQueryObject(dupHandle, ObjectTypeInformation, objectTypeInfo, 0x1000, NULL))) { printf("[%#x] Error!\n", handle.Handle); CloseHandle(dupHandle); continue; From 6a49605d76edeb22943b9cc40e5653476a790791 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:59:47 -0500 Subject: [PATCH 12/14] =?UTF-8?q?fix:=F0=9F=98=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 3018305..cc6c505 100644 --- a/main.cpp +++ b/main.cpp @@ -1702,7 +1702,7 @@ _NtQueryObject pfnNtQueryObject = } /* Query the object type first */ objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); - if (!NT_SUCCESS(pfnNtQueryObject(dupHandle, ObjectTypeInformation, objectTypeInfo, 0x1000, NULL))) { + if (!NT_SUCCESS(pfnNtQueryObject(dupHandle, (OBJECT_INFORMATION_CLASS)ObjectTypeInformation, objectTypeInfo, 0x1000, NULL))) { printf("[%#x] Error!\n", handle.Handle); CloseHandle(dupHandle); continue; From 64db3d138ba8d5706bf2bf053acca6dc4769416a Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 6 Feb 2026 22:22:43 -0500 Subject: [PATCH 13/14] =?UTF-8?q?=F0=9F=A4=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.cpp | 93 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 27 deletions(-) diff --git a/main.cpp b/main.cpp index cc6c505..bbeeade 100644 --- a/main.cpp +++ b/main.cpp @@ -1604,6 +1604,59 @@ _NtQueryObject pfnNtQueryObject = (_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); +// Structure to pass data to the worker thread +struct QueryObjectThreadData { + _NtQueryObject pfnNtQueryObject; + HANDLE handle; + PVOID buffer; + ULONG bufferSize; + NTSTATUS status; + volatile BOOL completed; +}; + +// Worker thread function +DWORD WINAPI QueryObjectThreadProc(LPVOID param) { + QueryObjectThreadData* data = (QueryObjectThreadData*)param; + + data->status = data->pfnNtQueryObject( + data->handle, + (OBJECT_INFORMATION_CLASS)ObjectNameInformation, + data->buffer, + data->bufferSize, + NULL + ); + + data->completed = TRUE; + return 0; +} + +// Safe query with 1ms timeout +bool QueryObjectNameSafe(_NtQueryObject pfnNtQueryObject, HANDLE handle, + PVOID buffer, ULONG bufferSize, NTSTATUS* outStatus) { + QueryObjectThreadData threadData = {0}; + threadData.pfnNtQueryObject = pfnNtQueryObject; + threadData.handle = handle; + threadData.buffer = buffer; + threadData.bufferSize = bufferSize; + threadData.completed = FALSE; + + HANDLE hThread = CreateThread(NULL, 0, QueryObjectThreadProc, &threadData, 0, NULL); + if (!hThread) return false; + + // Wait for 1ms + DWORD waitResult = WaitForSingleObject(hThread, 1); + + if (waitResult == WAIT_TIMEOUT) { + // Hung! Kill the thread (leaks the thread but keeps us fast) + TerminateThread(hThread, 1); + CloseHandle(hThread); + return false; // Timed out + } + + *outStatus = threadData.status; + CloseHandle(hThread); + return true; // Success +} void ListProcHandles(HANDLE hproc, DWORD pid) { // this is so that we can get the handles of a process @@ -1708,33 +1761,19 @@ _NtQueryObject pfnNtQueryObject = continue; } - /* Check if this type is known to hang on name queries */ - WCHAR typeName[64]; - wcsncpy_s(typeName, 64, objectTypeInfo->Name.Buffer, objectTypeInfo->Name.Length / 2); - typeName[objectTypeInfo->Name.Length / 2] = L'\0'; - - if (wcscmp(typeName, L"File") == 0 || wcscmp(typeName, L"ALPC Port") == 0) { - printf("[%#x] %.*S: (name query skipped)\n", handle.Handle, - objectTypeInfo->Name.Length / 2, objectTypeInfo->Name.Buffer); - free(objectTypeInfo); - CloseHandle(dupHandle); - continue; - } - /* Query the object name (unless it has an access of - 0x0012019f, on which NtQueryObject could hang. */ - if (handle.GrantedAccess == 0x0012019f) - { - /* We have the type, so display that. */ - printf( - "[%#x] %.*S: (did not get name)\n", - handle.Handle, - objectTypeInfo->Name.Length / 2, - objectTypeInfo->Name.Buffer - ); - free(objectTypeInfo); - CloseHandle(dupHandle); - continue; - } + NTSTATUS status; +if (!QueryObjectNameSafe(pfnNtQueryObject, dupHandle, objectNameInfo, 0x1000, &status)) { + // Timed out after 1ms - likely a blocking handle + printf("[%#x] %.*S: (query timed out)\n", + handle.Handle, + objectTypeInfo->Name.Length / 2, + objectTypeInfo->Name.Buffer); + free(objectTypeInfo); + CloseHandle(dupHandle); + continue; +} + + objectNameInfo = malloc(0x1000); From 0e22033d018e176c0922108c93a722be0170b39b Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Sat, 7 Feb 2026 09:24:56 -0500 Subject: [PATCH 14/14] =?UTF-8?q?=F0=9F=98=A1=F0=9F=A4=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.cpp | 93 ++++++++++++++++---------------------------------------- 1 file changed, 27 insertions(+), 66 deletions(-) diff --git a/main.cpp b/main.cpp index bbeeade..cc6c505 100644 --- a/main.cpp +++ b/main.cpp @@ -1604,59 +1604,6 @@ _NtQueryObject pfnNtQueryObject = (_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); -// Structure to pass data to the worker thread -struct QueryObjectThreadData { - _NtQueryObject pfnNtQueryObject; - HANDLE handle; - PVOID buffer; - ULONG bufferSize; - NTSTATUS status; - volatile BOOL completed; -}; - -// Worker thread function -DWORD WINAPI QueryObjectThreadProc(LPVOID param) { - QueryObjectThreadData* data = (QueryObjectThreadData*)param; - - data->status = data->pfnNtQueryObject( - data->handle, - (OBJECT_INFORMATION_CLASS)ObjectNameInformation, - data->buffer, - data->bufferSize, - NULL - ); - - data->completed = TRUE; - return 0; -} - -// Safe query with 1ms timeout -bool QueryObjectNameSafe(_NtQueryObject pfnNtQueryObject, HANDLE handle, - PVOID buffer, ULONG bufferSize, NTSTATUS* outStatus) { - QueryObjectThreadData threadData = {0}; - threadData.pfnNtQueryObject = pfnNtQueryObject; - threadData.handle = handle; - threadData.buffer = buffer; - threadData.bufferSize = bufferSize; - threadData.completed = FALSE; - - HANDLE hThread = CreateThread(NULL, 0, QueryObjectThreadProc, &threadData, 0, NULL); - if (!hThread) return false; - - // Wait for 1ms - DWORD waitResult = WaitForSingleObject(hThread, 1); - - if (waitResult == WAIT_TIMEOUT) { - // Hung! Kill the thread (leaks the thread but keeps us fast) - TerminateThread(hThread, 1); - CloseHandle(hThread); - return false; // Timed out - } - - *outStatus = threadData.status; - CloseHandle(hThread); - return true; // Success -} void ListProcHandles(HANDLE hproc, DWORD pid) { // this is so that we can get the handles of a process @@ -1761,19 +1708,33 @@ _NtQueryObject pfnNtQueryObject = continue; } - NTSTATUS status; -if (!QueryObjectNameSafe(pfnNtQueryObject, dupHandle, objectNameInfo, 0x1000, &status)) { - // Timed out after 1ms - likely a blocking handle - printf("[%#x] %.*S: (query timed out)\n", - handle.Handle, - objectTypeInfo->Name.Length / 2, - objectTypeInfo->Name.Buffer); - free(objectTypeInfo); - CloseHandle(dupHandle); - continue; -} - - + /* Check if this type is known to hang on name queries */ + WCHAR typeName[64]; + wcsncpy_s(typeName, 64, objectTypeInfo->Name.Buffer, objectTypeInfo->Name.Length / 2); + typeName[objectTypeInfo->Name.Length / 2] = L'\0'; + + if (wcscmp(typeName, L"File") == 0 || wcscmp(typeName, L"ALPC Port") == 0) { + printf("[%#x] %.*S: (name query skipped)\n", handle.Handle, + objectTypeInfo->Name.Length / 2, objectTypeInfo->Name.Buffer); + free(objectTypeInfo); + CloseHandle(dupHandle); + continue; + } + /* Query the object name (unless it has an access of + 0x0012019f, on which NtQueryObject could hang. */ + if (handle.GrantedAccess == 0x0012019f) + { + /* We have the type, so display that. */ + printf( + "[%#x] %.*S: (did not get name)\n", + handle.Handle, + objectTypeInfo->Name.Length / 2, + objectTypeInfo->Name.Buffer + ); + free(objectTypeInfo); + CloseHandle(dupHandle); + continue; + } objectNameInfo = malloc(0x1000);