[Windows] 内核对象

什么是内核对象

Windows 中一切皆为 对象,Linux 中一切皆为 文件。内核对象是 Windows 用来管理资源的一种 数据结构


内核对象分类

  • 访问标记对象
  • 注册表键对象
  • 调试对象
  • 目录对象
  • 符号链接对象
  • 事件对象
  • 文件对象
  • 文件映射对象
  • IO完成端口对象
  • LPC端口对象
  • 作业对象
  • 邮槽对象
  • 进程对象
  • 线程对象
  • 令牌对象
  • 时钟对象
  • 线程池对象
  • 互斥对象
  • 管道对象
  • 信号量对象

内核对象和句柄的关系

内核对象的所有者是操作系统内核,句柄的所有者是进程。在 r3 调用创建内核对象的函数时会返回一个索引该内核对象的句柄。

并不是所有的句柄都指向内核对象,菜单、窗口、鼠标光标等,这些属于用户对象GDI对象,而非内核对象。要判断一个对象是不是内核对象,最简单的方法就是查看创建这个对象的函数,内核对象在创建的时候都要传入一个 SECURITY_ATTRIBUTES 类型的参数(安全描述符)。


内核对象的结构

根据内核对象种类的不同,其结构也不同,但都有一个相同的 _OBJECT_HEADER 结构,这个结构位于 object - 0x30 处,(因为_OBJECT_HEADER 大小为0x30)

nt!_OBJECT_HEADER
   +0x000 PointerCount     : Int8B					// 对象的引用个数
   +0x008 HandleCount      : Int8B					// 指向该对象的句柄数
   +0x008 NextToFree       : Ptr64 Void
   +0x010 Lock             : _EX_PUSH_LOCK			// 针对每个对象的锁, 当修改该对象头部的域或者任何子头的域时, 需要用到该锁
   +0x018 TypeIndex⭐        : UChar				// 指向类型对象的索引,所有的类型对象都存储在 nt!ObTypeIndexTable 表中
   +0x019 TraceFlags       : UChar					// 追踪开启标志, 用来调试引用计数问题
   +0x019 DbgRefTrace      : Pos 0, 1 Bit
   +0x019 DbgTracePermanent : Pos 1, 1 Bit
   +0x01a InfoMask⭐         : UChar				// 可选头掩码, 除了创建者信息子头外, 只要存在, 就位于对象的前面. 该掩码通过 nt!ObpInfoMaskToOffset 表被转换成一个负的偏移;每个可选头都有一个与之关联的 1 字节索引, 将该可选头置于相对其他子头出现的位置
   +0x01b Flags            : UChar					// 对象的特征和对象属性
   +0x01b NewObject        : Pos 0, 1 Bit
   +0x01b KernelObject     : Pos 1, 1 Bit
   +0x01b KernelOnlyAccess : Pos 2, 1 Bit
   +0x01b ExclusiveObject  : Pos 3, 1 Bit
   +0x01b PermanentObject  : Pos 4, 1 Bit
   +0x01b DefaultSecurityQuota : Pos 5, 1 Bit
   +0x01b SingleHandleEntry : Pos 6, 1 Bit
   +0x01b DeletedInline    : Pos 7, 1 Bit
   +0x01c Reserved         : Uint4B
   +0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION
   +0x020 QuotaBlockCharged : Ptr64 Void
   +0x028 SecurityDescriptor : Ptr64 Void			// 决定谁可以使用该对象
   +0x030 Body             : _QUAD					// 对象体的位置


TypeIndex
在系统中,所有的 _OBJECT_TYPE 类型都保存在一个叫 ObTypeIndexTable 的数组中 ,这个数组的地址被导出,TypeIndex 就是这个表中的索引。
[Windows] 内核对象_第1张图片

InfoMask
因为并不是所有的对象都初始化了全部的信息,所以 InfoMask 代表了指定结构是否存在的掩码:

// 从上到下依次为
name									mask				size
_OBJECT_HEADER_EXTENDED_INFO			(0x80)				(0x8)
_OBJECT_HEADER_PADDING_INFO				(0x40)				()
_OBJECT_HEADER_AUDIT_INFO				(0x20)
_OBJECT_HEADER_PROCESS_INFO				(0x10)
_OBJECT_HEADER_QUOTA_INFO				(0x08)
_OBJECT_HEADER_HANDLE_INFO				(0X04)	
_OBJECT_HEADER_NAME_INFO				(0x02)
_OBJECT_HEADER_CREATOR_INFO				(0x01)
...
_OBJECT_HEADER

比如,我的 windbg.exe 的 eprocess = 0xFFFFB105F8625580:
[Windows] 内核对象_第2张图片


那么它的 _OBJECT_HEADER = 0xFFFFB105F8625550:
[Windows] 内核对象_第3张图片


InfoMask = 0x8 说明只初始化了 _OBJECT_HEADER_QUOTA_INFO 结构,查看 ObpInfoMaskToOffset[8] 的值:
[Windows] 内核对象_第4张图片


0x20 !,再看下 _OBJECT_HEADER_QUOTA_INFO 结构:

[Windows] 内核对象_第5张图片


大小刚好为 0x18+0x8 = 0x20! 验证成功!所以 _OBJECT_HEADER_QUOTA_INFO 的地址为 0xFFFFB105F8625550 - 0x20:
[Windows] 内核对象_第6张图片


可选头数据结构:

nt!_OBJECT_HEADER_CREATOR_INFO
   +0x000 TypeList         : _LIST_ENTRY			// 用来挂入所属对象类型中的链表
   +0x010 CreatorUniqueProcess : Ptr64 Void			// 本对象是由哪个进程创建的
   +0x018 CreatorBackTraceIndex : Uint2B
   +0x01a Reserved1        : Uint2B
   +0x01c Reserved2        : Uint4B

nt!_OBJECT_HEADER_NAME_INFO
   +0x000 Directory        : Ptr64 _OBJECT_DIRECTORY		// 对象目录中的父目录
   +0x008 Name             : _UNICODE_STRING				// 相对于 Directory 的路径或者全路径
   +0x018 ReferenceCount   : Int4B	
   +0x01c Reserved         : Uint4B

nt!_OBJECT_HEADER_HANDLE_INFO
   +0x000 HandleCountDataBase : Ptr64 _OBJECT_HANDLE_COUNT_DATABASE		// 句柄信息
   +0x000 SingleEntry      : _OBJECT_HANDLE_COUNT_ENTRY

nt!_OBJECT_HEADER_QUOTA_INFO
   +0x000 PagedPoolCharge  : Uint4B						// 分页负载
   +0x004 NonPagedPoolCharge : Uint4B					// 非分页负载
   +0x008 SecurityDescriptorCharge : Uint4B				// 安全描述符负载
   +0x00c Reserved1        : Uint4B
   +0x010 SecurityDescriptorQuotaBlock : Ptr64 Void
   +0x018 Reserved2        : Uint8B

nt!_OBJECT_HEADER_PROCESS_INFO
   +0x000 ExclusiveProcess : Ptr64 _EPROCESS			// 所属进程
   +0x008 Reserved         : Uint8B

nt!_OBJECT_HEADER_AUDIT_INFO
   +0x000 SecurityDescriptor : Ptr64 Void				// 安全描述符
   +0x008 Reserved         : Uint8B

nt!_OBJECT_HEADER_PADDING_INFO
   +0x000 PaddingAmount    : Uint4B						// 填充总量

nt!_OBJECT_HEADER_EXTENDED_INFO
   +0x000 Footer           : Ptr64 _OBJECT_FOOTER		// 扩展
   +0x008 Reserved         : Uint8B


_OBJECT_TYPE
TypeIndex 为 ObTypeIndexTable 数组的索引,而 ObTypeIndexTable 数组的类型为 _OBJECT_TYPE。

nt!_OBJECT_TYPE
   +0x000 TypeList         : _LIST_ENTRY					// 本类对象的链表,记录所有同类对象
   +0x010 Name             : _UNICODE_STRING				// 类型名
   +0x020 DefaultObject    : Ptr64 Void						// 本类对象默认使用的同步事件对象
   +0x028 Index            : UChar							// 类型的索引,也即表示这是系统中第几个注册的对象类型
   +0x02c TotalNumberOfObjects : Uint4B						// 对象链表中总的对象个数
   +0x030 TotalNumberOfHandles : Uint4B						// 所有同类对象的打开句柄总数
   +0x034 HighWaterNumberOfObjects : Uint4B					// 历史本类对象个数峰值
   +0x038 HighWaterNumberOfHandles : Uint4B					// 历史本类对象的句柄个数峰值
   +0x040 TypeInfo⭐         : _OBJECT_TYPE_INITIALIZER		// 对象类型信息
   +0x0b8 TypeLock         : _EX_PUSH_LOCK					// 对象类型锁
   +0x0c0 Key              : Uint4B							// 内存块标记
   +0x0c8 CallbackList     : _LIST_ENTRY					// 回调函数链表

nt!_OBJECT_TYPE_INITIALIZER
   +0x000 Length           : Uint2B							// 本结构体本身的长度
   +0x002 ObjectTypeFlags  : Uint2B							
   +0x002 CaseInsensitive  : Pos 0, 1 Bit					// 本类对象的对象名是否大小写不敏感
   +0x002 UnnamedObjectsOnly : Pos 1, 1 Bit
   +0x002 UseDefaultObject : Pos 2, 1 Bit					// 是否使用全局默认的同步事件对象
   +0x002 SecurityRequired : Pos 3, 1 Bit					// 本类对象是否需要安全控制
   +0x002 MaintainHandleCount : Pos 4, 1 Bit				// 对象头中是否维护句柄统计信息
   +0x002 MaintainTypeList : Pos 5, 1 Bit					// 是否维护创建者信息
   +0x002 SupportsObjectCallbacks : Pos 6, 1 Bit			// 支持的对象回调
   +0x002 CacheAligned     : Pos 7, 1 Bit					// 隐藏对齐
   +0x003 UseExtendedParameters : Pos 0, 1 Bit				// 启用扩展参数
   +0x003 Reserved         : Pos 1, 7 Bits
   +0x004 ObjectTypeCode   : Uint4B
   +0x008 InvalidAttributes : Uint4B
   +0x00c GenericMapping   : _GENERIC_MAPPING				// 通用映射
   +0x01c ValidAccessMask  : Uint4B							// 本类对象支持的属性集合
   +0x020 RetainAccess     : Uint4B
   +0x024 PoolType         : _POOL_TYPE						// 本类对象位于分页池还是非分页池(一般内核对象都分配在非分页池中)
   +0x028 DefaultPagedPoolCharge : Uint4B					// 对象占用的分页池总体大小
   +0x02c DefaultNonPagedPoolCharge : Uint4B				// 对象占用的非分页池总体大小
   // object hook 就是改变这几个成员指向的函数地址 =======================================
   >>>+0x030 DumpProcedure    : Ptr64     void 				// dump 对象时调用
   >>>+0x038 OpenProcedure    : Ptr64     long 				// 打开对象时调用
   >>>+0x040 CloseProcedure   : Ptr64     void 				// 关闭句柄时调用
   >>>+0x048 DeleteProcedure  : Ptr64     void 				// 销毁对象时调用
   >>>+0x050 ParseProcedure   : Ptr64     long 				// 解析路径时调用(设备、文件、键)
   >>>+0x050 ParseProcedureEx : Ptr64     long 
   >>>+0x058 SecurityProcedure : Ptr64     long 			// 查询、设置对象安全描述符时调用
   >>>+0x060 QueryNameProcedure : Ptr64     long 			// 文件对象提供了自定义的QueryNameString函数
   >>>+0x068 OkayToCloseProcedure : Ptr64     unsigned char // 每次关闭句柄前都会调用这个函数检查可否关闭
   +0x070 WaitObjectFlagMask : Uint4B
   +0x074 WaitObjectFlagOffset : Uint2B
   +0x076 WaitObjectPointerOffset : Uint2B


你可能感兴趣的:(Windows)