Skip to content

Instantly share code, notes, and snippets.

@lpBunny
Created May 27, 2021 22:10
Show Gist options
  • Save lpBunny/01be9b089f22cf2ef6e3b6eea1ecf470 to your computer and use it in GitHub Desktop.
Save lpBunny/01be9b089f22cf2ef6e3b6eea1ecf470 to your computer and use it in GitHub Desktop.
List process information including process architecture and username without opening any handles
/*
*
* 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