カーネルオブジェクトのハンドルから情報を取得する

`CreateEvent`や`CreateSemaphore`で作成したカーネルオブジェクトから情報を取得する方法のメモです。

以降で出現する`hHandle`は、下記のように作成した前提です。

```c

HANDLE hHandle = CreateEvent(NULL, FALSE, TRUE, L"sample");
```

なお、ファイルのパスを取得する場合は`GetFinalPathNameByHandle`を使えば簡単に取得できます。

## 下準備

この後使う`NtQueryObject`や`NtQuerySystemInformation`は、MSDNによるとインポートライブラリがないので、動的にリンクします。

```c
#include <winternl.h>
#include <ntstatus.h>

typedef
__kernel_entry NTSYSCALLAPI
NTSTATUS
(NTAPI
*NtQueryObject_t)(
	_In_opt_ HANDLE Handle,
	_In_ OBJECT_INFORMATION_CLASS ObjectInformationClass,
	_Out_writes_bytes_opt_(ObjectInformationLength) PVOID ObjectInformation,
	_In_ ULONG ObjectInformationLength,
	_Out_opt_ PULONG ReturnLength
);

typedef
__kernel_entry NTSTATUS
(NTAPI
*NtQuerySystemInformation_t)(
	IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
	OUT PVOID SystemInformation,
	IN ULONG SystemInformationLength,
	OUT PULONG ReturnLength OPTIONAL
);

HMODULE hNtDll = GetModuleHandle(L"ntdll.dll");
NtQueryObject_t fpNtQueryObject =  (NtQueryObject_t)GetProcAddress(hNtDll, "NtQueryObject");
NtQuerySystemInformation_t fpNtQuerySystemInformation = (NtQuerySystemInformation_t)GetProcAddress(hNtDll, "NtQuerySystemInformation");
```

## タイプの取得

ここまではドキュメント化されている範囲でできます。
`PUBLIC_OBJECT_TYPE_INFORMATION`と`ObjectTypeInformation`で種類(`Event`とか)を取得できます。

```c:GetTypeInfoFromHandle.c
ULONG size = 0;
fpNtQueryObject(hHandle, ObjectTypeInformation, NULL, 0, &size);
PPUBLIC_OBJECT_TYPE_INFORMATION buf = malloc(size);
NTSTATUS status = fpNtQueryObject(hHandle, ObjectTypeInformation, buf, size, &size);
if (NT_ERROR(status))
{
	free(buf);
	return;
}
printf("%ls\n", buf->TypeName.Buffer);
free(buf);
```

## 名前の取得

ここからはUndocumentedの範囲です。
**インターネットで適当に調べながら試したので、当然ながら正しく取得できる保証はありません。**

使用する構造体(`OBJECT_NAME_INFORMATION`)とコード(`ObjectNameInformation`)は自分で定義します。

```c
typedef struct _OBJECT_NAME_INFORMATION {
	UNICODE_STRING          Name;
	WCHAR                   NameBuffer[0];

} OBJECT_NAME_INFORMATION, * POBJECT_NAME_INFORMATION;

static const OBJECT_INFORMATION_CLASS ObjectNameInformation = (OBJECT_INFORMATION_CLASS)1;
```

```c:GetNameInfoFromHandle.c
ULONG size = 0;
fpNtQueryObject(hHandle, ObjectNameInformation, NULL, 0, &size);
POBJECT_NAME_INFORMATION buf = malloc(size);
NTSTATUS status = fpNtQueryObject(hHandle, ObjectNameInformation, buf, size, &size);
if (NT_ERROR(status))
{
	free(buf);
	return;
}
printf("%ls\n", buf->Name.Buffer);
free(buf);
```

`\Sessions\2\BaseNamedObjects\sample` のような文字列が取得できます。

## Object Address

Process Explorerで`Object Address`として表示される値です。
**当然ながら正しく取得できる保証はありません。**

調べた限り、ハンドルから直接取得する方法は無く、`SystemHandleInformation`で一旦システムのすべてのハンドルの情報を取得して、その中から検索するしかなさそうです。

```c
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
{
	USHORT UniqueProcessId;
	USHORT CreatorBackTraceIndex;
	UCHAR ObjectTypeIndex;
	UCHAR HandleAttributes;
	USHORT HandleValue;
	PVOID Object;
	ULONG GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
	ULONG NumberOfHandles;
	SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;

static const SYSTEM_INFORMATION_CLASS SystemHandleInformation = 16;
```

```c:GetObjectAddressFromHandle.c
DWORD dwPID = GetCurrentProcessId();
ULONG size = sizeof(SYSTEM_HANDLE_INFORMATION);
PSYSTEM_HANDLE_INFORMATION buf = NULL;
NTSTATUS status = STATUS_INFO_LENGTH_MISMATCH;
do
{
	size += sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO) * 0x10000;
	void *newPtr = realloc(buf, size);
	if (NULL == newPtr)
		break;
	buf = newPtr;
	ULONG returnLength = 0;
	status = fpNtQuerySystemInformation(SystemHandleInformation, buf, size, &returnLength);
} while (status == STATUS_INFO_LENGTH_MISMATCH);

if (NT_ERROR(status))
{
	free(buf);
	return;
}

for (ULONG i = 0; i < buf->NumberOfHandles;++i)
{
	if (buf->Handles[i].UniqueProcessId == dwPID && buf->Handles[i].HandleValue == (UINT_PTR)hHandle)
	{
		printf("%p\n", buf->Handles[i].Object);
	}
}
free(buf);
```

## Object Address(新しいAPIを使用)

前述の方法は古いみたいで、新しいAPIがあるみたいです。

```c

typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX
{
    PVOID Object;
    ULONG_PTR UniqueProcessId;
    ULONG_PTR HandleValue;
    ULONG GrantedAccess;
    USHORT CreatorBackTraceIndex;
    USHORT ObjectTypeIndex;
    ULONG HandleAttributes;
    ULONG Reserved;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX;

typedef struct _SYSTEM_HANDLE_INFORMATION_EX
{
	ULONG_PTR  NumberOfHandles;
	ULONG_PTR  Reserved;
	SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
} SYSTEM_HANDLE_INFORMATION_EX, * PSYSTEM_HANDLE_INFORMATION_EX;

static const SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = 0x40;
```

```c:GetObjectAddressFromHandleEx.c
DWORD dwPID = GetCurrentProcessId();
ULONG size = sizeof(SYSTEM_HANDLE_INFORMATION_EX);
PSYSTEM_HANDLE_INFORMATION_EX buf = NULL;
NTSTATUS status = STATUS_INFO_LENGTH_MISMATCH;
do
{
	size += sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX) * 0x10000;
	void* newPtr = realloc(buf, size);
	if (NULL == newPtr)
		break;
	buf = newPtr;
	ULONG returnLength = 0;
	status = fpNtQuerySystemInformation(SystemExtendedHandleInformation, buf, size, &returnLength);
} while (status == STATUS_INFO_LENGTH_MISMATCH);

if (NT_ERROR(status))
{
	free(buf);
	return -1;
}

for (ULONG i = 0; i < buf->NumberOfHandles; ++i)
{
	if (buf->Handles[i].UniqueProcessId == dwPID && buf->Handles[i].HandleValue == hHandle)
	{
		printf("%p\n", buf->Handles[i].Object);
	}
}
free(buf);
```