一、 摘要
在开发一个 Anti-Rootkit工具中的枚举进程句柄时,发现枚举System Process进程的句柄信息在Windows XP和Windows 7/8的处理细节有一些不一样,遂想刨根问底,一探究竟。在获取句柄对象名和类型名的时候都会使用NtQueryObject函数,而NtQueryObject函数中是通过ObReferenceObjectByHandle根据Handle获得Object的。此时内核句柄的所有秘密都在ObReferenceObjectByHandle中。
二、 相关补充
2.1 ETHREAD或KTHREAD中的EPROCESS域
l ETHREAD中的域ThreadsProcess(PEPROCESS)在Windows XP中有,Windows 7/8中没有。指向当前线程所属的进程,这是在线程初始创建时赋值的。通过此域,可以很方便地从一个线程访问到它所属的进程。WRK中base\ntos\inc\Ps.h中的宏THREAD_TO_PROCESS据此实现:
#define THREAD_TO_PROCESS(Thread) ((Thread)->ThreadsProcess)
l KTHREAD中Process(PKPROCESS)在Windows 7/8中有,Windows XP中没有,但本人也没有找到该域的作用。如有知情人,请告知。但我猜测作用同上。
注:以上两个域在WRK-1.2中全都存在。
l KTHREAD中的ApcState(KAPC_STATE)中的Process(KPROCESS)也可以EPROCESS。PsGetCurrentProcess函数就是据此实现:
#define _PsGetCurrentProcess()\
(CONTAINING_RECORD(\
((KeGetCurrentThread())->ApcState.Process),\
EPROCESS,Pcb))
这个域会根据线程所属的进程动态变化。当当前线程Attach到其他进程中时(KeAttachProcess/KeStackAttachProcess),原线程中的ApcState保存到SavedApcState中,ApcState保存新进程环境中的信息,ApcState.Process就指向新进程的EPROCESS,所以这里获取的才是真正的当前EPROCESS。Detach到原先进程中时(KeDetachProcess/KeUnstackDetachProcess),该过程与前一步相反。这里可参考WRK中相关API的实现。
三、 ObReferencObjectByHandle在不同版本Windows实现差异
3.1 Windows XP x86
虽然WRK是从Windows XP AMD64和Windows Server 2003 SP1抓取出来的,但ObReferenceObjectByHandle的处理结果和32位XP是以一样,所以这里以WRK中的
源码为例,这样更为清晰。
//
// 摘自:wrk-1.2\base\ntos\ob\Obref.c
//
NTSTATUS
ObReferenceObjectByHandle (
__inHANDLE Handle,
__inACCESS_MASK DesiredAccess,
__in_optPOBJECT_TYPE ObjectType,
__inKPROCESSOR_MODE AccessMode,
__outPVOID *Object,
__out_optPOBJECT_HANDLE_INFORMATION HandleInformation
)
{
ACCESS_MASKGrantedAccess;
PHANDLE_TABLE HandleTable;
POBJECT_HEADER ObjectHeader;
PHANDLE_TABLE_ENTRY ObjectTableEntry;
PEPROCESSProcess;
NTSTATUSStatus;
PETHREADThread;
ObpValidateIrql("ObReferenceObjectByHandle");
Thread= PsGetCurrentThread ();
*Object= NULL;
//
// 检查Handle是不是内核句柄
// (即小于0,也就是最高位带KERNEL_HANDLE_MASK标识)。
// 这里有两个句柄要区别处理:
// 当前进程句柄-1(0xFFFFFFFF)和当前线程句柄-2(0xFFFFFFFE)。
//
if((LONG)(ULONG_PTR)Handle < 0) {
if(Handle == NtCurrentProcess()){
if((ObjectType == PsProcessType)
|| (ObjectType == NULL)){
Process = PsGetCurrentProcessByThread(Thread);
GrantedAccess = Process->GrantedAccess;
if ((SeComputeDeniedAccesses(
GrantedAccess, DesiredAccess)== 0) ||
(AccessMode == KernelMode)){
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Process);
if (ARGUMENT_PRESENT(HandleInformation)) {
HandleInformation->GrantedAccess
= GrantedAccess;
HandleInformation->HandleAttributes= 0;
}