对象管理---4

    xiaoxiao2025-06-07  16

    几个常用的内核函数

    在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 * //获取HandleTableEntry (sizeof(HANDLE_TABLE_ENTRY) / SizeOfHandle(1))]; break; /* Nested index into mid level */ case 1: //为1时代表句柄表有两层 /* Get the second table and index into it */ Level2 = (PUCHAR)TableBase; //此时指向的是中间层 即Level2 i = Handle.Value % SizeOfHandle(LOW_LEVEL_ENTRIES); //i获取的是最低层的下标 即HANDLE_TABLE_ENTRY的一维数组下标 /* Substract this index, and get the next one */ Handle.Value -= i; //去掉低位 j = Handle.Value / //获取level2的相应下标 注意此时的j的单位是PHANDLE_TABLE_ENTRY (SizeOfHandle(LOW_LEVEL_ENTRIES) / sizeof(PHANDLE_TABLE_ENTRY)); /* Now get the next table and get the entry from it */ Level1 = (PUCHAR)*(PHANDLE_TABLE_ENTRY*)&Level2[j]; //获取level1的地址 Entry = (PVOID)&Level1[i * //在level1中获取句柄表项 (sizeof(HANDLE_TABLE_ENTRY) / SizeOfHandle(1))]; break; /* Nested index into high level */ case 2: //此时有三层 /* Start with the 3rd level table */ Level3 = (PUCHAR)TableBase; //此时的TableBse指向的是最顶层 i = Handle.Value % SizeOfHandle(LOW_LEVEL_ENTRIES); //获取level1中的下标 /* Subtract this index and get the index for the next lower table */ Handle.Value -= i; k = Handle.Value / //去掉最低层的i后得到的结果 (SizeOfHandle(LOW_LEVEL_ENTRIES) / sizeof(PHANDLE_TABLE_ENTRY)); /* Get the remaining index in the 2nd level table */ j = k % (MID_LEVEL_ENTRIES * sizeof(PHANDLE_TABLE_ENTRY)); //此时j是level2中的下标 此时乘是因为要乘sizeof(PHANDLE_TABLE_ENTRY)是因为以这个为单位 /* Get the remaining index, which is in the third table */ k -= j; k /= MID_LEVEL_ENTRIES; //此时的k是level3的下标 此时k的单位是PHANDLE_TABLE_ENTRY /* Extract the table level for the handle in each table */ Level2 = (PUCHAR)*(PHANDLE_TABLE_ENTRY*)&Level3[k]; Level1 = (PUCHAR)*(PHANDLE_TABLE_ENTRY*)&Level2[j]; /* Get the handle table entry */ Entry = (PVOID)&Level1[i * (sizeof(HANDLE_TABLE_ENTRY) / SizeOfHandle(1))]; default: /* All done */ break; } /* Return the handle entry */ return Entry; }

    其三级表结构如下

    // // Number of entries in each table level // #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))

    其流程就是根据层数来采取不同策略进行索引。 当然,首先要获取表的地址,表的地址在HandleTable->TableCode中,我们看下HandleTable的结构

    typedef struct _HANDLE_TABLE { #if (NTDDI_VERSION >= NTDDI_WINXP) ULONG_PTR TableCode; //这里的TableCode就是表的地址 #else PHANDLE_TABLE_ENTRY **Table; #endif PEPROCESS QuotaProcess; PVOID UniqueProcessId; #if (NTDDI_VERSION >= NTDDI_WINXP) EX_PUSH_LOCK HandleTableLock[4]; LIST_ENTRY HandleTableList; EX_PUSH_LOCK HandleContentionEvent; #else ERESOURCE HandleLock; LIST_ENTRY HandleTableList; KEVENT HandleContentionEvent; #endif PHANDLE_TRACE_DEBUG_INFO DebugInfo; LONG ExtraInfoPages; #if (NTDDI_VERSION >= NTDDI_LONGHORN) union { ULONG Flags; UCHAR StrictFIFO:1; }; LONG FirstFreeHandle; PHANDLE_TABLE_ENTRY LastFreeHandleEntry; LONG HandleCount; ULONG NextHandleNeedingPool; #else ULONG FirstFree; ULONG LastFree; ULONG NextHandleNeedingPool; LONG HandleCount; union { ULONG Flags; UCHAR StrictFIFO:1; }; #endif } HANDLE_TABLE, *PHANDLE_TABLE;

    当然TableCode并不是直接表示地址的,最低两位代表的是层数level,因为肯定是以4对齐的,所以最低两位就被空了出来

    一个HANDLE_TABLE_ENTRY结构的大小是8字节,而它是以页对齐的,所以一页有512个,所以在ExHandle中需要9bit来表示它在level1中的下标中层数组是一个PHANDLE_TABLE_ENTRY结构,它的一个元素大小是4bit,所以一页可表示1024个元素,所以在ExHandle中需要10bit来表示在level2中的下标最顶层即level3,它并不是以页对齐的,它的大小是32个指针大小,也就是说需要5位来寻址。所以在HIGH_LEVEL_ENTRIES 是12777216,其实也就是24 - 9 - 10 == 5 也就是ExHandle的5bit

    ObReferenceObjectByPointer

    NTSTATUS NTAPI ObReferenceObjectByPointer(IN PVOID Object, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode) { POBJECT_HEADER Header; /* Get the header */ Header = OBJECT_TO_OBJECT_HEADER(Object); //获取对象头 /* * Validate object type if the call is for UserMode. * NOTE: Unless it's a symbolic link (Caz Yokoyama [MSFT]) */ if ((Header->Type != ObjectType) && ((AccessMode != KernelMode) || //若类型不匹配 或者 类型是符号链接类型 则互殴指针失败 (ObjectType == ObSymbolicLinkType))) { /* Invalid type */ return STATUS_OBJECT_TYPE_MISMATCH; } /* Increment the reference count and return success */ InterlockedIncrement(&Header->PointerCount); //引用计数加1 return STATUS_SUCCESS; }

    这个函数的实质就是对相应对象的引用计数加一,也就是对Header头中的PointerCount字段加一,这个字段是记录着对象被引用的次数,当然,前提是要类型相匹配且不能是符号链接对象。

    ObpLookupObjectName—1

    这个函数很长,分段来看。该函数给定一个RootHandle,表示搜索的起点,ObjectName是路径名,InsertName是否存在决定当搜索不到相应的Object的时候是否进行插入。FoundObject是最后若找到该对象后所返回的对象。

    NTSTATUS NTAPI ObpLookupObjectName(IN HANDLE RootHandle OPTIONAL, IN PUNICODE_STRING ObjectName, IN ULONG Attributes, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode, IN OUT PVOID ParseContext, IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL, IN PVOID InsertObject OPTIONAL, IN OUT PACCESS_STATE AccessState, OUT POBP_LOOKUP_CONTEXT LookupContext, OUT PVOID *FoundObject) { PVOID Object; POBJECT_HEADER ObjectHeader; UNICODE_STRING ComponentName, RemainingName; BOOLEAN Reparse = FALSE, SymLink = FALSE; PDEVICE_MAP DeviceMap = NULL; POBJECT_DIRECTORY Directory = NULL, ParentDirectory = NULL, RootDirectory; POBJECT_DIRECTORY ReferencedDirectory = NULL, ReferencedParentDirectory = NULL; KIRQL CalloutIrql; OB_PARSE_METHOD ParseRoutine; NTSTATUS Status; KPROCESSOR_MODE AccessCheckMode; PWCHAR NewName; POBJECT_HEADER_NAME_INFO ObjectNameInfo; ULONG MaxReparse = 30; PAGED_CODE(); OBTRACE(OB_NAMESPACE_DEBUG, "%s - Finding Object: %wZ. Expecting: %p\n", __FUNCTION__, ObjectName, InsertObject); /* Initialize starting state */ ObpInitializeDirectoryLookup(LookupContext); //初始化上下文结构 记录着运行时候的一些参数 *FoundObject = NULL; Status = STATUS_SUCCESS; Object = NULL; /* Check if case-insensitivity is checked */ if (ObpCaseInsensitive) //若开启了大小写敏感 对应的属性中进行勾选 { /* Check if the object type requests this */ if (!(ObjectType) || (ObjectType->TypeInfo.CaseInsensitive)) { /* Add the flag to disable case sensitivity */ Attributes |= OBJ_CASE_INSENSITIVE; } } /* Check if this is a access checks are being forced */ AccessCheckMode = (Attributes & OBJ_FORCE_ACCESS_CHECK) ? //若需要进行访问控制检查 强制转换成用户模式 这样就一定会进行检查 UserMode : AccessMode;

    首先进行一些初始化工作,即是否勾选大小写敏感和是否进行访问控制检查。

    ObpLookupObjectName—2

    if (RootHandle) //当给出目录的时候 { /* We did. Reference it */ Status = ObReferenceObjectByHandle(RootHandle, //首先根据目录句柄获取相应的目录节点对象 0, NULL, AccessMode, (PVOID*)&RootDirectory, NULL); if (!NT_SUCCESS(Status)) return Status; /* Get the header */ ObjectHeader = OBJECT_TO_OBJECT_HEADER(RootDirectory); //注意ObjectHeader是目录节点的Header /* The name cannot start with a separator, unless this is a file */ if ((ObjectName->Buffer) && (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) && //若对象路径名给出的是绝对路径,却不是文件类型 说明出错 (ObjectHeader->Type != IoFileObjectType)) { /* The syntax is bad, so fail this request */ ObDereferenceObject(RootDirectory); return STATUS_OBJECT_PATH_SYNTAX_BAD; //路径名参数不当 } /* Don't parse a Directory */ if (ObjectHeader->Type != ObDirectoryType) //搜索到的这个目录节点对象类型也许不是目录对象(可能是符号链接对象) { /* Make sure the Object Type has a parse routine */ ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure; //则此时函数本身是无法解析的 自然需要对象给出Parse例程 if (!ParseRoutine) //不存在自然无法解析 { /* We can't parse a name if we don't have a parse routine */ ObDereferenceObject(RootDirectory); return STATUS_INVALID_HANDLE; } /* Set default parse count */ MaxReparse = 30; //最大解析次数 /* Now parse */ while (TRUE) { /* Start with the full name */ RemainingName = *ObjectName; //路径名给到RemainingName /* Call the Parse Procedure */ ObpCalloutStart(&CalloutIrql); //提升中断级别 Status = ParseRoutine(RootDirectory, //从而执行Parse函数解析路径 Object是解析后找到并返回的对象 ObjectType, AccessState, AccessCheckMode, Attributes, ObjectName, &RemainingName, ParseContext, SecurityQos, &Object); ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object); /* Check for success or failure, so not reparse */ if ((Status != STATUS_REPARSE) && //此时调用完若不需要重新解析 说明解析完毕 (Status != STATUS_REPARSE_OBJECT)) { /* Check for failure */ if (!NT_SUCCESS(Status)) //失败了自然是未找到对象的 { /* Parse routine might not have cleared this, do it */ Object = NULL; } else if (!Object) //对象为空自然也不行 { /* Modify status to reflect failure inside Ob */ Status = STATUS_OBJECT_NAME_NOT_FOUND; } /* We're done, return the status and object */ *FoundObject = Object; //此时说明成功找到了 ObDereferenceObject(RootDirectory); //可以不需要再引用该节点了 return Status; } else if ((!ObjectName->Length) || //此时说明需要重新解析 并且此时解析完后的路径名为空,说明起点已经更换了 需要从根节点重新解析 (!ObjectName->Buffer) || (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) { /* Reparsed to the root directory, so start over */ ObDereferenceObject(RootDirectory); RootDirectory = ObpRootDirectoryObject; /* Don't use this anymore, since we're starting at root */ RootHandle = NULL; goto ParseFromRoot; } else if (--MaxReparse) //若并无以上情况 则继续解析 { /* Try reparsing again */ continue; } else //此时穷尽了解析次数 自然返回失败 { /* Reparsed too many times */ ObDereferenceObject(RootDirectory); /* Return the object and normalized status */ *FoundObject = Object; if (!Object) Status = STATUS_OBJECT_NAME_NOT_FOUND; return Status; } } } //此时这个节点是目录对象 并且此时路径名为空 说明要找到的就是该目录对象 返回该目录对象 else if (!(ObjectName->Length) || !(ObjectName->Buffer)) { /* Just return the Root Directory if we didn't get a name*/ Status = ObReferenceObjectByPointer(RootDirectory, //递增其引用计数 0, ObjectType, AccessMode); if (NT_SUCCESS(Status)) Object = RootDirectory; //返回该目录对象 /* Remove the first reference we added and return the object */ ObDereferenceObject(RootDirectory); *FoundObject = Object; return Status; } }

    这段对应于ObpLookupObjectName的第二段,主要是当给出起点目录时候的情况,流程如下

    首先判断是否根据起点目录句柄获取起点对象结构,这个起点对象或许不是对象目录结构对于不是对象目录结构而言,它也许是个符号链接对象或是文件对象。找到的这个节点对象需要提供解析函数Parse函数解析,这个解析函数是在创建该对象类型的时候填写到“提交单”上的那堆函数之一。通过设定一个最大解析次数为30次的循环次数进行解析。每次解析完后有三种可能,第一是不需要再次解析,此时有可能是解析失败,或者成功解析完但是未找到目标对象,或是成功解析完,并且找到了目标对象,那么自然是大吉大利,返回该对象到FoundObject即可。第二是需要进行再次解析,且此时的路径名为空,或者从根节点出发,那么此时解除对根据RootHandle找到的RootDirectory的引用,转而引用ObpRootDirectoryObject,并跳到ParseFromRoot处,从根节点开始解析。第三是需要再次解析,但是30次的解析已经用完,此时只能放弃返回。若无上述情况且需要ReParse,那么解析次数减少一次继续循环此时对于RootHandle获得的RootDirectory对象排除了不是目录对象的可能,那么还有一种特例就是寻找的就是目录节点对象本身,此时的ObjectName显然为空,当是这种情况的时候,返回该目录节点对象即可

    ObpLookupObjectName—3

    下面看下第三部分

    else //此时未给出起点目录,即RootHandle { /* We did not get a Root Directory, so use the root */ RootDirectory = ObpRootDirectoryObject; //那么默认从根节点出发 /* It must start with a path separator */ if (!(ObjectName->Length) || //此时对应的ObjectName应该是以"\"为开头的 若不是自然路径名参数无效 !(ObjectName->Buffer) || (ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR)) { /* This name is invalid, so fail */ return STATUS_OBJECT_PATH_SYNTAX_BAD; } /* Check if the name is only the path separator */ if (ObjectName->Length == sizeof(OBJ_NAME_PATH_SEPARATOR)) //若此时未给出起点目录 并且路径名就是“\” 那么所要找的正是该根目录对象 { /* So the caller only wants the root directory; do we have one? */ if (!RootDirectory) //若根目录尚未建立 { /* This must be the first time we're creating it... right? */ if (InsertObject) //此时要插入的节点自然是没法插入的 直接返回 { /* Yes, so return it to ObInsert so that it can create it */ Status = ObReferenceObjectByPointer(InsertObject, //递增其引用计数 0, ObjectType, AccessMode); if (NT_SUCCESS(Status)) *FoundObject = InsertObject; return Status; } else { /* This should never really happen */ ASSERT(FALSE); return STATUS_INVALID_PARAMETER; } } else //若根目录建立 显然就是要获得根目录节点的 { /* We do have the root directory, so just return it */ Status = ObReferenceObjectByPointer(RootDirectory, //所以增加根目录的引用计数 0, ObjectType, AccessMode); if (NT_SUCCESS(Status)) *FoundObject = RootDirectory; //返回根目录 return Status; } }

    此时未给出起始目录,那么就将起始目录设置为从根目录开始,若此时的ObjectName不是以“\”出发的,那么显然就出现错误了。与要查找目录对象本身相似,这里也有一种特殊的情况那就是假如寻找的是根节点本身,那么就要先判断根节点本身是否建立起来,若未建立起来则只能返回InsertObject,建立起来的话说明寻找的正是根结点,那么返回根节点目录对象即可。

    ObpLookupObjectName—4

    else //若不是寻找根目录的 { ParseFromRoot: /* Check if we have a device map */ if (DeviceMap) { /* Dereference it */ //ObfDereferenceDeviceMap(DeviceMap); DeviceMap = NULL; } /* Check if this is a possible DOS name */ if (!((ULONG_PTR)(ObjectName->Buffer) & 7)) //这里判断是否是\??\节点还是\??节点 { /* * This could be one. Does it match the prefix? * Note that as an optimization, the match is done as 64-bit * compare since the prefix is "\??\" which is exactly 8 bytes. * * In the second branch, we test for "\??" which is also valid. * This time, we use a 32-bit compare followed by a Unicode * character compare (16-bit), since the sum is 6 bytes. */ if ((ObjectName->Length >= ObpDosDevicesShortName.Length) && //判断是否是\??\ (*(PULONGLONG)(ObjectName->Buffer) == ObpDosDevicesShortNamePrefix.Alignment.QuadPart)) { /* FIXME! */ } else if ((ObjectName->Length == ObpDosDevicesShortName.Length - //判断是否是\?? sizeof(WCHAR)) && (*(PULONG)(ObjectName->Buffer) == ObpDosDevicesShortNameRoot.Alignment.LowPart) && (*((PWCHAR)(ObjectName->Buffer) + 2) == (WCHAR)(ObpDosDevicesShortNameRoot.Alignment.HighPart))) { /* FIXME! */ } } } }

    这里由于历史原因,对象的绝对路径名有可能是“DOS路径名”,其特点是根节点以 “\??\” 或者是 “\??” 表示。由于他们是宽字符,所以前者是8个字节,后者是6个字节。所以对于前者就相当于与一个ULL大小的数进行比较。后者则分两次,按一个32位的整数和一个16位的整数进行比较。但是这里ReactOS代码还没有实现这两种情况。实际上,用户空间的 DLL的库函数负责将DOS路径转换成全路径。

    ObpLookupObjectName—5

    /* Check if we were reparsing a symbolic link */ if (!SymLink) { /* Allow reparse */ Reparse = TRUE; MaxReparse = 30; } /* Reparse */ while (Reparse) //当需要重新解析的时候 外层循环 { /* Get the name */ RemainingName = *ObjectName; //RemainingName是解析后余下的路径名 /* Disable reparsing again */ Reparse = FALSE; /* Start parse loop */ while (TRUE) //内层循环 { /* Clear object */ Object = NULL; /* Check if the name starts with a path separator */ if ((RemainingName.Length) && //如果RemainingName的开头是路径分割符 跳过 (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) { /* Skip the path separator */ RemainingName.Buffer++; //指向分割符的下一个 也即是该目录的起始位置 RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR); //长度自然也要减少1 } /* Find the next Part Name */ ComponentName = RemainingName; //ComponentName就是本次查找的节点 while (RemainingName.Length) //获取本次的length { /* Break if we found the \ ending */ if (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) break; /* Move on */ RemainingName.Buffer++; RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR); } /* Get its size and make sure it's valid */ ComponentName.Length -= RemainingName.Length; //本次的长度 if (!ComponentName.Length) { /* Invalid size, fail */ Status = STATUS_OBJECT_NAME_INVALID; break; } /* Check if we're in the root */ if (!Directory) Directory = RootDirectory; //Directory是当前目录 被初始化时的值是RootDirectory /* Check if this is a user-mode call that needs to traverse */ if ((AccessCheckMode != KernelMode) && //检查是否拥有穿越目录的权限 !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE)) { /* We shouldn't have referenced a directory yet */ ASSERT(ReferencedDirectory == NULL); /* Reference the directory */ ObReferenceObject(Directory); ReferencedDirectory = Directory; /* Check if we have a parent directory */ if (ParentDirectory) { /* Check for traverse access */ if (!ObpCheckTraverseAccess(ParentDirectory, //调用ObpCheckTraverseAccess检查是否拥有穿越目录的权限 DIRECTORY_TRAVERSE, AccessState, FALSE, AccessCheckMode, &Status)) { /* We don't have it, fail */ break; } } } /* Check if we don't have a remaining name yet */ if (!RemainingName.Length) { /* Check if we don't have a referenced directory yet */ if (!ReferencedDirectory) { /* Reference it */ ObReferenceObject(Directory); ReferencedDirectory = Directory; } /* Check if we are inserting an object */ if (InsertObject) { /* Lock the directory */ ObpAcquireDirectoryLockExclusive(Directory, LookupContext); } } /* Do the lookup */ Object = ObpLookupEntryDirectory(Directory, //查找本次对应的节点 Object为本次节点名对应的对象 &ComponentName, Attributes, InsertObject ? FALSE : TRUE, LookupContext); if (!Object) //若查找不到本次所要查找的节点 { /* We didn't find it... do we still have a path? */ if (RemainingName.Length) //若此时是一个中间节点 那么这个中间节点显然是有问题的 { /* Then tell the caller the path wasn't found */ Status = STATUS_OBJECT_PATH_NOT_FOUND; break; } else if (!InsertObject) //此时已经到最后一个 且并未要求插入 那么退出循环 不再解析 { /* Otherwise, we have a path, but the name isn't valid */ Status = STATUS_OBJECT_NAME_NOT_FOUND; break; } /* Check create access for the object */ if (!ObCheckCreateObjectAccess(Directory, //此时已经是最后一个且表示要插入 那么需要检测是否可以在目录中创建新的节点 ObjectType == ObDirectoryType ? DIRECTORY_CREATE_SUBDIRECTORY : DIRECTORY_CREATE_OBJECT, AccessState, &ComponentName, FALSE, AccessCheckMode, &Status)) { /* We don't have create access, fail */ break; } //若可以 /* Get the object header */ ObjectHeader = OBJECT_TO_OBJECT_HEADER(InsertObject); /* FIXME: Check if this is a Section Object or Sym Link */ /* FIXME: If it is, then check if this isn't session 0 */ /* FIXME: If it isn't, check for SeCreateGlobalPrivilege */ /* FIXME: If privilege isn't there, check for unsecure name */ /* FIXME: If it isn't a known unsecure name, then fail */ /* Create Object Name */ NewName = ExAllocatePoolWithTag(PagedPool, //分配一个空间存储创建的对象名 ComponentName.Length, OB_NAME_TAG); if (!(NewName) || !(ObpInsertEntryDirectory(Directory, //插入到对应目录下 LookupContext, ObjectHeader))) { /* Either couldn't allocate the name, or insert failed */ if (NewName) ExFreePool(NewName); /* Fail due to memory reasons */ Status = STATUS_INSUFFICIENT_RESOURCES; break; } /* Reference newly to be inserted object */ ObReferenceObject(InsertObject); //对该对象的引用计数加1 /* Get the name information */ ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader); /* Reference the directory */ ObReferenceObject(Directory); /* Copy the Name */ RtlCopyMemory(NewName, //将对象名拷贝到NewName中 ComponentName.Buffer, ComponentName.Length); /* Check if we had an old name */ if (ObjectNameInfo->Name.Buffer) //如果ObjectNameInfo里有了对象名 那么释放掉 { /* Free it */ ExFreePool(ObjectNameInfo->Name.Buffer); } /* Write new one */ ObjectNameInfo->Name.Buffer = NewName; //将创建的对象名放上去 设置相关长度 ObjectNameInfo->Name.Length = ComponentName.Length; ObjectNameInfo->Name.MaximumLength = ComponentName.Length; /* Return Status and the Expected Object */ Status = STATUS_SUCCESS; Object = InsertObject; //Object插入上去的对象 /* Get out of here */ break; }

    这里就是逐层查找了。ComponentName是本次查找的中间节点名。该循环首先获取本次搜索的中间节点名,然后检查是否有穿越目录的权限,若是有的话那么就可以调用ObpLookupEntryDirectory查找当前目录下是否有该中间节点,对于找不到该节点,主要有以下情况:

    此时RemainingName的Length还没有穷尽,说明刚刚查找的是一个中间节点,而中间节点查找不到说明有问题,失败退出循环经过上一步的check,现在找的正是最后一个节点,此时最后找的节点不存在,并且InsertObject为空,那么状态码设置成查找不到即可此时InsertObject存在,那么就插入到该目录下,并进行初始化,当然创建的时候需要检查该目录下是否有创建对象的权限

    ObpLookupObjectName—6

    ReparseObject: //如果对于本次查找,我们找到了该中间对象或者目标对象 /* We found it, so now get its header */ ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); /* * Check for a parse Procedure, but don't bother to parse for an insert * unless it's a Symbolic Link, in which case we MUST parse */ ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure; //如果该节点有解析例程 并且 是个解析符号链接的函数 if ((ParseRoutine) && (!(InsertObject) || (ParseRoutine == ObpParseSymbolicLink))) { /* Use the Root Directory next time */ Directory = NULL; /* Increment the pointer count */ InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1); /* Cleanup from the first lookup */ ObpCleanupDirectoryLookup(LookupContext); /* Check if we have a referenced directory */ if (ReferencedDirectory) { /* We do, dereference it */ ObDereferenceObject(ReferencedDirectory); ReferencedDirectory = NULL; } /* Check if we have a referenced parent directory */ if (ReferencedParentDirectory) { /* We do, dereference it */ ObDereferenceObject(ReferencedParentDirectory); ReferencedParentDirectory = NULL; } /* Call the Parse Procedure */ ObpCalloutStart(&CalloutIrql); Status = ParseRoutine(Object, //调用解析函数 ObjectType, AccessState, AccessCheckMode, Attributes, ObjectName, &RemainingName, ParseContext, SecurityQos, &Object); ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object); /* Remove our extra reference */ ObDereferenceObject(&ObjectHeader->Body); /* Check if we have to reparse */ if ((Status == STATUS_REPARSE) || //若需要重新解析整个路径 或者 需要从Object开始继续解析 (Status == STATUS_REPARSE_OBJECT)) { /* Reparse again */ Reparse = TRUE; /* Start over from root if we got sent back there */ if ((Status == STATUS_REPARSE_OBJECT) || //若起点已改变 或者新路径是从根节点出发的 (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) { /* Check if we got a root directory */ if (RootHandle) { /* Stop using it, because we have a new directory now */ ObDereferenceObject(RootDirectory); RootHandle = NULL; } /* Start at Root */ ParentDirectory = NULL; RootDirectory = ObpRootDirectoryObject; /* Check for reparse status */ if (Status == STATUS_REPARSE_OBJECT) //若需要从新的起点再次解析 { /* Don't reparse again */ Reparse = FALSE; /* Did we actually get an object to which to reparse? */ if (!Object) { /* We didn't, so set a failure status */ Status = STATUS_OBJECT_NAME_NOT_FOUND; } else { /* We did, so we're free to parse the new object */ goto ReparseObject; } } else //新路径名是从根路径出发的 所以从Root开始解析 { /* This is a symbolic link */ SymLink = TRUE; goto ParseFromRoot; } } else if (RootDirectory == ObpRootDirectoryObject) //路径名不以"\""开头 但却从根路径出发 显然错误 { /* We got STATUS_REPARSE but are at the Root Directory */ Object = NULL; Status = STATUS_OBJECT_NAME_NOT_FOUND; Reparse = FALSE; } } else if (!NT_SUCCESS(Status)) //若解析失败 则为NULL { /* Total failure */ Object = NULL; } else if (!Object) //若未找到新起点 失败 { /* We didn't reparse but we didn't find the Object Either */ Status = STATUS_OBJECT_NAME_NOT_FOUND; } /* Break out of the loop */ break; } else //若不存在解析函数 { /* No parse routine...do we still have a remaining name? */ if (!RemainingName.Length) //若已经到了最后一个节点 { /* Are we creating an object? */ if (!InsertObject) //此时表示插入 { /* Check if this is a user-mode call that needs to traverse */ if ((AccessCheckMode != KernelMode) && //检查能否进行穿越 !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE)) { /* Check if we can get it */ if (!ObpCheckTraverseAccess(Directory, DIRECTORY_TRAVERSE, AccessState, FALSE, AccessCheckMode, &Status)) { /* We don't have access, fail */ Object = NULL; break; } } /* Reference the Object */ Status = ObReferenceObjectByPointer(Object, 0, ObjectType, AccessMode); if (!NT_SUCCESS(Status)) Object = NULL; } /* And get out of the reparse loop */ break; } else { /* We still have a name; check if this is a directory object */ if (ObjectHeader->Type == ObDirectoryType) { /* Check if we have a referenced parent directory */ if (ReferencedParentDirectory) { /* Dereference it */ ObDereferenceObject(ReferencedParentDirectory); } /* Restart the lookup from this directory */ ReferencedParentDirectory = ReferencedDirectory; ParentDirectory = Directory; Directory = Object; //此时更换成该目录对象 ReferencedDirectory = NULL; } else //若不是目录对象 且不存在解析函数 显然错误 { /* We still have a name, but no parse routine for it */ Status = STATUS_OBJECT_TYPE_MISMATCH; Object = NULL; break; } } } } }

    如果通过ObLookupEntryDirectory找到了目标对象,那么先看看该目标对象是否有解析函数,若存在解析函数则交给对应的解析函数来处理,这个时候可能会返回STATUS_REPARSE或者是STATUS_REPARSE_OBJECT,前者是重新解析路径名,后者是从当前节点开始解析。若不存在解析函数,则判断是否到达了最后一个节点,若到达了最后一个节点,并且不是进行插入,说明已经找到了,此时检查一次是否能进行穿越,若可以 其访问控制就是合法的,那么递增一次引用计数即可,因为已经打开过了。若并没有到达最后一次,判断是否是目录节点,如果是的话更替目录为Object对应的目录,即ComponentName对应的目录。当然,如果既不是目录对象且没有提供解析函数的话,自然该循环是无法进行解析的,只好退出循环了。

    ObpLookupObjectName—7

    下面自然就是扫尾工作了

    /* Check if we failed */ if (!NT_SUCCESS(Status)) //若失败了 清除context { /* Cleanup after lookup */ ObpCleanupDirectoryLookup(LookupContext); } /* Check if we have a device map and dereference it if so */ //if (DeviceMap) ObfDereferenceDeviceMap(DeviceMap); /* Check if we have a referenced directory and dereference it if so */ if (ReferencedDirectory) ObDereferenceObject(ReferencedDirectory); /* Check if we have a referenced parent directory */ if (ReferencedParentDirectory) { /* We do, dereference it */ ObDereferenceObject(ReferencedParentDirectory); } /* Set the found object and check if we got one */ *FoundObject = Object; //返回找到的对象 如果找到的话 if (!Object) { /* Nothing was found. Did we reparse or get success? */ if ((Status == STATUS_REPARSE) || (NT_SUCCESS(Status))) { /* Set correct failure */ Status = STATUS_OBJECT_NAME_NOT_FOUND; } } /* Check if we had a root directory */ if (RootHandle) ObDereferenceObject(RootDirectory); //如果存在目录句柄的话 解除之前对RootDirectory的引用 /* Return status to caller */ OBTRACE(OB_NAMESPACE_DEBUG, "%s - Found Object: %p. Expected: %p\n", __FUNCTION__, *FoundObject, InsertObject); return Status; }

    扫尾工作 就是清除一些空间 解除引用。 总结一下该函数的功能:

    当Object存在,InsertObject为空时,表示找到了该Object 实际上相当于打开该对象,返回该对象的指针当Object不存在时,InsertObject不为空且权限允许时(即拥有在该目录创建对象的权限),此时会在指定的目录下创建该对象当Object不存在时,InsertObject为空时,此时表示未找到该对象 ,自然返回NULL

    ObOpenObjectByName

    NTSTATUS NTAPI ObOpenObjectByName(IN POBJECT_ATTRIBUTES ObjectAttributes, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode, IN PACCESS_STATE PassedAccessState, IN ACCESS_MASK DesiredAccess, IN OUT PVOID ParseContext, OUT PHANDLE Handle) { PVOID Object = NULL; UNICODE_STRING ObjectName; NTSTATUS Status; POBJECT_HEADER ObjectHeader; PGENERIC_MAPPING GenericMapping = NULL; OB_OPEN_REASON OpenReason; POB_TEMP_BUFFER TempBuffer; PAGED_CODE(); /* Assume failure */ *Handle = NULL; /* Check if we didn't get any Object Attributes */ if (!ObjectAttributes) { /* Fail with special status code */ return STATUS_INVALID_PARAMETER; } /* Allocate the temporary buffer */ TempBuffer = ExAllocatePoolWithTag(NonPagedPool, //首先分配一个size 为 OB_TEMP_BUFFER的空间用来存放这个数据结构 sizeof(OB_TEMP_BUFFER), TAG_OB_TEMP_STORAGE); if (!TempBuffer) return STATUS_INSUFFICIENT_RESOURCES; /* Capture all the info */ Status = ObpCaptureObjectAttributes(ObjectAttributes, //分离Attr 到 TempBuffer的ObjectCreateInfo里 AccessMode, TRUE, &TempBuffer->ObjectCreateInfo, &ObjectName); //ObjectName是要查找的对象名 if (!NT_SUCCESS(Status)) { /* Fail */ ExFreePool(TempBuffer); return Status; } /* Check if we didn't get an access state */ if (!PassedAccessState) //如果没有权限 则调用SeCreateAccessState生成 一个 AccessState { /* Try to get the generic mapping if we can */ if (ObjectType) GenericMapping = &ObjectType->TypeInfo.GenericMapping; /* Use our built-in access state */ PassedAccessState = &TempBuffer->LocalAccessState; Status = SeCreateAccessState(&TempBuffer->LocalAccessState, &TempBuffer->AuxData, DesiredAccess, GenericMapping); if (!NT_SUCCESS(Status)) goto Quickie; } /* Get the security descriptor */ if (TempBuffer->ObjectCreateInfo.SecurityDescriptor) { /* Save it in the access state */ PassedAccessState->SecurityDescriptor = TempBuffer->ObjectCreateInfo.SecurityDescriptor; } /* Now do the lookup */ Status = ObpLookupObjectName(TempBuffer->ObjectCreateInfo.RootDirectory, //起始目录 &ObjectName, //对象名 TempBuffer->ObjectCreateInfo.Attributes, //对象属性 ObjectType, AccessMode, ParseContext, TempBuffer->ObjectCreateInfo.SecurityQos, NULL, PassedAccessState, &TempBuffer->LookupContext, &Object); //最后找到的对象 if (!NT_SUCCESS(Status)) { /* Cleanup after lookup */ ObpCleanupDirectoryLookup(&TempBuffer->LookupContext); goto Cleanup; } //若成功找到Object /* Check if this object has create information */ ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); if (ObjectHeader->Flags & OB_FLAG_CREATE_INFO) //若是为了创建对象 { /* Then we are creating a new handle */ OpenReason = ObCreateHandle; //则将OpenReason设置为创建句柄 /* Check if we still have create info */ if (ObjectHeader->ObjectCreateInfo) { /* Free it */ ObpFreeAndReleaseCapturedAttributes(ObjectHeader-> ObjectCreateInfo); ObjectHeader->ObjectCreateInfo = NULL; } } else { /* Otherwise, we are merely opening it */ OpenReason = ObOpenHandle; //否则表示打开 } /* Check if we have invalid object attributes */ if (ObjectHeader->Type->TypeInfo.InvalidAttributes & //若该对象类型中的无效参数与传入的属性有冲突 TempBuffer->ObjectCreateInfo.Attributes) { /* Set failure code */ Status = STATUS_INVALID_PARAMETER; /* Cleanup after lookup */ ObpCleanupDirectoryLookup(&TempBuffer->LookupContext); /* Dereference the object */ ObDereferenceObject(Object); } else //若跟本身的对象类型的Attributes无冲突 { /* Create the actual handle now */ Status = ObpCreateHandle(OpenReason, //则为它创建一个句柄 也即是安装到了句柄表的相应位置 并返回对应的Handle Object, ObjectType, PassedAccessState, 0, TempBuffer->ObjectCreateInfo.Attributes, &TempBuffer->LookupContext, AccessMode, NULL, Handle); if (!NT_SUCCESS(Status)) ObDereferenceObject(Object); } Cleanup: /* Delete the access state */ if (PassedAccessState == &TempBuffer->LocalAccessState) { SeDeleteAccessState(PassedAccessState); } Quickie: /* Release the object attributes and temporary buffer */ ObpReleaseCapturedAttributes(&TempBuffer->ObjectCreateInfo); //释放该空间 if (ObjectName.Buffer) ObpFreeObjectNameBuffer(&ObjectName); //释放对象名所占空间 ExFreePool(TempBuffer); //释放OB_TEMP_BUFFER /* Return status */ OBTRACE(OB_HANDLE_DEBUG, "%s - returning Object %p with PC S: %lx %lx\n", __FUNCTION__, Object, Object ? OBJECT_TO_OBJECT_HEADER(Object)->PointerCount : -1, Status); return Status; }

    首先分配了一个OB_TEMP_BUFFER的空间 OB_TEMP_BUFFER结构如下

    typedef struct _OB_TEMP_BUFFER { ACCESS_STATE LocalAccessState; OBJECT_CREATE_INFORMATION ObjectCreateInfo; //创建信息 即传入的Attributes OBP_LOOKUP_CONTEXT LookupContext; AUX_ACCESS_DATA AuxData; } OB_TEMP_BUFFER, *POB_TEMP_BUFFER;

    OB_TEMP_BUFFER的主要目的是 然后调用ObpCaptureObjectAttributes将对象属性进行分离,大部分到TempBuffer->ObjectCreateInfo里,然后属性ObjectAttributes中的ObjectName也被提取了出来用于之后的操作,根据Name打开对象最终是由ObpLookupObjectName来实现的,最后为该对象创建一个句柄,即调用了ObpCreateHandle将对象安装到句柄表或找到相对应的对象句柄。 这里的打开,是指因打开而创建句柄,也就是OpenReason为OpenHandle这个枚举值,打开和创建其实质都是一样的,是为了创建并获取句柄。

    ObReferenceObjectByName

    通过对象名获取对象指针,对于用户而言,是无法直接操纵对象本身的,所以这个函数显然不是给用户用的,而是内核本身为了操作有名对象而设计方便的。

    NTSTATUS NTAPI ObReferenceObjectByName(IN PUNICODE_STRING ObjectPath, IN ULONG Attributes, IN PACCESS_STATE PassedAccessState, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode, IN OUT PVOID ParseContext, OUT PVOID* ObjectPtr) { PVOID Object = NULL; UNICODE_STRING ObjectName; NTSTATUS Status; OBP_LOOKUP_CONTEXT Context; AUX_DATA AuxData; ACCESS_STATE AccessState; PAGED_CODE(); /* Fail quickly */ if (!ObjectPath) return STATUS_OBJECT_NAME_INVALID; /* Capture the name */ Status = ObpCaptureObjectName(&ObjectName, ObjectPath, AccessMode, TRUE); //获取对象名 if (!NT_SUCCESS(Status)) return Status; /* We also need a valid name after capture */ if (!ObjectName.Length) return STATUS_OBJECT_NAME_INVALID; /* Check if we didn't get an access state */ if (!PassedAccessState) { /* Use our built-in access state */ PassedAccessState = &AccessState; Status = SeCreateAccessState(&AccessState, &AuxData, DesiredAccess, &ObjectType->TypeInfo.GenericMapping); if (!NT_SUCCESS(Status)) goto Quickie; } /* Find the object */ *ObjectPtr = NULL; Status = ObpLookupObjectName(NULL, //依旧调用的是ObpLookupObjectName查找相关的Object &ObjectName, Attributes, ObjectType, AccessMode, ParseContext, NULL, NULL, PassedAccessState, &Context, &Object); /* Cleanup after lookup */ ObpCleanupDirectoryLookup(&Context); /* Check if the lookup succeeded */ if (NT_SUCCESS(Status)) //若成功找到了Object { /* Check if access is allowed */ if (ObpCheckObjectReference(Object, PassedAccessState, FALSE, AccessMode, &Status)) { /* Return the object */ *ObjectPtr = Object; //直接将Object返回即可 } } /* Free the access state */ if (PassedAccessState == &AccessState) { SeDeleteAccessState(PassedAccessState); } Quickie: /* Free the captured name if we had one, and return status */ ObpFreeObjectNameBuffer(&ObjectName); return Status; }

    这里很简单的先将ObjectName用ObpCaptureObjectName()进行提取,然后调用ObpLookupObjectName来获取对应的对象若成功将该Object放到对应参数中得以返回,最后进行扫尾,清除掉分配的pool等空间。

    最新回复(0)