对象创建(ObCreateObject)和对象删除(ObDereferenceObject、ObpRemoveObjectRoutine)

原文地址:http://uninformed.org/index.cgi?v=1&a=5&p=5


进程执行的临界时段是指在新的进程对象实例被nt!ObCreateObject创建 和该进程对象被nt!ObInsertObject插入到进程对象类型表之间的时段。在该时段尝试获取进程的句柄是不安全的,比如使用nt!ObOpenObjectByPointer。如果一个应用程序试图在进程对象被nt!ObInsertObject插入对象表之前获取该进程的句柄,则保存在进程对象头部的创建中状态信息会被改写为代表已经过了应该被nt!ObInsertObject处理的初始安全确认阶段。在某些情况下,在nt!ObInsertObject被调用之前改写该状态信息会导致无效的指针引用,当nt!ObInsertObject最终被调用时,这回导致很多用户都熟悉的可恶的蓝屏。

为了更好的理解这个问题,有必要先了解nt!PspCreateProcess创建和初始化会被返回给调用者的进程对象和进程句柄的方式。对象创建的一部分伴随着如下对nt!ObCreateObject的调用:
ObCreateObject(
KeGetPreviousMode(),
PsProcessType,
ObjectAttributes,
KeGetPreviousMode(),
0,
0x258,
0,
0,
&ProcessObject);

如果调用成功,一个给定大小的进程对象会被创建并使用调用者提供的属性初始化。在这种情况下,该对象使用nt!PsProcessType类型创建。参数size由nt!ObCreateObject提供,在这里是0x258,在不同的windows版本是不同的,非公开的EPROCESS结构下的域有出入。正如所有的内核对象都含有的,进程对象头部有OBJECT_HEADER结构,可能存在可选的对象信息。OBJECT_HEADER结构引用如下:
OBJECT_HEADER:
+0x000 PointerCount : Int4B
+0x004 HandleCount : Int4B
+0x004 NextToFree : Ptr32 Void
+0x008 Type : Ptr32 _OBJECT_TYPE
+0x00c NameInfoOffset : UChar
+0x00d HandleInfoOffset : UChar
+0x00e QuotaInfoOffset : UChar
+0x00f Flags : UChar
+0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
+0x010 QuotaBlockCharged : Ptr32 Void
+0x014 SecurityDescriptor : Ptr32 Void
+0x018 Body : _QUAD

当一个对象首次被nt!ObCreateObject返回,Flags的OB_FLAG_CREATE_INFO位域会指出ObjectCreateInfo域是否指向有效数据,置1,则有效。如果该标志被设置,则ObjectCreateInfo指向如下OBJECT_CREATE_INFORMATION结构:
OBJECT_CREATE_INFORMATION:
+0x000 Attributes : Uint4B
+0x004 RootDirectory : Ptr32 Void
+0x008 ParseContext : Ptr32 Void
+0x00c ProbeMode : Char
+0x010 PagedPoolCharge : Uint4B
+0x014 NonPagedPoolCharge : Uint4B
+0x018 SecurityDescriptorCharge : Uint4B
+0x01c SecurityDescriptor : Ptr32 Void
+0x020 SecurityQos : Ptr32 _SECURITY_QUALITY_OF_SERVICE
+0x024 SecurityQualityOfService : _SECURITY_QUALITY_OF_SERVICE

当nt!ObInsertObject最终被调用,OB_FLAG_CREATE_INFO位标记被假定为仍然存在。这种情况会经常发生,除非某些事件导致该位被清零,本章的后面会解释此点。进程执行的流程在nt!ObInsertObject开始处先会检查进程对象头是否存在名字信息,由OBJECT_HEADER的NameInfoOffset表示。无论是否存在名字信息,下一步是检查提供给nt!ObInsertObject的和内核对象联系在一起的对象类型是否需要执行安全性检查。此需求由如下定义的OBJECT_TYPE类型的TypeInfo域表示:

OBJECT_TYPE:
+0x000 Mutex : _ERESOURCE
+0x038 TypeList : _LIST_ENTRY
+0x040 Name : _UNICODE_STRING
+0x048 DefaultObject : Ptr32 Void
+0x04c Index : Uint4B
+0x050 TotalNumberOfObjects : Uint4B
+0x054 TotalNumberOfHandles : Uint4B
+0x058 HighWaterNumberOfObjects : Uint4B
+0x05c HighWaterNumberOfHandles : Uint4B
+0x060 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0ac Key : Uint4B
+0x0b0 ObjectLocks : [4] _ERESOURCE

OBJECT_TYPE_INITIALIZER:
+0x000 Length : Uint2B
+0x002 UseDefaultObject : UChar
+0x003 CaseInsensitive : UChar
+0x004 InvalidAttributes : Uint4B
+0x008 GenericMapping : _GENERIC_MAPPING
+0x018 ValidAccessMask : Uint4B
+0x01c SecurityRequired : UChar
+0x01d MaintainHandleCount : UChar
+0x01e MaintainTypeList : UChar
+0x020 PoolType : _POOL_TYPE
+0x024 DefaultPagedPoolCharge : Uint4B
+0x028 DefaultNonPagedPoolCharge : Uint4B
+0x02c DumpProcedure : Ptr32
+0x030 OpenProcedure : Ptr32
+0x034 CloseProcedure : Ptr32
+0x038 DeleteProcedure : Ptr32
+0x03c ParseProcedure : Ptr32
+0x040 SecurityProcedure : Ptr32
+0x044 QueryNameProcedure : Ptr32
+0x048 OkayToCloseProcedure : Ptr32

该特定的被nt!ObInsertObject检查的布尔域是TypeInfo.SecurityRequired 标志。若该标志被置TRUE,对于nt!PsProcessType类型,nt!ObInsertObject使用传入的第二个参数的访问状态或创建一个零时访问状态使作为第三个传入参数的访问权限标记生效。在此之前ACCESS_STATE结构的SecurityDescriptor域
被设为OBJECT_CREATE_INFORMATION结构的SecurityDescriptor域。此操作被完成时没有任何检查来确保对象头部的OB_FLAG_CREATE_INFO标志仍然存在,这会导致潜在危险,若此标志被清除并且联合结构不再指向创建信息。


就翻译到这里吧。

猜你喜欢

转载自blog.csdn.net/qq1841370452/article/details/81634275