欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[Windows] 内核对象

程序员文章站 2024-02-02 10:00:22
...

什么是内核对象

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] 内核对象

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] 内核对象


那么它的 _OBJECT_HEADER = 0xFFFFB105F8625550:
[Windows] 内核对象


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


0x20 !,再看下 _OBJECT_HEADER_QUOTA_INFO 结构:

[Windows] 内核对象


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


可选头数据结构:

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