Created
May 27, 2021 22:10
-
-
Save lpBunny/01be9b089f22cf2ef6e3b6eea1ecf470 to your computer and use it in GitHub Desktop.
List process information including process architecture and username without opening any handles
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* | |
* List process information on windows without opening any handles, including process architecture and username | |
* | |
*/ | |
#include <Windows.h> | |
#include <stdio.h> | |
#include <math.h> | |
typedef struct _UNICODE_STRING { | |
USHORT Length; | |
USHORT MaximumLength; | |
PWSTR Buffer; | |
} UNICODE_STRING, * PUNICODE_STRING; | |
typedef enum _KTHREAD_STATE | |
{ | |
Initialized, | |
Ready, | |
Running, | |
Standby, | |
Terminated, | |
Waiting, | |
Transition, | |
DeferredReady, | |
GateWaitObsolete, | |
WaitingForProcessInSwap, | |
MaximumThreadState | |
} KTHREAD_STATE, * PKTHREAD_STATE; | |
typedef enum _KWAIT_REASON | |
{ | |
Executive, | |
FreePage, | |
PageIn, | |
PoolAllocation, | |
DelayExecution, | |
Suspended, | |
UserRequest, | |
WrExecutive, | |
WrFreePage, | |
WrPageIn, | |
WrPoolAllocation, | |
WrDelayExecution, | |
WrSuspended, | |
WrUserRequest, | |
WrEventPair, | |
WrQueue, | |
WrLpcReceive, | |
WrLpcReply, | |
WrVirtualMemory, | |
WrPageOut, | |
WrRendezvous, | |
WrKeyedEvent, | |
WrTerminated, | |
WrProcessInSwap, | |
WrCpuRateControl, | |
WrCalloutStack, | |
WrKernel, | |
WrResource, | |
WrPushLock, | |
WrMutex, | |
WrQuantumEnd, | |
WrDispatchInt, | |
WrPreempted, | |
WrYieldExecution, | |
WrFastMutex, | |
WrGuardedMutex, | |
WrRundown, | |
WrAlertByThreadId, | |
WrDeferredPreempt, | |
MaximumWaitReason | |
} KWAIT_REASON, * PKWAIT_REASON; | |
typedef LONG KPRIORITY; | |
typedef struct _CLIENT_ID { | |
HANDLE UniqueProcess; | |
HANDLE UniqueThread; | |
} CLIENT_ID; | |
typedef struct _SYSTEM_THREAD_INFORMATION | |
{ | |
LARGE_INTEGER KernelTime; | |
LARGE_INTEGER UserTime; | |
LARGE_INTEGER CreateTime; | |
ULONG WaitTime; | |
PVOID StartAddress; | |
CLIENT_ID ClientId; | |
KPRIORITY Priority; | |
LONG BasePriority; | |
ULONG ContextSwitches; | |
KTHREAD_STATE ThreadState; | |
KWAIT_REASON WaitReason; | |
} SYSTEM_THREAD_INFORMATION, * PSYSTEM_THREAD_INFORMATION; | |
typedef struct _SYSTEM_EXTENDED_THREAD_INFORMATION | |
{ | |
SYSTEM_THREAD_INFORMATION ThreadInfo; | |
PVOID StackBase; | |
PVOID StackLimit; | |
PVOID Win32StartAddress; | |
PVOID TebBase; // since VISTA | |
ULONG_PTR Reserved2; | |
ULONG_PTR Reserved3; | |
ULONG_PTR Reserved4; | |
} SYSTEM_EXTENDED_THREAD_INFORMATION, * PSYSTEM_EXTENDED_THREAD_INFORMATION; | |
typedef struct _SYSTEM_PROCESS_INFORMATION | |
{ | |
ULONG NextEntryOffset; | |
ULONG NumberOfThreads; | |
LARGE_INTEGER WorkingSetPrivateSize; // since VISTA | |
ULONG HardFaultCount; // since WIN7 | |
ULONG NumberOfThreadsHighWatermark; // since WIN7 | |
ULONGLONG CycleTime; // since WIN7 | |
LARGE_INTEGER CreateTime; | |
LARGE_INTEGER UserTime; | |
LARGE_INTEGER KernelTime; | |
UNICODE_STRING ImageName; | |
KPRIORITY BasePriority; | |
HANDLE UniqueProcessId; | |
HANDLE InheritedFromUniqueProcessId; | |
ULONG HandleCount; | |
ULONG SessionId; | |
ULONG_PTR UniqueProcessKey; // since VISTA (requires SystemExtendedProcessInformation) | |
SIZE_T PeakVirtualSize; | |
SIZE_T VirtualSize; | |
ULONG PageFaultCount; | |
SIZE_T PeakWorkingSetSize; | |
SIZE_T WorkingSetSize; | |
SIZE_T QuotaPeakPagedPoolUsage; | |
SIZE_T QuotaPagedPoolUsage; | |
SIZE_T QuotaPeakNonPagedPoolUsage; | |
SIZE_T QuotaNonPagedPoolUsage; | |
SIZE_T PagefileUsage; | |
SIZE_T PeakPagefileUsage; | |
SIZE_T PrivatePageCount; | |
LARGE_INTEGER ReadOperationCount; | |
LARGE_INTEGER WriteOperationCount; | |
LARGE_INTEGER OtherOperationCount; | |
LARGE_INTEGER ReadTransferCount; | |
LARGE_INTEGER WriteTransferCount; | |
LARGE_INTEGER OtherTransferCount; | |
} SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION; | |
typedef struct _PROCESS_DISK_COUNTERS | |
{ | |
ULONGLONG BytesRead; | |
ULONGLONG BytesWritten; | |
ULONGLONG ReadOperationCount; | |
ULONGLONG WriteOperationCount; | |
ULONGLONG FlushOperationCount; | |
} PROCESS_DISK_COUNTERS, * PPROCESS_DISK_COUNTERS; | |
typedef union _ENERGY_STATE_DURATION | |
{ | |
union | |
{ | |
ULONGLONG Value; | |
ULONG LastChangeTime; | |
}; | |
ULONG Duration : 31; | |
ULONG IsInState : 1; | |
} ENERGY_STATE_DURATION, * PENERGY_STATE_DURATION; | |
typedef struct _PROCESS_ENERGY_VALUES | |
{ | |
ULONGLONG Cycles[4][2]; | |
ULONGLONG DiskEnergy; | |
ULONGLONG NetworkTailEnergy; | |
ULONGLONG MBBTailEnergy; | |
ULONGLONG NetworkTxRxBytes; | |
ULONGLONG MBBTxRxBytes; | |
ENERGY_STATE_DURATION Durations[3]; | |
ULONG CompositionRendered; | |
ULONG CompositionDirtyGenerated; | |
ULONG CompositionDirtyPropagated; | |
ULONG Reserved1; | |
ULONGLONG AttributedCycles[4][2]; | |
ULONGLONG WorkOnBehalfCycles[4][2]; | |
} PROCESS_ENERGY_VALUES, * PPROCESS_ENERGY_VALUES; | |
typedef struct _SYSTEM_PROCESS_INFORMATION_EXTENSION | |
{ | |
PROCESS_DISK_COUNTERS DiskCounters; | |
ULONGLONG ContextSwitches; | |
ULONG Flags; | |
ULONG UserSidOffset; | |
ULONG PackageFullNameOffset; | |
PROCESS_ENERGY_VALUES EnergyValues; | |
ULONG AppIdOffset; | |
SIZE_T SharedCommitCharge; | |
ULONG JobObjectId; | |
ULONG SpareUlong; | |
ULONGLONG ProcessSequenceNumber; | |
} SYSTEM_PROCESS_INFORMATION_EXTENSION, * PSYSTEM_PROCESS_INFORMATION_EXTENSION; | |
#define SeDebugPrivilege 20 | |
#define SystemExtendedProcessInformation 0x39 | |
#define SystemFullProcessInformation 0x94 | |
typedef NTSTATUS(NTAPI* _RtlAdjustPrivilege)(int Privilege, bool Enable, bool ThreadPrivilege, bool* Previous); | |
typedef NTSTATUS(NTAPI* _NtQuerySystemInformation)(int SystemInformationClass, PVOID SystemInformation, DWORD SystemInformationLength, PDWORD ReturnLength); | |
#define NT_SUCCESS(status) ((status) >= 0) | |
int main() | |
{ | |
HMODULE ntdll = GetModuleHandleA("ntdll.dll"); | |
_NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetProcAddress(ntdll, "NtQuerySystemInformation"); | |
//We can fetch process usernames without a handle using SystemFullProcessInformation, | |
//but it appears to only be available for x64 and as admin | |
#ifdef _WIN64 | |
bool b; | |
_RtlAdjustPrivilege RtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(ntdll, "RtlAdjustPrivilege"); | |
bool use_full_info = NT_SUCCESS(RtlAdjustPrivilege(SeDebugPrivilege, true, false, &b)); | |
#else | |
bool use_full_info = false; | |
#endif | |
int info_class = use_full_info ? SystemFullProcessInformation : SystemExtendedProcessInformation; | |
DWORD buffer_length; | |
NtQuerySystemInformation(info_class, NULL, 0, &buffer_length); | |
//allocate some extra space in case some extra threads or processes were created inbetween calls | |
buffer_length += 0x1000; | |
PVOID buffer = LocalAlloc(0, buffer_length); | |
if (!buffer) { | |
printf("[-] Failed to allocate memory\n"); | |
return 1; | |
} | |
NTSTATUS status = NtQuerySystemInformation(info_class, buffer, buffer_length, &buffer_length); | |
if (!NT_SUCCESS(status)) { | |
printf("[-] NtQuerySystemInformation failed (0x%08X)\n", status); | |
return 1; | |
} | |
PSYSTEM_PROCESS_INFORMATION proc = (PSYSTEM_PROCESS_INFORMATION)buffer; | |
printf(" PID PPID Process Name Arch Session %s\n", use_full_info ? "User" : ""); | |
printf(" --- ---- ------------ ---- ------- %s\n", use_full_info ? "----" : ""); | |
do { | |
proc = (PSYSTEM_PROCESS_INFORMATION)(((PBYTE)proc) + proc->NextEntryOffset); | |
ULONG pid = (ULONG)proc->UniqueProcessId; | |
ULONG ppid = (ULONG)proc->InheritedFromUniqueProcessId; | |
ULONG session = proc->SessionId; | |
//get the process name | |
PWCHAR image_name = proc->ImageName.Buffer; | |
PWCHAR proc_name = image_name; | |
//FullProcessInfo returns the entire NT path, so select the last part | |
if (use_full_info) { | |
wchar_t* last_slash = wcsrchr(image_name, L'\\'); | |
if(last_slash) | |
proc_name = last_slash + 1; | |
} | |
//get the architecture. | |
//in theory this could be incorrect for an x64 process (all threads are located in exe with a non-dynamic base with | |
//an imagebase < 0xffffffff), but in practice it's very accurate | |
PSYSTEM_EXTENDED_THREAD_INFORMATION threads = (PSYSTEM_EXTENDED_THREAD_INFORMATION)(((PBYTE)proc) + sizeof(SYSTEM_PROCESS_INFORMATION)); | |
bool x86 = true; | |
for (ULONG i = 0; i < proc->NumberOfThreads; i++) | |
if (((ULONGLONG)(threads[i].Win32StartAddress)) > 0xffffffff) | |
x86 = false; | |
//get the user (only available if admin and x64) | |
CHAR userbuffer[1024] = { 0 }; | |
if (use_full_info) { | |
PSYSTEM_PROCESS_INFORMATION_EXTENSION process_ext = (PSYSTEM_PROCESS_INFORMATION_EXTENSION)(((PBYTE)threads) + (sizeof(SYSTEM_EXTENDED_THREAD_INFORMATION) * proc->NumberOfThreads)); | |
PSID psid = (PSID)(((PBYTE)process_ext) + process_ext->UserSidOffset); | |
SID_NAME_USE sidtype = SidTypeUnknown; | |
DWORD account_length = 0; | |
DWORD domain_length = 0; | |
LookupAccountSidA(NULL, psid, NULL, &account_length, NULL, &domain_length, &sidtype); | |
LookupAccountSidA(NULL, psid, userbuffer + domain_length, &account_length, userbuffer, &domain_length, &sidtype); | |
if (domain_length > 0) | |
userbuffer[domain_length] = '\\'; | |
} | |
//Ugly format string but it comes out looking pretty *shrug emoji* | |
printf(" %d %*s%d %*s%ls %*s%s %d %*s%s\n", pid, 10 - (int)(log10(pid ? pid : 1)), "", ppid, 10 - (int)(log10(ppid ? ppid : 1)), "", proc_name, 38 - wcslen(proc_name), "", x86 ? "x86" : "x64", session, 10 - (int)(log10(session ? session : 1)), "", userbuffer); | |
} while (proc->NextEntryOffset); | |
LocalFree(buffer); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment