句柄的定义

每个进程都有一个表,这个表中的每一项保存着需要访问的内核对象信息,系统为用户态应用程序提供一个“句柄”值,这个句柄值实际上是这个表的某种索引,通过这个值,可以在表中定位到具体需要访问的内核对象信息。用户态程序通过API创建或打开一个内核对象时,这个表中的信息会增加一项,用来描述这个内核对象的信息,并产生一个相应的句柄值,用户态程序把这个句柄传递到相应API,API进入内核后,通过这个句柄值定位到需要操作的内核对象,对内核对象进行相应的操作。

typedef void *HANDLE;
typedef HANDLE *PHANDLE

内核对象的引用计数

每个内核对象存在两个计数,一个称为“句柄计数”,另一个称为“指针计数”,句柄计数是指这个内核对象被多少个句柄值所指向,如在用户态中创建一个命名的EVENT对象,获取到一个句柄,那么这个EVENT的句柄计数就是1,当其他程序通过该EVENT名字打开该EVENT时,会获取到另外一个句柄,这时候,句柄计数等于2。指针计数是在句柄计数基础上递增的计数,在刚才所提的例子中,句柄计数等于2,指针计数也等于2,句柄计数的增加,会相应导致指针计数增加,同理,句柄计数的减少,会相应导致指针计数减少,但指针计数可以独立增加与减少而不影响句柄计数。当一个对象的指针计数等于0的时候,这个对象会被系统释放。请注意,不同操作系统,系统对引用计数值的管理稍有不同。

句柄操作CODE

CODE

#include <stdio.h>
#include <ntddk.h>
#include <ntifs.h>

BOOLEAN EventOperationSample()
{
BOOLEAN bSucc = FALSE;
HANDLE hCreateEvent = NULL;
PVOID pCrateEventObject = NULL;
HANDLE hOpenEvent = NULL;
PVOID pOpenEventObject = NULL;

do
{
OBJECT_ATTRIBUTES ObjAttr = { 0 };
UNICODE_STRING uNameString = { 0 };
RtlInitUnicodeString(&uNameString, L"\\BaseNameObjects\\TestEvent");
InitializeObjectAttributes(&ObjAttr, &uNameString, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
ZwCreateEvent(&hCreateEvent, EVENT_ALL_ACCESS, &ObjAttr, SynchronizationEvent, FALSE);
if (hCreateEvent == NULL)
{
break;
}

ObReferenceObjectByHandle(hCreateEvent, EVENT_ALL_ACCESS, *ExEventObjectType, KernelMode, &pCrateEventObject, NULL);
if (hOpenEvent == NULL)
{
break;
}

ObReferenceObjectByHandle(hOpenEvent, EVENT_ALL_ACCESS, *ExEventObjectType, KernelMode, &pCrateEventObject, NULL);

if (pOpenEventObject == NULL)
{
break;
}

DbgPrint("Create Handle: %p, Create Pointer = %p\n", hCreateEvent, pCrateEventObject);
DbgPrint("Open Handle: %p, Open Pointer = %p\n", hOpenEvent, pOpenEventObject);
bSucc = TRUE;
} while (FALSE);
if (pCrateEventObject != NULL)
{
ObDereferenceObject(pCrateEventObject);
pCrateEventObject = NULL;
}

if (hCreateEvent != NULL)
{
ZwClose(hCreateEvent);
hCreateEvent = NULL;
}

if (pOpenEventObject != NULL)
{
ObDereferenceObject(pOpenEventObject);
pOpenEventObject = NULL;
}

if (hOpenEvent != NULL)
{
ZwClose(hOpenEvent);
hOpenEvent = NULL;
}

return bSucc;
}


逻辑分析

ZwCreateEvent成功后,句柄会保存在hCreateEvent变量中,这个句柄是一个内核句柄。接着代码中调用ObReferenceObåjectByHandle函数,获取hCreateEvent句柄对应的EVENT对象指针。

例子代码在调用ObReferenceObjectByHandle函数成功后,使用ZwOpenEvent函数再次打开刚才创建的EVENT并获取一个句柄,保存在hOpenEvent变量中;接着通过hOpenEvent的值,使用ObReferenceObjectByHandle获取EVENT的对象指针,保存在pOpenEventObject变量中。

所以最后会产生两个句柄以及两个对象指针。代码通过DbgPrint函数把这些信息输出:

Create Handle : FFFFFFFF80000B90, Create Pointer = FFFFD1898059C260
Open Handle : FFFFFFFF800003648, Create Pointer = FFFFD1898059C260

ZwCreateEvent 函数

ZwCreateEvent 函数创建对象 EVENT 对象

NASTATUS ZwCreateEvent(
PHANDLE EventHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES objectAttributes,
EVENT_TYPE EventType,
BOOLEAN InitialState
);

EventHandle 参数用于保存EVENT的句柄;

DesiredAccess 参数 表示EVENT的权限;

ObjectAttributes 参数表示创建EVENT的属性信息;

EventType 参数表示EVENT的类型,取值为SynchronizationEvent或者NotificationEvent,分别表示同步类型EVENT以及通告类型的EVENT;

InitialState 参数 表示EVENT的初始状态,TRUE表示EVENT被创建后的状态为“有信号”,否则为“无信号”。

注意 :ObjectAttributes参数的用法最为重要,ObjectAttributes描述了需要打开或创建的内核对象属性,大部分涉及打开或创建内核对象的API都会有ObjectAttributes参数。

OBJECT_ATTRIBUTES 结构体

OBJECT_ATTRIBUTES 结构描述内核对象的属性;属性包括对象的名字,根目录

的句柄,对象的安全描述。

typedef struct _OBJECT_ATTRIBUTES
{
ULONG length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQuakityofService;
} OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES;

Length 成员表示结构体的大小,一般等于sizeof(OBJECT_ATTRIBUTES),RootDirectory 表示对象的根目录句柄,可以为NULL。

ObjectName 是一个UNICODE_STRING的指针,表示对象的路径或名字,RootDirectoryObjectName 共同组成了一个完整的对象全路径名字。Attributes 表示对象打开或创建时的具体属性,常见的有:

  • OBJ_INHERIT 表示内核对象的句柄可以被继承
  • OBJ_CASE_INSENSITIVE 表示内核对象的名字不区分大小写。
  • OBJ_KERNEL_HANDLE 表示使用内核句柄,即句柄存在于系统句柄表中。
  • SecurityDescriptor以及SecurityQualityOfService与安全性相关,可以暂设置成NULL。

ObReferenceObjectByHandle 函数

ObReferenceObjectByHandle 函数用于获取句柄对应的对象的指针。

ObReferenceObjectByHandle 函数成功则返回STATUS_SUCCESS,失败则返回错误码。函数原型如下:

NTSTATUS ObReferenceObjectByHandle(
HANDLE Handle,
ACCESS_MASK DesiredAccess,
POBJECT_TYPE ObjectType,
KPROCESSOR_MODE AccessMode,
PVOID* object,
POBJECT_HANDLE_INFORMATION HandleInformation
);

Handle 参数表示句柄值。

DesiredAccess 参数表示需要获取此对象的权限,针对不同类型的对象,这个权限的值不同,对于本例的EVENT对象来说,这个值可以传递EVENT_ALL_ACCESS,表示EVENT的所有权限。

ObjectType 参数 表示对象的类型,不同的对象用不同的“对象类型”来表示,

AccessMode 参数表示访问模式,可以是KernelMode或UserMode,分别表示用户态与内核态。如果参数Handle是内核句柄,则应该传递KernelMode。参数Object是返回参数,若函数执行成功则该参数保存对象的指针。HandleInformation 参数暂时没用,可以设置为NULL。

⬆︎TOP