热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Windows内核中的对象管理

1Windows内核中的对象管理每个对象都分为对象头和对象体,定义如下:typedefstruct_OBJECT_HEADER{LONGPoint

1  Windows内核中的对象管理

每个对象都分为对象头和对象体,定义如下:

typedef struct _OBJECT_HEADER

{

     LONG PointerCount; //引用计数

     union

     {

          LONG HandleCount; //指向该对象的句柄数

          PVOID NextToFree;  //对象被延迟删除时加入到一条链中

     };

     POBJECT_TYPE Type;      //指向对象的类型对象

     UCHAR NameInfoOffset;   //名称信息的内存偏移

     UCHAR HandleInfoOffset;  //句柄信息的内存偏移

     UCHAR QuotaInfoOffset;   //配额信息的内存偏移

     UCHAR Flags;

     union

     {

          POBJECT_CREATE_INFORMATION ObjectCreateInfo;

          PVOID QuotaBlockCharged;

     };

     PVOID SecurityDescriptor;   //安全描述符

     QUAD Body;               //对象体开始

} OBJECT_HEADER, *POBJECT_HEADER;

每种对象都有一个对应的类型对象(OBJECT_TYPE),定义如下:

#define OBJECT_LOCK_COUNT 4

typedef struct _OBJECT_TYPE {

    ERESOURCE Mutex;

    LIST_ENTRY TypeList;

    UNICODE_STRING Name;            // Copy from object header for convenience

    PVOID DefaultObject;

    ULONG Index;                      //此类型对象在全局数组中的索引

    ULONG TotalNumberOfObjects;

    ULONG TotalNumberOfHandles;

    ULONG HighWaterNumberOfObjects;

    ULONG HighWaterNumberOfHandles;

    OBJECT_TYPE_INITIALIZER TypeInfo;

#ifdef POOL_TAGGING

    ULONG Key;

#endif //POOL_TAGGING

    ERESOURCE ObjectLocks[ OBJECT_LOCK_COUNT ];

} OBJECT_TYPE, *POBJECT_TYPE;

WRK中的全局类型对象变量,如下表:

CmpKeyObjectType

ExWindowStationObjectType

ObpDeviceMapObjectType

DbgkDebugObjectType

IoAdapterObjectType

ObpDirectoryObjectType

ExCallbackObjectType

IoCompletionObjectType

ObpSymbolicLinkObjectType

ExDesktopObjectType

IoControllerObjectType

ObpTypeObjectType

ExEventObjectType

IoDeviceHandlerObjectType

PsJobType

ExEventPairObjectType

IoDriviceObjectType

PsProcessType

ExMutantObjectType

IoDriverObjectType

PsThreadType

ExpKeyedEventObjectType

IoFileObjectType

SeTokenObjectType

ExProfileObjectType

LpcPortObjectType

WmipGuidObjectType

ExSemaphoreObjectType

LpcWaitablePortObjectType

 

ExTimerObjectType

MmSectionObjectType

 

通常在初始化过程调用ObCreateObjectType函数构建这种对象类型,完成相应全局变量的初始化。

NTKERNELAPI

NTSTATUS

ObCreateObjectType(

    __in PUNICODE_STRING TypeName,

    __in POBJECT_TYPE_INITIALIZER ObjectTypeInitializer,

    __in_opt PSECURITY_DESCRIPTOR SecurityDescriptor,

    __out POBJECT_TYPE *ObjectType

);

ObjectTypeInitializer值其类型为指向OBJECT_TYPE_INITIALIZER结构的指针,结构定义如下:

 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;

可以看到在调用ObCreateObjectType创建对象类型,除了数据,还指定了基本的增删改等基本操作。之后内核就可以调用ObCreateObject来创建此种对象了。系统有一个全局变量ObpObjectTypes数组记录了所有已创建的类型。

NTSTATUS

ObCreateObject (

    __in KPROCESSOR_MODE ProbeMode,            // 决定是否要验证参数

    __in POBJECT_TYPE ObjectType,              // 对象类型指针

    __in POBJECT_ATTRIBUTES ObjectAttributes,  // 对象的属性, 最终会转化成ObAllocateObject需要的OBJECT_CREATE_INFORMATION结构

    __in KPROCESSOR_MODE OwnershipMode,        // 内核对象?用户对象? 同上

    __inout_opt PVOID ParseContext,            // 这参数没用

    __in ULONG ObjectBodySize,                 // 对象体大小

    __in ULONG PagedPoolCharge,                // ...

    __in ULONG NonPagedPoolCharge,             // ...

    __out PVOID *Object                        // 接收对象体的指针

)

以进程和线程对象为例:

RtlZeroMemory (&ObjectTypeInitializer, sizeof (ObjectTypeInitializer));

ObjectTypeInitializer.Length = sizeof (ObjectTypeInitializer);

ObjectTypeInitializer.SecurityRequired = TRUE;

ObjectTypeInitializer.PoolType = NonPagedPool;

ObjectTypeInitializer.InvalidAttributes = OBJ_PERMANENT |

                                          OBJ_EXCLUSIVE |

                                          OBJ_OPENIF;

RtlInitUnicodeString (&NameString, L"Process");

ObjectTypeInitializer.DefaultPagedPoolCharge = PSP_PROCESS_PAGED_CHARGE;

ObjectTypeInitializer.DefaultNonPagedPoolCharge = PSP_PROCESS_NONPAGED_CHARGE;

ObjectTypeInitializer.DeleteProcedure = PspProcessDelete;

ObjectTypeInitializer.ValidAccessMask = PROCESS_ALL_ACCESS;

ObjectTypeInitializer.GenericMapping = PspProcessMapping;

 

if (!NT_SUCCESS (ObCreateObjectType (&NameString,

                                     &ObjectTypeInitializer,

                                     (PSECURITY_DESCRIPTOR) NULL,

                                     &PsProcessType))) {

    return FALSE;

}

RtlInitUnicodeString (&NameString, L"Thread");

ObjectTypeInitializer.DefaultPagedPoolCharge = PSP_THREAD_PAGED_CHARGE;

ObjectTypeInitializer.DefaultNonPagedPoolCharge = PSP_THREAD_NONPAGED_CHARGE;

ObjectTypeInitializer.DeleteProcedure = PspThreadDelete;

ObjectTypeInitializer.ValidAccessMask = THREAD_ALL_ACCESS;

ObjectTypeInitializer.GenericMapping = PspThreadMapping;

 

if (!NT_SUCCESS (ObCreateObjectType (&NameString,

                                     &ObjectTypeInitializer,

                                     (PSECURITY_DESCRIPTOR) NULL,

                                     &PsThreadType))) {

    return FALSE;

}

创建进程:

Status = ObCreateObject (PreviousMode,

                         PsProcessType,

                         ObjectAttributes,

                         PreviousMode,

                         NULL,

                         sizeof (EPROCESS),

                         0,

                         0,

                         &Process);

创建线程:

Status = ObCreateObject (PreviousMode,

                        PsThreadType,

                        ObjectAttributes,

                        PreviousMode,

                        NULL,

                        sizeof(ETHREAD),

                        0,

                        0,

                        &Thread);

 

对象管理器使用的对象头中还有两个重要信息:

1.指针计数,记录了内核本身(也包括驱动程序)引用该对象的次数。

2.句柄计数,记录了有多少个句柄引用此对象。这些句柄可能出现在不同进程中。

对象构造由两部分完成

1. 调用ObCreateObject,根据指定的类型对象来完成对象头初始化,并且按指定大小分配对象体内存。

2. 完成对象体的初始化。

Windows允许以名称的方式管理对象。Windows维护一套全局的名称查询机制,ObpDirectoryObjectType类型对象就说实现这一机制的关键。

Windows内部维护了一个对象层次目录(系统全局名字空间),其根目录对象是由全局变量ObRootDirectoryObject来定义。

NTCreateDirectoryObject 可以查看Callback、ArcName、Device、Driver、FileSystem

、KernelObjects 等子目录创建的过程。

ObpLookupDirectoryEntry指定目录查找一个名称。

ObpInsertDirectoryEntry 把一个对象插入到一个目录中。

ObpDeleteDirectoryEntry 删除找到的某一项。

ObpLookupObjectName 从指定的目录或根目录,递归地根据名称来找到一个对象。

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

);

ObpLookupObjectName执行逻辑:

  1. 参数检查
  2. 如果调用者指定了RootDirectoryHandle参数,则利用RootDirectory的Parse方法来解析对象名称,直到解析成功或者不成功,或者指示从头解析。
  3. 如果没有指定RootDirectoryHandle,则系统从全局的根目录ObpRootDirectoryObject开始解析。这种情况下,传递进来的对象名称必须以”\”开始。如果待查找的名称仅仅是“\”,则执行特殊处理。否则,执行下面的逻辑:

  • a. 首先判断名称是否以”\??\”打头,如果是的话,需要拿到当前进程的DeviceMap(设备表),以进一步查询。

  • b. 如果名称正好是”\??”,则直接返回当前进程的DeviceMap 作为结果。

  • c. 调用ObpLookupDirectoryEntry 查询。

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

对象管理器中的对象是执行体对象,位于系统地址空间中,所有进程都可以访问这些对象。用户模式代码是调用系统服务时通过句柄来引用执行体对象。在内核中通过ObReferenceObjectByHandle将一个句柄转换成对应的对象。该函数负责从当前进程环境或内核环境句柄表中获得指定的对象引用。

关于对象的内存结构和生命周期,对象分为对象头对象体,头部结构是OBJECT_HEADER,对象体因对象而异,所以看到很多函数在接受对象作为参数时,类型为PVOID,而对象头和体通过ObpAllocateObject函数可知在同一块内存中。

从对象体转换到对象头,可以通过

#define OBJECT_TO_OBJECT_HEADER( o ) \

    CONTAINING_RECORD( (o), OBJECT_HEADER, Body )

对象是通过引用计数实现管理生命周期,一旦计数为零,则生命周期结束,对象的引用计数来源于2个方面,第一个来源是内核中的指针引用,一旦内核中新增了一个对象的引用,则对象引用计数需要增1,如果不在引用,则减1。第二个来源是进程打开一个对象获取一个句柄,它以后通过此句柄来引用此对象。对象头信息中准确记录有多少个句柄指向该对象,当1句柄不再被使用时,句柄计数减一。两种作用是在函数ObpIncrementHandleCount/ObpDecrementHandleCount中完成的。


推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文总结了Java中日期格式化的常用方法,并给出了示例代码。通过使用SimpleDateFormat类和jstl fmt标签库,可以实现日期的格式化和显示。在页面中添加相应的标签库引用后,可以使用不同的日期格式化样式来显示当前年份和月份。该文提供了详细的代码示例和说明。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
author-avatar
我只当你的千纸鹤
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有