It is common to some defenses to detect patterns, one typicall is create a handle of a remote process. The idea is to enumerate them and find a process who has full access 0x1fffff
to a process, use that handle to inject our code.
NtQuerySystemInformation (NativeAPI)
The NtQuerySystemInformation
function can return a list of all open handles in the system, including those associated with a specific process. This technique uses the SystemHandleInformation
(code 16) to obtain information about handles.
__kernel_entry NTSTATUS NtQuerySystemInformation(
[in] SYSTEM_INFORMATION_CLASS SystemInformationClass,
[in, out] PVOID SystemInformation,
[in] ULONG SystemInformationLength,
[out, optional] PULONG ReturnLength
);
Since SYSTEM_HANDLE_INFORMATION
is not defined we should define the structs.
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;
There are different types of Handles (directory, token, file, events, keys…) each one has a different ObjectTypeNumer
.
ObjectTypeNumber | ObjectType | Description |
---|---|---|
0x07 | Process | Open process |
0x08 | Thread | Tread of a process |
0x05 | Token | Security token |
0x25 | File | Open file |
0x2C | Key | Windows registry key |
0x0D | FileMap | File mapping (shared memory) |
0x03 | Directory | Directory object |
To query information of a handle of other process we need to duplicate it NtDuplicateObject
on the current process and query it NtQueryObject
.
Here an example of how to list all the handles of a specified PID process:
#include <Windows.h>
#include <stdio.h>
#include <winternl.h>
#define SystemHandleInformation 16
#define ObjectTypeInformation 2
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#define HANDLE_TOKEN 0x04
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[1024];
} 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;
typedef NTSTATUS (NTAPI * t_NtQuerySystemInformation)(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
typedef NTSTATUS (NTAPI * t_NtQueryObject)(
HANDLE Handle,
ULONG ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength,
PULONG ReturnLength
);
typedef NTSTATUS (NTAPI * t_NtDuplicateObject)(
HANDLE SourceProcessHandle,
HANDLE SourceHandle,
HANDLE TargetProcessHandle,
PHANDLE TargetHandle,
ACCESS_MASK DesiredAccess,
ULONG Attributes,
ULONG Options
);
int ListHandles(int pid) {
DWORD sysHandleSize = 0x10000;
SYSTEM_HANDLE_INFORMATION * sysHandle;
NTSTATUS status;
DWORD hinfoSize = 0x100;
POBJECT_TYPE_INFORMATION hInfo;
NTSTATUS status2;
HANDLE hProc;
HANDLE dupHandle;
// Link NtAPIS
t_NtQuerySystemInformation pNtQuerySystemInformation;
pNtQuerySystemInformation = (t_NtQuerySystemInformation)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtQuerySystemInformation");
t_NtDuplicateObject pNtDuplicateObject;
pNtDuplicateObject = (t_NtDuplicateObject) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDuplicateObject");
t_NtQueryObject pNtQueryObject;
pNtQueryObject =(t_NtQueryObject)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtQueryObject");
// Get INITIAL size
sysHandle = (SYSTEM_HANDLE_INFORMATION*)malloc(sysHandleSize);
while((status = pNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS) SystemHandleInformation, sysHandle, sysHandleSize, NULL))==STATUS_INFO_LENGTH_MISMATCH){
sysHandle = (SYSTEM_HANDLE_INFORMATION *)realloc(sysHandle,sysHandleSize*=2);
}
printf("PID\tAddress\t\tObjectTypeNumber\tGrantedAccess\tObjectType\n");
printf("---------------------------------------------------\n");
for (int i=0; i<sysHandle->HandleCount; i++){
SYSTEM_HANDLE handle = sysHandle->Handles[i];
if(handle.ProcessId == pid){
// Duplicate the handle to query it
hProc = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, FALSE, handle.ProcessId);
if((pNtDuplicateObject(hProc, (void *) handle.Handle, GetCurrentProcess(), &dupHandle, 0, 0, DUPLICATE_SAME_ACCESS)) < 0){
continue;
}
// Get info about the handle
hInfo = (OBJECT_TYPE_INFORMATION *)malloc(hinfoSize);
while((status2 = pNtQueryObject((HANDLE)dupHandle, ObjectTypeInformation, hInfo, hinfoSize, &hinfoSize))==STATUS_INFO_LENGTH_MISMATCH){
hInfo = (OBJECT_TYPE_INFORMATION *)realloc(hInfo, hinfoSize*=2);
}
UNICODE_STRING typeName = (UNICODE_STRING)hInfo->Name;
wprintf(L"%#5d\t%#10p\t%#16x\t%#13x\t%.*s\n", handle.ProcessId, handle.Object, handle.ObjectTypeNumber,handle.GrantedAccess,typeName.Length/sizeof(WCHAR), typeName.Buffer);
}
}
return 0;
}
int main(int argc, char ** argv) {
int pid = 5816;
ListHandles(pid);
return 0;
}