windows内核开发学习笔记四十二:内核对象管理目录及接口

        windows内部维护了一个对象层次目录(即系统全局名字空间),其根目录对象是由全局变量ObpRootDirectoryObject来定义的。在根目录之下,系统内置了一些子目录,通过查询NtCreateDirectoryObject函数可以看到CallBack、ArcName、Device、Driver、FileSystem、KernelObjects、ObjectTypes、GLOBAL??和Security子目录的创建过程,下面的是创建Driver子目录的代码:

//创建driver目录

RtlInitUnicodeString(&nameString,L"\\Driver");

InitializeObjectAttributes(&objectAttributes,

                                      &nameString,

                                      OBJ_PERMANENT|OBJ_KERNEL_HSNDLE,

                                        (HANDLE)NULL,

                                        (PSECURITY_DESCRIPTOR)NULL);

status = NtCreateDirectoryObject(&handle,

                                                        DIRECTORY_ALL_ACCESS,

                                                        &objectAttributes);

if (!NT_SUCCESS(status))  {

        return FALSE;

} else {

        (void)obCloseHandle(handle,kernelMode);

}

对象管理器提供了一些基本的操作用于名字空间中插入、查询和删除目录或目录项:

  • ObpLookupDirectoryEntry:在一个指定目录中查找一个名称。
  • ObpInsertDirectoryEntry:把一个对象插入到一个目录中。
  • ObpDeleteDirectoryEntry:删除一个目录中的对象。

以上三个函数都是直接在一个对象子目录中进行操作,都是基于ObpLookupObjectName这个重要操作函数,可以从指定的目录或根目录,递归地根据名称来找到相应的目标对象。

NTSTATUS ObpLookupObjectName {

        IN HANDLE RootDirectoryHandle,

        IN PUNICODE_STRING ObjectName,

        IN ULONG Attributes,

        IN POBJECT_TYPE ObjectType,

        IN KPROCESSOR_MODE AccessMode,

        IN PVOID ParseContext OPTIONAL,

        IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,

        IN PVOID InsertObject OPTIONAL,

        IN OUT PACCESS_STATE AccessState,

        OUT POBP_LOOKUP_CONTEXT LookupContext,

        OUT PVOID *FoundObject

};

        对象管理的两个接口函数ObOpenObjectByName和ObReferenceObjectByName,正是通过ObpLookupObjectName来完成其打开对象或引用对象的功能的。另外一个接口函数ObInsertObject,它的作用是把一个对象插入到一个进程的句柄表中,它通过ObpLookupObjectName来验证待插入的对象是否在全局名字空间中不存在,当然,对于无名称的对象并不需要做这样的检查。其基本执行逻辑:

  • 参数检查
  • 如果调用者指定了RootDirectoryHandle参数,则利用此RootDirectory的Parse方法来解析对象名称,直到解析成功或者不成功,或者指示从头解析。
  • 如果调用者没有指定了RootDirectoryHandle参数,则系统从全局的根目录ObpRootDirectoryObject开始解析。在这种情况下,传递进来的对象名称必须以”\“开始。如果待查找的名称仅仅是”\“,则执行特殊处理,否则执行下面的逻辑:
    • 首先判断名称是否以”\??\“开头,如果是的话,则需要拿到当前进程的DeviceMap(设备表),以进一步查询。
    • 如果名称正好是”\??\“,则直接返回到当前进程的DeviceMap作为结果。
    • 调用ObpLookupDirectoryEntry函数,层层递进,或者碰到具有Parse方法的对象,由它来解析余下的名称字符串,或者碰到的子目录对象,从而可以在子目录对象中进一步查询下一级名称。

        内核代码在创建对象类型时,可以为新的对象类型指定Parse方法,每一种对象都可以有它自己的名称解析方法,这使得windows的名字系统非常强大,既允许以基本的目录的方法来管理名称的层次结构,它允许特定类型的对象有它自己的命名和解析策略。譬如,文件File对象有它自己的Parse方法,从而可以方便地支持我们所熟悉的文件系统中的目录结构。

        在ObpLookupObjectName的代码逻辑中,可以看到进程的设备表(DeviceMap),而且,在目录对象的数据结构OBJECT_DIRECTORY中也有一个名为DeviceMap的成员,指向一个DEVICE_MAP。它定义了一个DOS设备名字空间,比如驱动器字母(C:、D:)和一些外设(如COM1)。当对象管理器看到一个以”\??\“开头的名称或者”\??“这样的名称,它会利用进程的DeviceMap来获得相应的对象目录,然后进一步解析剩余的名称字符串。

        对象管理器中的对象是执行体对象,它们位于系统地址空间中,因而所有的进程都可以访问这些对象。但是,在进程地址空间中运行的用户模式代码不能用指针的方式来引用这些对象,它们在调用系统服务时只能通过句柄来引用执行体对象。句柄是进程范畴的概念。它一定要在特定的进程环境中才有意义,在内核中,将一个句柄转换成对应的对应的对象,可以通过ObReferenceObjectByHandle函数来完成,该函数负责从当前进程环境或内核环境的句柄中获得指定的对象引用。

        对象的内存结构和生命周期,对象由对象头和对象体两部分,对象头的数据结构是OBJECT_HEADER,而对象体的类型则因对象而异,很多函数在接受一个对象作为参数时其类型为PVOID。对象头和对象体实际实际上是在同一块内存中,在对象头之前可能还有其他一些可选内容,包括QuotaInfo、HandleInfo、NameInfo和CreatorInfo四部分,这些部分是否出现,取决于对象的类型以及创建对象时所提供的参数。基于这样的内存结构,从对象体转换到对象头不难实现:

#define OBJECT_TO_OBJECT_HEADER(o)    \

        CONTAINING_RECORD((o),OBJECT_HEADER,Body)

        而对象的其他信息,如QuotaInfo、HandleInfo、NameInfo,则可以通过OBJECT_HEADER的QuotaInfoOffset、HandleInfoOffset、NameInfoOffset来获得(OBJECT_HEADER_TO_QUOTA_INFO、OBJECT_HEADER_TO_HANDLE_INFO、OBJECT_HEADER_TO_NAME_INFO函数)。而如果要获得对象的CreatorInfo信息,则可以根据这样的内存机构直接往前推算得到。因此,内核代码在任何时候都可以很方便地获得对象的各种信息。

        对象是通过引用计数来管理其生命周期的,一旦引用计数为零,则对象的生命周期结束,它所占用的内存也可以被回收。对象的应用计数来源于两个方面。第一个来源是内核中的指针引用。一旦内核中新增了一个对象的引用,则对象的引用计数需要增一;如果一个对象的引用不再有用,则引用计数减一。这两种作用是在ObReferenceObjectByPointer和ObDereferenceObject函数中完成的。第二个来源是,一个进程打开一个对象并获得一个句柄,它以后通过此句柄来引用此对象。对象头信息中准确地记录了有多少个句柄指向此对象,当一个句柄不再使用时,其句柄计数减一。这两种作用在函数ObpIncrementHandleCount和ObpDecrementHandleCount中完成的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jyl_sh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值