WINDOWS XP ReactOS 4.2 对象类型

系列文章目录

文章目录

  • 系列文章目录
  • 4.2 对象类型
  • OBJECT_TYPE_INITIALIZER
  • ExpInitializeTimerImplementation()
  • ObpInsertEntryDirectory()
  • ObInit()
  • IopCreateObjectTypes()


4.2 对象类型

对象是分类的,因而是有“类型(Type)”的,前面列举了许多常用的Windows 对象类型。但是要列举所有的对象类型则不可能,因为Windows对象类型的集合(对于内核)是开放的,内核函数可以向这个集合中加入新的对象类型。一旦增加了一种对象类型之后,用户程序就可以通过(借用文件对象的)系统调用创建或打开并操作属于此种类型的对象。那么用户程序是否可以增加新的(内核)对象类型呢?Windows并没有提供这样的系统调用,所以从用户空间是无法达到这个目标的。但是用户可以把内核模块即.sys模块动态安装到内核中,使其成为内核的一部分。这样,用户就实际上有了增加新对象类型的自由。
Windows内核为新对象类型的定义提供了一个全局的OBJECT_TYPE_INITIALIZER 数据结构,作为需要填写和递交的“申请单”:

OBJECT_TYPE_INITIALIZER

//
// Object Type Initialize for ObCreateObjectType
//
typedef struct _OBJECT_TYPE_INITIALIZER
{USHORT Length;BOOLEAN UseDefaultObject;BOOLEAN CaseInsensitive;ULONG InvalidAttributes;GENERIC_MAPPING GenericMapping;ULONG ValidAccessMask;BOOLEAN SecurityRequired;BOOLEAN MaintainHandleCount;BOOLEAN MaintainTypeList;POOL_TYPE PoolType;ULONG DefaultPagedPoolCharge;ULONG DefaultNonPagedPoolCharge;OB_DUMP_METHOD DumpProcedure;OB_OPEN_METHOD OpenProcedure;OB_CLOSE_METHOD CloseProcedure;OB_DELETE_METHOD DeleteProcedure;OB_PARSE_METHOD ParseProcedure;OB_SECURITY_METHOD SecurityProcedure;OB_QUERYNAME_METHOD QueryNameProcedure;OB_OKAYTOCLOSE_METHOD OkayToCloseProcedure;
} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;

字段 Length 说明目标数据结构的长度。OpenProcedure、CloseProcedure、DeleteProcedure 等均为函数指针,都是为目标对象类型所定义的操作。其中ParseProcedure提供了解析路径名以找到目标对象的方法。一般对象的ParseProcedure 都是很简单的,因为都是在对象目录中寻找目标对象,但是文件对象的 ParseProcedure 是个特例,文件对象的寻找涉及文件系统,需要转入文件目录中寻找。

要定义即创建一种新的对象类型时,就填写好一个OBJECT_TYPE_INITIALIZER数据结构,然后调用(内核函数)ObCreateObiectTypeO。这个函数根据“申请单”创建起新对象类型的数据结构,然后将其挂入对象目录。对象类型的数据结构是OBJECT_TYPE,其内部有个成分TypeInfo也是OBJECT_TYPE_INITIALIZER数据结构,“申请单”的内容将被复制到OBJECT_TYPE数据结构内部的Typelnfo中。

下面通过一个实例来说明对象类型的创建,这个过程实际上涉及对象、对象类型以及对象目录这三个方面。这里用做实例的仍是Timer 即“定时器”这个对象类型的创建。

ExpInitializeTimerImplementation()


VOID
INIT_FUNCTION
NTAPI
ExpInitializeTimerImplementation(VOID)
{OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;UNICODE_STRING Name;/* Create the Timer Object Type */RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));RtlInitUnicodeString(&Name, L"Timer");//对象类型为Section,L表示UnicodeObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(ETIMER);ObjectTypeInitializer.GenericMapping = ExpTimerMapping;//用于访问控制ObjectTypeInitializer.PoolType = NonPagedPool;//采用不可倒换页面池ObjectTypeInitializer.ValidAccessMask = TIMER_ALL_ACCESS;ObjectTypeInitializer.DeleteProcedure = ExpDeleteTimer;//删除定时器需要调用的函数。ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ExTimerType);/* Initialize the Wait List and Lock */KeInitializeSpinLock(&ExpWakeListLock);InitializeListHead(&ExpWakeList);
}

函数名前面的INIT_FUNCTION表示这个函数仅在初始化时用到,初始化之后就可以回收其所占的空间另作他用:NTAPI则表示这是由内核“导出”的函数,即可以在可安装模块(.sys模块)
中调用的函数。
程序很简单,填写好一个“申请单”,然后就调用ObCreateObjectTypeO。注意“申请单”中的字段 Poolype 设置成NonPagedPool,说明此类对象的数据结构需常驻(物理)内存,其所在页面不可以被换出到倒换文件中。另一个字段 DefaultNonPagedPoolCharge 则表示这种对象类型的数据结构所消耗的不可倒换虚存实际上就是物理内存,这里说是sizeOf(ETIMER),因为这种类型的数据结构就是 ETIMER。
调用 ObCreateObjectType()时的参数ExTimerType 是个全局的OBJECT_TYPE 结构指针,用来返回所创建的数据结构。这个数据结构成为“定时器”这个对象类型的定义,以后凡是要创建定时器对象即类型为ETIMER的对象时,就要以该指针作为参数之一,以便快速找到这个类型的定义。
但是对象类型的创建并不简单,我们来分段阅读ObCreateObjectTypeO的代码:


NTSTATUS
NTAPI
ObCreateObjectType(IN PUNICODE_STRING TypeName,IN POBJECT_TYPE_INITIALIZER ObjectTypeInitializer,IN PVOID Reserved,OUT POBJECT_TYPE *ObjectType)
{POBJECT_TYPE LocalObjectType;..../* Verify parameters */if (!(TypeName) ||....)..../* Setup a lookup context */ObpInitializeDirectoryLookup(&Context);/* Check if we've already created the directory of types */if (ObpTypeDirectoryObject){/* Acquire the directory lock */ObpAcquireDirectoryLockExclusive(ObpTypeDirectoryObject, &Context);/* Do the lookup */if (ObpLookupEntryDirectory(ObpTypeDirectoryObject,TypeName,OBJ_CASE_INSENSITIVE,FALSE,&Context)){/* We have already created it, so fail */ObpCleanupDirectoryLookup(&Context);return STATUS_OBJECT_NAME_COLLISION;}}}

前面讲过,内核中有个对象目录,指针ObpRotDirectoryObject指向对象目录的根。其实内核中还有个“对象类型”目录,这是个单层的目录,除一个目录节点之外,其余的就都是叶节点了。此外,还有个OBJECT_DIRECTORY指针 ObpTypeDirectoryObject,指向对象类型目录的目录节点如果对象类型目录已经建立,就先通过ObpLookupEntryDirectory()在该目录中寻找,如果找到了同名的节点就说明同名的对象类型业已存在,所以失败返回
我们继续往下看。

/* Now make a copy of the object name */ObjectName.Buffer = ExAllocatePoolWithTag(PagedPool,TypeName->MaximumLength,OB_NAME_TAG);if (!ObjectName.Buffer){/* Out of memory, fail */ObpCleanupDirectoryLookup(&Context);return STATUS_INSUFFICIENT_RESOURCES;}/* Set the length and copy the name */ObjectName.MaximumLength = TypeName->MaximumLength;RtlCopyUnicodeString(&ObjectName, TypeName);/* Allocate the Object */Status = ObpAllocateObject(NULL,&ObjectName,ObpTypeObjectType,sizeof(OBJECT_TYPE),KernelMode,(POBJECT_HEADER*)&Header);if (!NT_SUCCESS(Status)){/* Free the name and fail */ObpCleanupDirectoryLookup(&Context);ExFreePool(ObjectName.Buffer);return Status;}/* Setup the flags and name */LocalObjectType = (POBJECT_TYPE)&Header->Body;LocalObjectType->Name = ObjectName;Header->Flags |= OB_FLAG_KERNEL_MODE | OB_FLAG_PERMANENT;/* Clear accounting data */LocalObjectType->TotalNumberOfObjects =LocalObjectType->TotalNumberOfHandles =LocalObjectType->HighWaterNumberOfObjects =LocalObjectType->HighWaterNumberOfHandles = 0;

对于内核而言,新创建的对象类型本身就是个“对象”,只不过是一种名为“Type”的特殊的对象,其数据结构是 OBJECT TYPE。

类型OBJECTTYPE是“元类型”。所有对象类型(的定义)都是作为对象而存在的,它们的类型都是 OBJECT_TYPE。所以这里实际创建的是类型为OBJECT_TYPE,对象名为TypeName 例如“Timer”等的对象,注意这里所说的对象名实际上就是所创建类型的类型名。将Unicode 形式的类型名复制到对象名 ObiectName中之后,就通过 ObpAllocateObiect0为该对象分配数据结构及其对象头。注意这里的第一个参数是NULL,这本来应该是个OBJECT_CREATE_INFORMATION结构指针,但是在创建对象类型时这个指针都是NULL,因为没有关于具体对象属性的信息需要传递。第三个参数为ObpTypeObjectType,这是个全局指针,指向对象类型“Type”的OBJECT_TYPE 数据结构。每一种对象类型都有自己的OBJECTTYPE数据结构,元类型OBJECTTYPE自身也不例外。第四个参数是 size0f(OBJECT TYPE),因为这里要分配的是 OBJECT TYPE 数据结构。最后一个参数则是指针 Header的地址,用来返回所分配的数据结构。ObpAllocateObiectO的代码不难理解,有兴趣或需要的读者可以自己找来阅读。

为所创建的对象类型分配了数据结构之后,下面就是必要的初始化,并将其插入对象类型目录:

 /* Check if this is the first Object Type */if (!ObpTypeObjectType){/* It is, so set this as the type object */ObpTypeObjectType = LocalObjectType;Header->Type = ObpTypeObjectType;/* Set the hard-coded key and object count */LocalObjectType->TotalNumberOfObjects = 1;LocalObjectType->Key = TAG('O', 'b', 'j', 'T');}else{/* Set Tag */Tag[0] = (CHAR)TypeName->Buffer[0];Tag[1] = (CHAR)TypeName->Buffer[1];Tag[2] = (CHAR)TypeName->Buffer[2];Tag[3] = (CHAR)TypeName->Buffer[3];LocalObjectType->Key = *(PULONG)Tag;}/* Set up the type information */LocalObjectType->TypeInfo = *ObjectTypeInitializer;LocalObjectType->TypeInfo.PoolType = ObjectTypeInitializer->PoolType;/* Check if we have to maintain a type list */if (NtGlobalFlag & FLG_MAINTAIN_OBJECT_TYPELIST){/* Enable support */LocalObjectType->TypeInfo.MaintainTypeList = TRUE;}/* Calculate how much space our header'll take up */HeaderSize = sizeof(OBJECT_HEADER) +sizeof(OBJECT_HEADER_NAME_INFO) +(ObjectTypeInitializer->MaintainHandleCount ? sizeof(OBJECT_HEADER_HANDLE_INFO) : 0);/* Check the pool type */if (ObjectTypeInitializer->PoolType == NonPagedPool){/* Update the NonPaged Pool charge */LocalObjectType->TypeInfo.DefaultNonPagedPoolCharge += HeaderSize;}else{/* Update the Paged Pool charge */LocalObjectType->TypeInfo.DefaultPagedPoolCharge += HeaderSize;}/* All objects types need a security procedure */if (!ObjectTypeInitializer->SecurityProcedure){LocalObjectType->TypeInfo.SecurityProcedure = SeDefaultObjectMethod;}

总的来说这一段代码没有多大技术含量,列在这里只是为了保持这部分代码的完整性。注意这里的 HeaderSize是OBJECT _HEADER 的大小加上OBJECT_HEADER_NAME_INFO 的大小,再加上个可选项 OBJECT_HEADER_HANDLE_INFO 的大小。根据所分配缓冲区的来源,这里记下对象头对可倒换页面池或不可倒换页面池的耗用。我们再往下看:

/* Select the Wait Object */if (LocalObjectType->TypeInfo.UseDefaultObject){/* Add the SYNCHRONIZE access mask since it's waitable */LocalObjectType->TypeInfo.ValidAccessMask |= SYNCHRONIZE;/* Use the "Default Object", a simple event */LocalObjectType->DefaultObject = &ObpDefaultObject;}/* The File Object gets an optimized hack so it can be waited on */else if ((TypeName->Length == 8) && !(wcscmp(TypeName->Buffer, L"File"))){/* Wait on the File Object's event directly */LocalObjectType->DefaultObject = (PVOID)FIELD_OFFSET(FILE_OBJECT,Event);}else if ((TypeName->Length == 24) && !(wcscmp(TypeName->Buffer, L"WaitablePort"))){/* Wait on the LPC Port's object directly */LocalObjectType->DefaultObject = (PVOID)FIELD_OFFSET(LPCP_PORT_OBJECT,WaitEvent);}else{/* No default Object */LocalObjectType->DefaultObject = NULL;}/* Initialize Object Type components */ExInitializeResourceLite(&LocalObjectType->Mutex);for (i = 0; i < 4; i++){/* Initialize the object locks */ExInitializeResourceLite(&LocalObjectType->ObjectLocks[i]);}InitializeListHead(&LocalObjectType->TypeList);/* Lock the object type */ObpEnterObjectTypeMutex(LocalObjectType);/* Get creator info and insert it into the type list */CreatorInfo = OBJECT_HEADER_TO_CREATOR_INFO(Header);if (CreatorInfo) InsertTailList(&ObpTypeObjectType->TypeList,&CreatorInfo->TypeList);/* Set the index and the entry into the object type array */LocalObjectType->Index = ObpTypeObjectType->TotalNumberOfObjects;if (LocalObjectType->Index < 32){/* It fits, insert it */ObpObjectTypes[LocalObjectType->Index - 1] = LocalObjectType;}/* Release the object type */ObpLeaveObjectTypeMutex(LocalObjectType);

对某些类型的对象所进行的某些操作常常需要等待,这样的操作可以是同步的也可以是异步的,这就需要在操作的调用者和执行者之间有个同步的手段,这通常是另一个对象,一个专门用于同步即等待的对象。为此,对象类型的OBJECT_TYPE结构中有个指针DefaultObject,指向一个默认的“等待对象”。如果使用时没有特别的安排,用于等待的对象就是作为操作目标的对象所属类型的默认等待对象。后面,读者在系统调用NtWaitForSingleObiect()和NtWaitForMultipleObjectsO)的代码中将会看到对“等待对象”的使用。有些对象类型以全局的默认等待对象ObpDefaultObject作为其默认等待对象,而File对象则以一个专门的Event对象为默认等待对象,这就是FILE_OBJECT数据结构内部作为其结构成分之一的Event对象。同样,WaitablePort 对象也以其数据结构内部的WaitEvent 对象为默认等待对象。最后,还有些对象类型是不存在操作同步的问题的,或者不使用默认的等待对象,这些对象类型的DefaultObject设置成NULL。但是要注意,当对象类型为File或WaitablePort 的时候,DefaultObiect的值并非指针,而是位移,因为默认的等待对象就在File或WaitablePort 对象的数据结构内部。

下面的InsertTailList()将新创建的对象类型挂入 ObpTypeObjectType 队列。如前所述,在对象头部和OBJECTTYPE结构内部都没有用于这个目的的队列头,这样的队列头在OBJECTHEADERCREATOR_INFO结构中。此外,内核中还有个BJECT_TYPE结构指针数组ObpObjectTypes],用来指向系统中的各种对象类型,这个数组的大小只是32,但如果不考虑由可安装内核模块所动态创建的对象类型,光对付内核所固有的对象类型则已经足够了。
我们再往下看:

 /* Check if we're actually creating the directory object itself */if (!(ObpTypeDirectoryObject) ||(ObpInsertEntryDirectory(ObpTypeDirectoryObject, &Context, Header))){/* Check if the type directory exists */if (ObpTypeDirectoryObject){/* Reference it */ObReferenceObject(ObpTypeDirectoryObject);}/* Cleanup the lookup context */ObpCleanupDirectoryLookup(&Context);/* Return the object type and success */*ObjectType = LocalObjectType;return STATUS_SUCCESS;}/* If we got here, then we failed */ObpCleanupDirectoryLookup(&Context);return STATUS_INSUFFICIENT_RESOURCES;

最后通过 ObpInsertEntryDirectory()将新创建的对象类型插入对象类型目录 ObpTypeDirectoryObject。注意这是以 ObpTypeDirectoryObjec非空即类型目录已经存在为条件的;如果类型目录尚未建立,即ObpTypeDirectoryObject 为空,则只是通过参数ObjectType返回新创建的对象类型。如果ObpInsertEntryDirectory()失败,则ObCreateObjectType()失败返回。
如前所述,OBJECT_HEADER数据结构中没有用来将其挂入某个队列的队列头或指针,OBJECT_TYPE结构中虽然有个队列头,但那只是用来将所有的对象类型连接在一起的。那么,代表对象类型的数据结构又怎样被连接到对象目录中呢?我们继续往下看。

ObpInsertEntryDirectory()

[ObCreateObjectType() > ObpInsertEntryDirectory()]BOOLEAN
NTAPI
ObpInsertEntryDirectory(IN POBJECT_DIRECTORY Parent,IN POBP_LOOKUP_CONTEXT Context,IN POBJECT_HEADER ObjectHeader)
{POBJECT_DIRECTORY_ENTRY *AllocatedEntry;POBJECT_DIRECTORY_ENTRY NewEntry;POBJECT_HEADER_NAME_INFO HeaderNameInfo;/* Make sure we have a name */ASSERT(ObjectHeader->NameInfoOffset != 0);/* Validate the context */if ((Context->Object) ||!(Context->DirectoryLocked) ||(Parent != Context->Directory)){/* Invalid context */DPRINT1("OB: ObpInsertEntryDirectory - invalid context %p %ld\n",Context, Context->DirectoryLocked);KEBUGCHECK(0);return FALSE;}/* Allocate a new Directory Entry */NewEntry = ExAllocatePoolWithTag(PagedPool,sizeof(OBJECT_DIRECTORY_ENTRY),OB_DIR_TAG);if (!NewEntry) return FALSE;/* Save the hash */NewEntry->HashValue = Context->HashValue;/* Get the Object Name Information */HeaderNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);/* Get the Allocated entry */AllocatedEntry = &Parent->HashBuckets[Context->HashIndex];/* Set it */NewEntry->ChainLink = *AllocatedEntry;*AllocatedEntry = NewEntry;/* Associate the Object */NewEntry->Object = &ObjectHeader->Body;/* Associate the Directory */HeaderNameInfo->Directory = Parent;return TRUE;
}

如前所述,为了把一个对象挂入某个对象目录,需要为其分配一个OBJECT_DIRECTORYENTRY即“对象目录项”数据结构,让该数据结构中的指针Object指向这个对象(的正身),而真正插入队列的则是对象目录项数据结构。在这里,需要挂入目录的是个对象类型,但是对象类型也是对象,所以也需要为其分配一个对象目录项。另一方面,对象类型目录是个单层的目录,其目录节点就是 ObpTypeDirectoryObject。
下面再通过对象目录的初始化过程 ObnitO)进一步说明对象类型的创建。

ObInit()


BOOLEAN
INIT_FUNCTION
NTAPI
ObInit(VOID)
{....PKPRCB Prcb = KeGetCurrentPrcb();......../* Create kernel handle table */PsGetCurrentProcess()->ObjectTable = ExCreateHandleTable(NULL);ObpKernelHandleTable = PsGetCurrentProcess()->ObjectTable;/* Create the Type Type */RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));RtlInitUnicodeString(&Name, L"Type");ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);ObjectTypeInitializer.ValidAccessMask = OBJECT_TYPE_ALL_ACCESS;ObjectTypeInitializer.UseDefaultObject = TRUE;ObjectTypeInitializer.MaintainTypeList = TRUE;ObjectTypeInitializer.PoolType = NonPagedPool;ObjectTypeInitializer.GenericMapping = ObpTypeMapping;ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(OBJECT_TYPE);ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ObpTypeObjectType);/* Create the Directory Type */RtlInitUnicodeString(&Name, L"Directory");ObjectTypeInitializer.ValidAccessMask = DIRECTORY_ALL_ACCESS;ObjectTypeInitializer.UseDefaultObject = FALSE;ObjectTypeInitializer.MaintainTypeList = FALSE;ObjectTypeInitializer.GenericMapping = ObpDirectoryMapping;ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(OBJECT_DIRECTORY);ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ObDirectoryType);/* Create 'symbolic link' object type */RtlInitUnicodeString(&Name, L"SymbolicLink");ObjectTypeInitializer.DefaultNonPagedPoolCharge =sizeof(OBJECT_SYMBOLIC_LINK);ObjectTypeInitializer.GenericMapping = ObpSymbolicLinkMapping;ObjectTypeInitializer.ValidAccessMask = SYMBOLIC_LINK_ALL_ACCESS;ObjectTypeInitializer.ParseProcedure = ObpParseSymbolicLink;ObjectTypeInitializer.DeleteProcedure = ObpDeleteSymbolicLink;ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ObSymbolicLinkType);/* Phase 0 initialization complete */ObpInitializationPhase++;return TRUE;

函数 Obnit()在系统的初始化阶段被调用两次,第一次是在所谓Phase0阶段,第二次是在Phase1阶段,或者说是“后Phase0”阶段。上面这一段代码是在Phase0中执行的。

系统中首先创建的对象类型是Type,这是所有其他对象类型的模板,是“元类型”。如前所述,ObjectTypeInitializer是个全局的OBJECT_TYPE_INITIALIZER数据结构,是用于创建对象类型的“申请单”。填写好这个数据结构之后就调用 ObCreateObjectType()予以创建。注意这里的最后一个参数是&ObpTypeObjectType,这是因为“Type”是系统中的第一个对象类型,其后在创建其他对象类型的时候都要引用这个指针。在前面我们看到,为创建定时器对象类型而调用ObpAllocateObiect()时的第三个参数就是指针 ObpTypeObjectType。
创建了元类型之后,又创建了Directory和SymbolicLink 两种对象类型。这两种类型是用来构建对象目录的,所以要首先创建。这时候的“申请单”不需要完全从头填写,只要在Type的“申请单”上做一些修改就可以了。

在这三种对象类型中,元类型Type是不对用户空间开放的,所以严格地说不属于我们所说的“对象”类型,而 Directory和 SymbolicLink 则都对用户空间开放,所以是真正意义上的“对象”类型。所以,Phase0完成了创建对象目录的准备。

到系统初始化的Phase1阶段,Obnit()又会被调用,这一次就转到了标签 ObPostPhase0 下面,这一次要创建的是对象目录:

ObPostPhase0:/* Re-initialize lookaside lists */ObInit2();/* Initialize Object Types directory attributes */RtlInitUnicodeString(&Name, L"\\");InitializeObjectAttributes(&ObjectAttributes,&Name,OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,NULL,SePublicDefaultUnrestrictedSd);/* Create the directory */Status = NtCreateDirectoryObject(&Handle,DIRECTORY_ALL_ACCESS,&ObjectAttributes);if (!NT_SUCCESS(Status)) return FALSE;/* Get a handle to it */Status = ObReferenceObjectByHandle(Handle,0,ObDirectoryType,KernelMode,(PVOID*)&ObpRootDirectoryObject,NULL);if (!NT_SUCCESS(Status)) return FALSE;/* Close the extra handle */Status = NtClose(Handle);if (!NT_SUCCESS(Status)) return FALSE;/* Initialize Object Types directory attributes */RtlInitUnicodeString(&Name, L"\\ObjectTypes");InitializeObjectAttributes(&ObjectAttributes,&Name,OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,NULL,NULL);/* Create the directory */Status = NtCreateDirectoryObject(&Handle,DIRECTORY_ALL_ACCESS,&ObjectAttributes);if (!NT_SUCCESS(Status)) return FALSE;/* Get a handle to it */Status = ObReferenceObjectByHandle(Handle,0,ObDirectoryType,KernelMode,(PVOID*)&ObpTypeDirectoryObject,NULL);if (!NT_SUCCESS(Status)) return FALSE;/* Close the extra handle */Status = NtClose(Handle);if (!NT_SUCCESS(Status)) return FALSE;/* Initialize lookup context */ObpInitializeDirectoryLookup(&Context);

这里通过 NtCreateDirectoryObject()创建根目录“\”和子目录“\ObjectTypes”两个目录对象前者就是对象目录 ObpRootDirectoryObject,后者就是对象类型目录ObpTypeDirectoryObject。NtCreateDirectoryObject()是系统调用,这里是在内核中不带系统调用框架直接调用其内核函数。注意在内核中根目录是“\”,代码中之所以说是“\”,是因为在C语言中“\”是个特殊字符。

NtCreateDirectoryObject0)是用来创建各种对象的若千系统调用之一。凡是创建(或打开)对象的系统调用都有个重要的参数ObjectAttributes,指向一个OBJECT_ATTRIBUTES 数据结构。OBJECT_ATTRIBUTES是个不小的数据结构,里面包含着对象(路径)名、访问权限、属性、访问控制等信息。这里先通过 NtCreateDirectoryObject()创建一个类型为OBJECT DIRECTORY、名为“\”的对象作为整个对象目录的根。这个函数通过参数&Handle 返回一个属于当前进程的句柄。在这里,当前进程是系统初始化进程。每个进程都有个句柄表,表中维持句柄与目标对象数据结构之间的对应关系。另一方面,创建一个对象的同时实际上也打开了这个对象,一个句柄就代表着对于目标的一次打开。但是,在实际需要访问根目录节点时的当前进程多半不会是系统初始化进程,所以在内核中使用这个句柄是不方便的,因此内核中总是通过指针ObpRootDirectoryObiect访问根目录节点。这样,这个句柄就是多余的了,所以通过系统调用NtCose()将其关闭。然而,这样又有问题,因为 NClose()会使目标对象数据结构中的“引用(Reference)计数”减1,这样一来就会使引用计数降到0,因为刚创建或打开的对象的引用计数是1。如果一个对象的引用计数降到了0,就说明这个对象的数据结构已经不再使用而应该将其释放,以免无谓地占用资源。为解决这个问题,这里预先通过 ObReferenceObjectByHandle()递增其引用计数。这样,这个引用计数在NtCloseO)以后还是1,正好符合要求。函数 ObReferenceObjectByHandleO)的主要目的是根据 Handle 找到目标对象的数据结构指针并递增其引用计数,其第5个参数就是用来返回目标对象的数据结构指针,这里分别为ObpRootDirectoryObject和ObpTypeDirectoryObject.

以前讲过,以系统调用NtClose()为例,在内核中调用应该用ZwClose0,可是这里的两个系统调用所用的都是其 Nt版本。实际上,直接调用例如NtCose()并无不可,只是此时系统空间堆栈上没有自陷框架,只要不涉及自陷框架就没有问题。另一方面,对于用户开发的.sys模块,则想调用NIClose()也调用不到,因为这些Nt函数是不导出、不对第三方软件开放的,所以只能用 Zw*版本而这里的Obnit()则本身就是内核的一部分,所以不存在这个问题。

明白了目录节点“\”的创建,“\ObjectTypes”的创建也就容易理解了。所不同的是,对象名实际上是路径名“\ObjectTypes”表明节“ObjectTypes”是直接挂在根目录节点的下面。

有了目录节点“\ObjectTypes”即 ObpTypeDirectoryObject 以后,还要把前面 Phase 0时所创建的几个代表着对象类型的对象插入到这个目录中。

    /* Lock it */ObpAcquireDirectoryLockExclusive(ObpTypeDirectoryObject, &Context);/* Loop the object types */ListHead = &ObpTypeObjectType->TypeList;NextEntry = ListHead->Flink;while (ListHead != NextEntry){/* Get the creator info from the list */CreatorInfo = CONTAINING_RECORD(NextEntry,OBJECT_HEADER_CREATOR_INFO,TypeList);/* Recover the header and the name header from the creator info */Header = (POBJECT_HEADER)(CreatorInfo + 1);NameInfo = OBJECT_HEADER_TO_NAME_INFO(Header);/* Make sure we have a name, and aren't inserted yet */if ((NameInfo) && !(NameInfo->Directory)){/* Do the initial lookup to setup the context */if (!ObpLookupEntryDirectory(ObpTypeDirectoryObject,&NameInfo->Name,OBJ_CASE_INSENSITIVE,FALSE,&Context)){/* Insert this object type */ObpInsertEntryDirectory(ObpTypeDirectoryObject,&Context,Header);}}/* Move to the next entry */NextEntry = NextEntry->Flink;}/* Cleanup after lookup */ObpCleanupDirectoryLookup(&Context);/* Initialize DOS Devices Directory and related Symbolic Links */Status = ObpCreateDosDevicesDirectory();if (!NT_SUCCESS(Status)) return FALSE;return TRUE;
}

这样,到Phase1阶段的初始化完成时,系统中已经有了一个对象目录“\”,这个目录中已经有了一个子目录“\ObjectTypes”,这个子目录里面有Type、Directory和SymbolicLink 三个对象,代表着三种基本的对象类型。在这个基础上,各个子系统将创建它们自己的对象类型,而应用进程则会创建起相应的对象。
以 I/0子系统为例,在其初始化的过程中就创建了Adapter、Controller、Device、Driver、loCompletion、File 等对象类型:

IopCreateObjectTypes()


BOOLEAN
INIT_FUNCTION
NTAPI
IopCreateObjectTypes(VOID)
{OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;UNICODE_STRING Name;/* Initialize default settings */RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);ObjectTypeInitializer.PoolType = NonPagedPool;ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;ObjectTypeInitializer.ValidAccessMask = FILE_ALL_ACCESS;ObjectTypeInitializer.UseDefaultObject = TRUE;ObjectTypeInitializer.GenericMapping = IopFileMapping;/* Do the Adapter Type */RtlInitUnicodeString(&Name, L"Adapter");if (!NT_SUCCESS(ObCreateObjectType(&Name,&ObjectTypeInitializer,NULL,&IoAdapterObjectType))) return FALSE;/* Do the Controller Type */RtlInitUnicodeString(&Name, L"Controller");ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(CONTROLLER_OBJECT);if (!NT_SUCCESS(ObCreateObjectType(&Name,&ObjectTypeInitializer,NULL,&IoControllerObjectType))) return FALSE;/* Do the Device Type. FIXME: Needs Delete Routine! */RtlInitUnicodeString(&Name, L"Device");ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(DEVICE_OBJECT);ObjectTypeInitializer.ParseProcedure = IopParseDevice;ObjectTypeInitializer.SecurityProcedure = IopSecurityFile;if (!NT_SUCCESS(ObCreateObjectType(&Name,&ObjectTypeInitializer,NULL,&IoDeviceObjectType))) return FALSE;/* Initialize the Driver object type */RtlInitUnicodeString(&Name, L"Driver");ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(DRIVER_OBJECT);ObjectTypeInitializer.DeleteProcedure = IopDeleteDriver;ObjectTypeInitializer.ParseProcedure = NULL;ObjectTypeInitializer.SecurityProcedure = NULL;if (!NT_SUCCESS(ObCreateObjectType(&Name,&ObjectTypeInitializer,NULL,&IoDriverObjectType))) return FALSE;/* Initialize the I/O Completion object type */RtlInitUnicodeString(&Name, L"IoCompletion");ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KQUEUE);ObjectTypeInitializer.ValidAccessMask = IO_COMPLETION_ALL_ACCESS;ObjectTypeInitializer.InvalidAttributes |= OBJ_PERMANENT;ObjectTypeInitializer.GenericMapping = IopCompletionMapping;ObjectTypeInitializer.DeleteProcedure = IopDeleteIoCompletion;if (!NT_SUCCESS(ObCreateObjectType(&Name,&ObjectTypeInitializer,NULL,&IoCompletionType))) return FALSE;/* Initialize the File object type  */RtlInitUnicodeString(&Name, L"File");//创建对象类型“FileObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(FILE_OBJECT);ObjectTypeInitializer.InvalidAttributes |= OBJ_EXCLUSIVE;ObjectTypeInitializer.MaintainHandleCount = TRUE;ObjectTypeInitializer.ValidAccessMask = FILE_ALL_ACCESS;ObjectTypeInitializer.GenericMapping = IopFileMapping;ObjectTypeInitializer.CloseProcedure = IopCloseFile;ObjectTypeInitializer.DeleteProcedure = IopDeleteFile;ObjectTypeInitializer.SecurityProcedure = IopSecurityFile;ObjectTypeInitializer.QueryNameProcedure = IopQueryNameFile;ObjectTypeInitializer.ParseProcedure = IopParseFile;ObjectTypeInitializer.UseDefaultObject = FALSE;if (!NT_SUCCESS(ObCreateObjectType(&Name,&ObjectTypeInitializer,NULL,&IoFileObjectType))) return FALSE;/* Success */return TRUE;
}

我们已经在前面看过ObCreateObiectType0的代码,注意在创建这些对象类型时指针ObpTypeDirectoryObject 已经非空,所以每次都会调用 ObpInseriEntryDirectory0)。于是,一方面这些OBJECT_TYPE节点都被挂入了ObpTypeObjectType的队列,另一方面又被插入了子目录“\ObjectTypes”。在此以后,对象目录中便有了“\ObjectTypes\Device”、“\ObjectTypes\Driver"“\ObjectTypes\oCompletion"“\ObjectTypes\File”等节点。
这里,在每创建一个对象类型之后,都有个指针指向该对象类型,例如在创建了“Device”之后指针 IoDeviceObjectType 就指向了这个对象类型。此后凡是要创建类型为“Device”的对象时就要引用这个指针。对象头部的字段Type 实际上是OBJECT_TYPE指针,所用的就是这些指针。这样,给定一个对象,通过其头部的 Type 字段就可以获取其所属类型的 OBJECT TYPE 数据结构,从而就可以获取有关这个类型的许多信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/9891.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux第八课-进程间通信】管道、共享内存、消息队列、信号量、信号、可重入函数、volatile

目录 进程间通信为什么&#xff1f;是什么&#xff1f;怎么办&#xff1f;一般规律具体做法 匿名管道原理代码 命名管道原理代码 system V共享内存消息队列信号量信号量的接口 信号概念为什么&#xff1f;怎么办&#xff1f;准备信号的产生信号的保存概念三张表匹配的操作和系统…

串的模式匹配

子串的定位操作通常称为串的模式匹配&#xff0c;它求的是子串(常称模式串)在主串中的位置。 子串——主串的一部分&#xff0c;一定存在 模式串——不一定能在主串中找到 朴素模式匹配 将主串中所有长度为m的子串&#xff08;共有n-m1个&#xff09;依次与模式串对比&…

继承的学习

1.继承 继承权限在类外&#xff0c;访问权限在类内部 1.1继承的概念 继承是面向对象程序设计使代码可以复用的重要手段&#xff08;解决类层次的复用问题&#xff09; 派生类&#xff1a;类特性的基础上进行扩展&#xff0c;增加方法&#xff08;成员函数&#xff09;和属性…

YOLOPv2论文翻译

YOLOPv2: Better, Faster, Stronger for Panoptic Driving Perception 摘要 在过去的十年中&#xff0c;多任务学习方法在解决全景驾驶感知问题方面取得了令人鼓舞的成果&#xff0c;既提供了高精度又具备高效能的性能。在设计用于实时实际自动驾驶系统的网络时&#xff0c;这…

跳表原理-课堂笔记

课程地址 跳表是一种基于随机化的有序数据结构&#xff0c;它提出是为了赋予有序单链表以 O(logn) 的快速查找和插入的能力 创建 首先在头部创建一个 sentinel 节点&#xff0c;然后在 L1 层采用“抛硬币”的方式来决定 L0 层的指针是否增长到 L1 层 例如上图中&#xff0c;L…

贪心day04(买卖股票的最佳时机)

1.买卖股票的最佳时机 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;我们其实只需遍历一篇就可以解决这个问题。首先我们定义一个min为无穷大值&#xff0c;再遍历只要有数字比min跟小我们就更改min的值就好&#xff0c;此时我们只需要找出…

【Python爬虫实战】深入解锁 DrissionPage:ChromiumPage 自动化网页操作指南

&#x1f308;个人主页&#xff1a;易辰君-CSDN博客 &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html ​ 目录 前言 一、ChromiumPage基础操作 &#xff08;一&#xff09;初始化Drission 和 ChromiumPage 对象 &#xff0…

VS Code 插件 MySQL Shell for VS Code

https://marketplace.visualstudio.com/items?itemNameOracle.mysql-shell-for-vs-code

稳压二极管详解

目录 1. 工作原理 2. 稳压二极管的伏安特性曲线 3. 正向特性&#xff1a; 4. 反向特性 5. 稳定电压&#xff08;Vz&#xff09; 6. 动态电阻&#xff08;rz&#xff09; 7.最大耗散功率&#xff08;PzM&#xff09; 8. 最大稳定工作电流&#xff08;IzMAX&#xff09;和…

Springboot 一个西餐主题网站-计算机设计毕业源码73020

目录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 2.2.2 业务流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 系统总体设…

JS渗透(安全)

JS逆向 基本了解 作用域&#xff1a; 相关数据值 调用堆栈&#xff1a; 由下到上就是代码的执行顺序 常见分析调试流程&#xff1a; 1、代码全局搜索 2、文件流程断点 3、代码标签断点 4、XHR提交断点 某通js逆向结合burp插件jsEncrypter 申通快递会员中心-登录 查看登录包…

世界技能竞赛大数据应用开发环境1:1还原

关注我&#xff0c;私信我获得集群环境 集群情况 模块A搭建环境&#xff0c;在容器中搭建大数据平台 Hadoop HA环境 Pc机&#xff0c;安装安装比赛需要软件 模块B中使用idea快速开发完成数据处理 模块E包含了接口数据&#xff0c;使用vs code快速搭建vue数据可视化

【c++丨STL】vector模拟实现

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C、STL 目录 前言 一、vector底层刨析 二、模拟实现 1. 属性、迭代器以及函数声明 2. 功能实现 交换两个容器的内容 构造函数 拷贝构造 赋值重载 析构…

指针的运用

接下来我将会用的话&#xff0c;讲解我对指针运用仅有的印象 1.解引用 int a23; int*p&a; *p666; 而*p666&#xff1b;&#xff0c;便是解引用操作&#xff0c;跟简单地说*p便是解引用&#xff0c;它的意思是&#xff0c;对p中所储存的地址所在位置的内容进行操作&#xf…

三周精通FastAPI:38 针对不同的编程语言来生成客户端

官方文档&#xff1a;https://fastapi.tiangolo.com/zh/advanced/generate-clients/ 生成客户端 因为 FastAPI 是基于OpenAPI规范的&#xff0c;自然您可以使用许多相匹配的工具&#xff0c;包括自动生成API文档 (由 Swagger UI 提供)。 一个不太明显而又特别的优势是&#…

广告联盟有哪些

随着互联网的发展&#xff0c;越来越多的人开始投身于网站建设和运营。对于站长来说&#xff0c;如何在提供优质内容的同时获取收益是一个重要的问题。广告联盟作为一种常见的盈利模式&#xff0c;受到了广大站长的青睐。本文将介绍5个适合国内站长的广告联盟平台&#xff0c;帮…

兵马未动,粮草先行-InnoDB统计数据是如何收集的

我们前面介绍查询成本的时候经常用到一些统计数据&#xff0c;比如通过SHOW TABLE STATUS可以看到关于表的统计数据&#xff0c;通过SHOW INDEX可以看到关于索引的统计数据&#xff0c;那么这些统计数据是怎么来的呢&#xff1f;它们是以什么方式收集的呢&#xff1f;本章将聚焦…

【Promise】JS 异步之宏队列与微队列

文章目录 1 原理图2 说明3 相关面试题3.1 面试题13.2 面试题23.3 面试题33.4 面试题4 1 原理图 2 说明 JS 中用来存储待执行回调函数的队列包含 2 个不同特定的队列&#xff1a;宏队列和微队列。宏队列&#xff1a;用来保存待执行的宏任务(回调)&#xff0c;比如&#xff1a;定…

基础概念理解

一&#xff0c;数据结构分类 连续结构&#xff0c;跳转结构。 二&#xff0c;对变量的理解 在 C 语言中&#xff0c;变量是用于存储数据的抽象符号。变量本质上是一块内存区域的标识符&#xff08;即它代表内存中的某一块区域&#xff09;&#xff0c;用来存储数据&#xff…

C 学习(4)

return 0; 前提&#xff1a;C 语言规定&#xff0c;main()是程序的入口函数&#xff0c;即所有的程序一定要包含一个main()函数。程序总是从这个函数开始执行&#xff0c;如果没有该函数&#xff0c;程序就无法启动。其他函数都是通过它引入程序的。 main()的写法&#xff0c…