几个常用的内核函数
在Windows的内核管理层(Executive),对象管理是个很重要的机制。Windows对象管理中,有些著作喜欢将其称为"子系统",其实也就是内核管理层。下面讲介绍几个常用的内核中对象管理的函数。
ObReferenceObjectByHandle
这个函数根据Handle,返回一个该Handle对应的对象。
//
// Converts to and from a Kernel Handle to a normal handle
//
#define ObKernelHandleToHandle(Handle) \ //将内核句柄的相应标志位去掉
(HANDLE)((ULONG_PTR)(Handle) & ~KERNEL_HANDLE_FLAG)
#define ObMarkHandleAsKernelHandle(Handle) \
(HANDLE)((ULONG_PTR)(Handle) | KERNEL_HANDLE_FLAG)
#define ObpGetHandleObject(x) \ //获取的是对象头
((POBJECT_HEADER)((ULONG_PTR)x->Object & ~OBJ_HANDLE_ATTRIBUTES))
NTSTATUS
NTAPI
ObReferenceObjectByHandle(IN HANDLE Handle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID* Object,
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL)
{
PHANDLE_TABLE_ENTRY HandleEntry;
POBJECT_HEADER ObjectHeader;
ACCESS_MASK GrantedAccess;
ULONG Attributes;
PEPROCESS CurrentProcess;
PVOID HandleTable;
PETHREAD CurrentThread;
NTSTATUS Status;
PAGED_CODE();
/* Assume failure */
*Object = NULL;
/* Check if the caller wants the current process */
if ((Handle == NtCurrentProcess()) && //当Handle值是NtCurrentProcess时 特殊对待
((ObjectType == PsProcessType) || !(ObjectType)))
{
/* Get the current process */
CurrentProcess = PsGetCurrentProcess(); //调用PsGetCurrentProcess获取EPROCESS结构
/* Check if the caller wanted handle information */
if (HandleInformation) //如果要求返回句柄信息的话 填写相应的字段
{
/* Return it */
HandleInformation->HandleAttributes = 0;
HandleInformation->GrantedAccess = PROCESS_ALL_ACCESS;
}
/* Reference ourselves */
ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentProcess); //获取对象头
InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1); //获取对象头的目的是对 Header的PointerCount 即对该对象的引用计数加1
/* Return the pointer */
*Object = CurrentProcess;//EPROCESS结构即要找的对象
return STATUS_SUCCESS;
}
else if (Handle == NtCurrentProcess()) //若Handle值是NtCurrentProcess,但类型不匹配 说明传入的类型出现错误
{
/* The caller used this special handle value with a non-process type */
return STATUS_OBJECT_TYPE_MISMATCH;
}
/* Check if the caller wants the current thread */
if ((Handle == NtCurrentThread()) && //对于Handle值为当前线程而言,同样如此
((ObjectType == PsThreadType) || !(ObjectType)))
{
/* Get the current thread */
CurrentThread = PsGetCurrentThread();
/* Check if the caller wanted handle information */
if (HandleInformation)
{
/* Return it */
HandleInformation->HandleAttributes = 0;
HandleInformation->GrantedAccess = THREAD_ALL_ACCESS;
}
/* Reference ourselves */
ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentThread);
InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1);
/* Return the pointer */
*Object = CurrentThread;
return STATUS_SUCCESS;
}
else if (Handle == NtCurrentThread())
{
/* The caller used this special handle value with a non-thread type */
return STATUS_OBJECT_TYPE_MISMATCH;
}
//下面的是一般情况
/* Check if this is a kernel handle */
if (ObIsKernelHandle(Handle, AccessMode)) //若该句柄是内核句柄
{
/* Use the kernel handle table and get the actual handle value */
Handle = ObKernelHandleToHandle(Handle); //也就是将最高位(表示此时是kernel_handle)的标志位清零,因为之后要查找
HandleTable = ObpKernelHandleTable; //选用内核句柄表
}
else
{
/* Otherwise use this process's handle table */
HandleTable = PsGetCurrentProcess()->ObjectTable; //选用本进程的句柄表
}
/* Enter a critical region while we touch the handle table */
ASSERT(HandleTable != NULL);
KeEnterCriticalRegion(); //进入临界区
/* Get the handle entry */
HandleEntry = ExMapHandleToPointer(HandleTable, Handle); //调用ExMapHandleToPointer根据句柄表和对应的句柄获取对应的句柄表项 非常重要的一个函数
if (HandleEntry) //如果找到了对应的句柄表项
{
/* Get the object header and validate the type*/
ObjectHeader = ObpGetHandleObject(HandleEntry); //根据句柄表项获取对应的对象头
if (!(ObjectType) || (ObjectType == ObjectHeader->Type))
{
/* Get the granted access and validate it */
GrantedAccess = HandleEntry->GrantedAccess;
if ((AccessMode == KernelMode) || //若访问机制不冲突
!(~GrantedAccess & DesiredAccess))
{
/* Reference the object directly since we have its header */
InterlockedIncrement(&ObjectHeader->PointerCount); //对该对象的引用计数加一
/* Mask out the internal attributes */
Attributes = HandleEntry->ObAttributes & OBJ_HANDLE_ATTRIBUTES;
/* Check if the caller wants handle information */
if (HandleInformation) //若需要句柄信息 进行填写
{
/* Fill out the information */
HandleInformation->HandleAttributes = Attributes;
HandleInformation->GrantedAccess = GrantedAccess;
}
/* Return the pointer */
*Object = &ObjectHeader->Body; //返回对应对象的主体 即具体对象的数据结构
/* Unlock the handle */
ExUnlockHandleTableEntry(HandleTable, HandleEntry);
KeLeaveCriticalRegion(); //离开临界区
/* Return success */
ASSERT(*Object != NULL);
return STATUS_SUCCESS;
}
else
{
/* Requested access failed */
Status = STATUS_ACCESS_DENIED;
}
}
else
{
/* Invalid object type */
Status = STATUS_OBJECT_TYPE_MISMATCH;
}
/* Unlock the entry */
ExUnlockHandleTableEntry(HandleTable, HandleEntry);
}
else
{
/* Invalid handle */
Status = STATUS_INVALID_HANDLE;
}
/* Return failure status */
KeLeaveCriticalRegion();
*Object = NULL;
return Status;
}
对于当前进程和当前线程的句柄值而言,他们是很特殊的,Windows将它们也视为一个对象!!!。其对应的当前进程和当前线程的句柄值通过NtCurrentProcess和NtCurrentThread可以获取,注意这里要跟获取EPROCESS的函数 PsGetCurrentProcess进行区分。
#define NtCurrentProcess ( (HANDLE)(ULONG_PTR) -1 )
#define NtCurrentThread ( (HANDLE)(ULONG_PTR) -2 )
NtCurrentProcess是句柄值!!!,而PsGetCurrentProcess()则是函数IoGetCurrentProcess,它返回的是当前进程的EPROCESS数据结构的地址。
但是-1 和 -2 并不是真正的句柄,所以需要特殊处理。于是如果ObReferenceObjectByHandle 给定的句柄是-1,并且ObjectType是PsProcessType或者是默认ObjectType(即传入0),那么就会进入这条控制流,通过PsGetCurrentProcess来获取对象的数据结构。
当然大部分情况是下面的情况。也就是从句柄表获取相应的句柄表项,然后获取对象地址。
- 其流程是先根据是否是内核句柄选择相应的句柄表,若是内核句柄,则选择内核句柄表的时候,需要将KERNEL_HANDLE_FLAG的值设置为0。
- 然后调用ExMapHandleToPointer,根据选择的句柄表和我们传入的Handle找到相应的句柄表,这是一个很重要的函数,我们等会会去看它。
- 获取句柄表项后,其句柄表项就对应着相应的对象,通过ObpGetHandleObject宏可以获取到相应的对象,注意的是这里的对象Object,其实是ObjectHeader对象头。成功获取后,需要对其对象头字段的PointerCount,即引用计数要加一,最后返回的时候要返回它的Body,也就是ObjectHeader->Body。
ExMapHandleToPointer
看下它根据句柄表和句柄寻找句柄表项的主要逻辑。
ObReferenceObjectByHandle->ExMapHandleToPointer
PHANDLE_TABLE_ENTRY
NTAPI
ExMapHandleToPointer(IN PHANDLE_TABLE HandleTable,
IN HANDLE Handle)
{
EXHANDLE ExHandle;
PHANDLE_TABLE_ENTRY HandleTableEntry;
PAGED_CODE();
/* Set the handle value */
ExHandle.GenericHandleOverlay = Handle; //可以看出这里将Handle用ExHandle包装了起来
/* Fail if we got an invalid index */
if (!(ExHandle.Index & (LOW_LEVEL_ENTRIES - 1))) return NULL; //如果最低层的索引为0 表明出现问题
/* Do the lookup */
HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, ExHandle); //最终调用ExpLookupHandleTableEntry完成查找
if (!HandleTableEntry) return NULL;
/* Lock it */
if (!ExpLockHandleTableEntry(HandleTable, HandleTableEntry)) return NULL;
/* Return the entry */
return HandleTableEntry;
}
这里将Handle用ExHandle包装了起来,然后判断该Handle是否合法,最后将查找的工作留给了ExpLookupHandleTableEntry
ExHandle的结构如下
#define LOW_LEVEL_ENTRIES (PAGE_SIZE / sizeof(HANDLE_TABLE_ENTRY))
#define MID_LEVEL_ENTRIES (PAGE_SIZE / sizeof(PHANDLE_TABLE_ENTRY))
#define HIGH_LEVEL_ENTRIES (16777216 / (LOW_LEVEL_ENTRIES * MID_LEVEL_ENTRIES))
#define HANDLE_LOW_BITS (PAGE_SHIFT - 3)
#define HANDLE_HIGH_BITS (PAGE_SHIFT - 2)
#define HANDLE_TAG_BITS (2)
#define HANDLE_INDEX_BITS (HANDLE_LOW_BITS + 2*HANDLE_HIGH_BITS) // 9 + 20
#define KERNEL_FLAG_BITS (sizeof(PVOID)*8 - HANDLE_INDEX_BITS - HANDLE_TAG_BITS)
typedef union _EXHANDLE
{
struct
{
ULONG_PTR TagBits : HANDLE_TAG_BITS; //2位标签 代表的是HandleTable的层数
ULONG_PTR Index : HANDLE_INDEX_BITS; //29位
ULONG_PTR KernelFlag : KERNEL_FLAG_BITS; //1位 最高位 代表是否是内核句柄
};
struct
{
ULONG_PTR TagBits2: HANDLE_TAG_BITS;
ULONG_PTR LowIndex: HANDLE_LOW_BITS;
ULONG_PTR MidIndex: HANDLE_HIGH_BITS;
ULONG_PTR HighIndex: HANDLE_HIGH_BITS;
ULONG_PTR KernelFlag2: KERNEL_FLAG_BITS;
};
HANDLE GenericHandleOverlay;
ULONG_PTR Value;
} EXHANDLE, *PEXHANDLE;
可以看到EXHANDLE类型是个Union结构,最低两位是个Tag标签,剩下的29位是Index代表是一个查找的下标,其实也是30位,因为最高位在之前已经被清零了。因为Tag代表的是层数,当最低9位为0的时候,说明只有一层,那么此时索引为0显然是有问题的。
看下ExpLookupHandleTableEntry
ExpLookupHandleTableEntry
ObReferenceObjectByHandle->ExMapHandleToPointer->ExpLookupHandleTableEntry
PHANDLE_TABLE_ENTRY
NTAPI
ExpLookupHandleTableEntry(IN PHANDLE_TABLE HandleTable,
IN EXHANDLE LookupHandle)
{
ULONG i, j, k, TableLevel, NextHandle;
ULONG_PTR TableBase;
PHANDLE_TABLE_ENTRY Entry = NULL;
EXHANDLE Handle = LookupHandle;
PUCHAR Level1, Level2, Level3;
/* Clear the tag bits and check what the next handle is */
Handle.TagBits = 0; //清空层数
NextHandle = HandleTable->NextHandleNeedingPool;
if (Handle.Value >= NextHandle) return NULL; //若Handle的Value落在了下一个HandleTable的区间 说明有问题
/* Get the table code */
TableBase = (ULONG_PTR)HandleTable->TableCode; //获取句柄表对应的下一级表地址(伪地址)
/* Extract the table level and actual table base */
TableLevel = (ULONG)(TableBase & 3); //TableCode不是严格的一个地址 其最低两位代表的是句柄表的层级
TableBase = TableBase - TableLevel; //去掉层级后才是真正的句柄表地址
/* Check what level we're running at */
switch (TableLevel) //根据层级来
{
/* Direct index */
case 0: //为0时表示此时的TableBase指向的是一个一维数组 每一个数组元素都是一个HANDLE_TABLE_ENTRY结构 一个元素8 Bytes大小
/* Use level 1 and just get the entry directlry */
Level1 = (PUCHAR)TableBase; //此时TableBase指向的是最低层的一维数组
Entry = (PVOID)&Level1[Handle.Value *