windows Object Manager

来源:互联网 发布:大数据时代读书报告 编辑:程序博客网 时间:2024/06/03 16:17

Handles Object Manager Initialization and Shutdown

阶段 0 初始化:
1.初始化obci和obnm,创建信息和对象名称链表
2.创建安全描述符缓存
3.初始化默认对象事件和设备映射锁
4.在EPROCESS AND ETHREAD set GrantedAccess 为高等访问权限
5.创建句柄表,并设置到EPROCESS 的ObjectTable上。
6.创建 Type Diretory SymbolicLink 三种对象类型
阶段1初始化:
1.初始化对象目录,L”\”,L”\KernelObjects”, L”\ObjectTypes”
2.检索ObpTypeObjectType->TypeList,将类型对象加入到 L”\ObjectTypes”中
3.创建DosDeviceDirectory L”\GLOBAL??”
4.创建关于dos device directory 的符号连接

对象的具体管理

创建对象 ObCreateObject

我们先看对象头部,这个是对象管理器来管理的,它自己创建对象头部的相关信息。

typedef struct _OBJECT_HEADER{    LONG PointerCount;    union    {        LONG HandleCount;        volatile PVOID NextToFree;    };    POBJECT_TYPE Type;    UCHAR NameInfoOffset;    UCHAR HandleInfoOffset;    UCHAR QuotaInfoOffset;    UCHAR Flags;    union    {        POBJECT_CREATE_INFORMATION ObjectCreateInfo;        PVOID QuotaBlockCharged;    };    PSECURITY_DESCRIPTOR SecurityDescriptor;    QUAD Body;} OBJECT_HEADER, *POBJECT_HEADER;

一般情况下创建对象就是根据不同的要求设置对象头,对应的申请空间也是不同大小的,要在ObpAllocateObject中计算对象头的大小,然后申请内存供上层函数完成对象的创建。跟着对象头一起创建的还有quotainfo,handleinfo,nameinfo,creatorinfo.然后将这些信息的偏移地址分别设置到header上,然后初始化header数据结构中的其他变量。设置PointCount 和HandleCount,并设置对象类型,以分辨出是何种对象等等。
以创建timer为例,当上层调用对象管理器的api来创建对象时,使用如下参数创建

    /* Create the Object */    Status = ObCreateObject(PreviousMode,                            ExTimerType,                            ObjectAttributes,                            PreviousMode,                            NULL,                            sizeof(ETIMER),                            0,                            0,                            (PVOID*)&Timer);

我们可以在ObCreateObject获知创建模式,创建属性和创建的对象大小。计算出FinaSize的值(即header中额外的管理信息)和objectSize 是个sizeof(ETIMER)。再根据该对象自己的属性,来决定是创建在换页或是非换页的内存上。

    Header = ExAllocatePoolWithTag(PoolType, FinalSize + ObjectSize, Tag);

然后如上申请了某个对象内存空间,获得该对象的一个指针,此时系统中存在了一个内核对象的数据实例,当创建成功后,ObCreateObject最后一个参数获得的是一个header->Body,此时我们知道,对于对象的其他数据是通过对象管理器来管理的,而且提供对外接口,当外部引用这个数据的时候,我们会增加引用计数和句柄计数,修改对象头内部数据。

插入对象ObInsertObject

当我们创建成功后,我们修改自己的对象内容,比如:

        Timer->ApcAssociated = FALSE;        Timer->WakeTimer = FALSE;        Timer->WakeTimerListEntry.Flink = NULL;

设置完对象数据后,我们调用插入对象,这里一般是获得对象操作的句柄,还是以timer为例

        /* Insert the Timer */        Status = ObInsertObject((PVOID)Timer,                                NULL,                                DesiredAccess,                                0,                                NULL,                                &hTimer);

在函数中我们根据对象体Timer来获取对象头header,然后取出对象头中的createinfo,nameinfo, objecttype,objectname.
然后根据对象名在对象管理器中查找对象。

Status = ObpLookupObjectName(ObjectCreateInfo->RootDirectory,                                     ObjectName,                                     ObjectCreateInfo->Attributes,                                     ObjectType,                                     (ObjectHeader->Flags & OB_FLAG_KERNEL_MODE) ?                                     KernelMode : UserMode,                                     ObjectCreateInfo->ParseContext,                                     ObjectCreateInfo->SecurityQos,                                     Object,                                     AccessState,                                     &Context,                                     &InsertObject);

当找到对象后,ObpCreateHandle来创建对象句柄。这种情况一般是向上层提供句柄,供上层使用。

创建对象类型

NTSTATUSNTAPIObCreateObjectType(IN PUNICODE_STRING TypeName,                   IN POBJECT_TYPE_INITIALIZER ObjectTypeInitializer,                   IN PVOID Reserved,                   OUT POBJECT_TYPE *ObjectType);

我们现在对象管理器的类型目录中查找该对象类型,看是否已经存在该对象。如果存在我们返回,如果不存在,那么调用ObpAllocateObject申请对象
并设置header->Type = ObpTypeObjectType,设置成为一个类型对象。

对象目录

在系统启动时初始化对象类型目录,如下创建目录对象,一共创建三个基础目录L”\”,L”\KernelObjects”, L”\ObjectTypes”

NTSTATUSNTAPINtCreateDirectoryObject(OUT PHANDLE DirectoryHandle,                        IN ACCESS_MASK DesiredAccess,                        IN POBJECT_ATTRIBUTES ObjectAttributes) 

如下创建自己的根目录对象

    /* 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;

当我们要查找对象时,就指定在那个目录下查找,这个范围通过ObpRootDirectoryObject来指定。
自己创建的类型对象放置于 L”\ObjectTypes”中,新创建也会检索这个路径下是否已存在。而类型也是一个对象,创建起来没有和正常对象又太大的区别。
这个函数中,我们通过对象属性和访问标志,创建对象目录。内部调用ObCreateObject来创建对象,使用的参数是ObDirectoryType,来表示创建的是个目录对象。
当我们要创建新的对象类型时,要查找类型目录,看这个对象类型是否已存在。

在对象目录中查找对象

PVOIDNTAPIObpLookupEntryDirectory(IN POBJECT_DIRECTORY Directory,                        IN PUNICODE_STRING Name,                        IN ULONG Attributes,                        IN UCHAR SearchShadow,                        IN POBP_LOOKUP_CONTEXT Context)

上述函数用来在类型目录中查找已存在的类型。
这个函数中我们使用的是hash算法来查找的,我们找到对应的目录,然查看目录中是否有我们这个名字的对象,最终还是根据对象名来最终匹配是否是我们建立的对象类型。

向目录中插入对象

BOOLEANNTAPIObpInsertEntryDirectory(IN POBJECT_DIRECTORY Parent,                        IN POBP_LOOKUP_CONTEXT Context,                        IN POBJECT_HEADER ObjectHeader);

对象目录中每一项如下定义,函数中申请_OBJECT_DIRECTORY_ENTRY实例内存,填写内部信息,修改ChainLink链表,将新的对象挂入到制定的目录中。并设置ObjectHeader的内部信息,指向这个目录。

//// Object Directory Structures//typedef struct _OBJECT_DIRECTORY_ENTRY{    struct _OBJECT_DIRECTORY_ENTRY *ChainLink;    PVOID Object;#if (NTDDI_VERSION >= NTDDI_WS03)    ULONG HashValue;#endif} OBJECT_DIRECTORY_ENTRY, *POBJECT_DIRECTORY_ENTRY;

如果我们要删除对象目录时,我们使用ObpDeleteEntryDirectory来删除目录,我们是找到指定的链表,然后断开链表,释放object_directory_entry所指向的空间。

浏览对象

Status = ObpLookupObjectName(ObjectCreateInfo->RootDirectory,                                     ObjectName,                                     ObjectCreateInfo->Attributes,                                     ObjectType,                                     (ObjectHeader->Flags & OB_FLAG_KERNEL_MODE) ?                                     KernelMode : UserMode,                                     ObjectCreateInfo->ParseContext,                                     ObjectCreateInfo->SecurityQos,                                     Object,                                     AccessState,                                     &Context,                                     &InsertObject);it look up a name under only two circumstances:1. when a process creates a names object2. when a process opens a handle to a named object.

句柄的管理

句柄的创建

在进程创建的时候即PspCreateProcess,某一时刻调用ObinitProcess 来初始化进程关于对象处理方面的操作。这里一般是继承句柄表或者是创建进程的句柄表。

根据对象名打开对象

NTSTATUSNTAPIObOpenObjectByName(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)

一般情况下,以thread操作为例,当我们打开某个线程时使用NtOpenThread函数,在这个函数的内部,检查属性判断是否按线程对象名打开线程,如果是,我们开始调用ObOpenObjectByName函数来处理我们这一操作。在ObOpenObjectByName中,开始我们进行参数检测和安全性检查,然后我们使用ObpLookupObjectName在对象根目录中查找该对象,当我们找到对象的时候,得到了该对象的指针,然后根据对象中是否有创建信息,来判断我们是创建句柄释放创建信息还是打开句柄。无论是创建还是打开,我们都使用ObpCreateHandle来获得对象对应的句柄。

NTSTATUSNTAPIObpCreateHandle(IN OB_OPEN_REASON OpenReason,                IN PVOID Object,                IN POBJECT_TYPE Type OPTIONAL,                IN PACCESS_STATE AccessState,                IN ULONG AdditionalReferences,                IN ULONG HandleAttributes,                IN POBP_LOOKUP_CONTEXT Context,                IN KPROCESSOR_MODE AccessMode,                OUT PVOID *ReturnedObject,                OUT PHANDLE ReturnedHandle);

这个函数来处理对象和句柄的关系,并调用ExCreatehandle来实际创建句柄,首先我们获取创建进程的句柄表即PsGetCurrentProcess()->ObjectTable,并更根据参数属性设置句柄项的值,单例句柄数据结构如下,当我们找到了对象后,使用Object(指向对象体)来分析出内核对象头,然后将对象标记到句柄项_HANDLE_TABLE_ENTRY中,这个实例句柄就已经指向一个对象了。然后我们设置其他相关值,设置句柄的访问属性。当然在创建之前使用了ObpIncrementHandleCount来递增了该对象的句柄计数。我们稍后分析这个函数。首先我们分析一下如何创建的句柄。也就是ExCreateHandle函数。

typedef struct _HANDLE_TABLE_ENTRY{    union    {        PVOID Object;        ULONG_PTR ObAttributes;        PHANDLE_TABLE_ENTRY_INFO InfoTable;        ULONG_PTR Value;    };    union    {        ULONG GrantedAccess;        struct        {            USHORT GrantedAccessIndex;            USHORT CreatorBackTraceIndex;        };        LONG NextFreeTableEntry;    };} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;

如下函数,返回一个句柄,而返回值typedef void *HANDLE; 由此可以看到在windows 32 位平台下,HANDLE 只不过是一个32位的指针,函数参数也是极其简单,在内核中也是少见的简单的两个参数的函数,一个是句柄表,这个从指定进程的EPROCESS中给出,另外一个是句柄表项,表示一个单例句柄。后期我们会分析关于进程句柄表的组织结构。

HANDLENTAPIExCreateHandle(IN PHANDLE_TABLE HandleTable,               IN PHANDLE_TABLE_ENTRY HandleTableEntry)

在函数内部,我们根据句柄表使用ExpAllocateHandleTableEntry创建一个句柄表项,将参数中的表项复制给这个新创建的句柄项。

递增句柄引用计数(todo)

当我们创建句柄时,我们需要在对象上递增句柄的引用计数,
NTSTATUS
NTAPI
ObpIncrementHandleCount(IN PVOID Object,
IN PACCESS_STATE AccessState OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
IN ULONG HandleAttributes,
IN PEPROCESS Process,
IN OB_OPEN_REASON OpenReason)

对象的引用与释放

对象的释放

    /* Check whether the object can now be deleted. */    OldCount = InterlockedDecrement(&Header->PointerCount);

首先递减指针计数,然后判断指针计数是否为零,如果是,查看是否当前可以进行apc,如果是则延迟删除对象,如果不是则直接删除对象。

删除对象

VOIDNTAPIObpDeleteObject(IN PVOID Object,                IN BOOLEAN CalledFromWorkerThread);

函数中分别通过对象指针获取文件头,命名信息和创建信息。分别释放创建信息和命名信息,如果此对象又安全描述符回调函数和删除对象的回调函数,则调用之。最后我们释放对象体的空间。

根据指针引用对象

NTSTATUSNTAPIObReferenceObjectByPointer(IN PVOID Object,                           IN ACCESS_MASK DesiredAccess,                           IN POBJECT_TYPE ObjectType,                           IN KPROCESSOR_MODE AccessMode)    /* Get the header */    Header = OBJECT_TO_OBJECT_HEADER(Object);

这里的一个参数是对象指针,实际上指向对象体,我们通过这个指针得到对象头并验证对象类型和访问方式。而且不能是链接对象。然后检查通过后,递增指针计数

    /* Increment the reference count and return success */    InterlockedIncrement(&Header->PointerCount);

根据句柄引用对象 ObReferenceObjectByHandle

我们先检查一下该句柄是不是特殊句柄,如果这个句柄是当前进程句柄,那么使用
CurrentProcess = PsGetCurrentProcess(); *Object = CurrentProcess; 直接获取到了进程对象指针
如果是一个当前线程句柄 CurrentThread = PsGetCurrentThread(); *Object = CurrentThread; 然后增加进程和线程对象的引用计数。
否则如果是内核模式,我们使用内核句柄表,如果是用户模式,我们使用当前进程的句柄表。
然后将句柄在句柄表中查询,得到对应句柄项,然后将从句柄项中提取对象头,返回对象体,并递增对象头中的PointerCount。

0 0
原创粉丝点击