一、  背景:Windows NT 的对象机制

Windows NT系统将各种资源以对象的方式进行组织和管理。虽然Windows NT内核使用C语言和汇编语言编写的,本身并未使用到C++中的面向对象机制。但依然通过抽象化的对象概念来对各类资源进行管理。

对象分为对象头和对象体两部分,对象头又分为标准的对象头和可选头部,后者这里不介绍。标准头部的定义如下:

代码:
typedef struct _OBJECT_HEADER {LONG_PTR PointerCount;union {LONG_PTR HandleCount;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_HEADE

可以看到,标准的对象头的前面部分长度是固定的,长度正好为0x18,紧随其后就是对象体,并且对象体长度不定。
  在_OBJECT_HEADER 结构中,POBJECT_TYPE Type是对象类型的指针。Windows已经预先定义好了基本的对象类型。例如ProcessType。(进程对象),其对象体就是EPROCESS结构体(包含KPROCESS)。内核在PsCreatProcess()中建立新进程时会同时建立一个新的对象。
  下面看一下对象类型相关的OBJECT_TYPE结构体:

代码:
typedef struct _OBJECT_TYPE {ERESOURCE Mutex;LIST_ENTRY TypeList;UNICODE_STRING Name;            // Copy from object header for conveniencePVOID DefaultObject;ULONG Index;ULONG TotalNumberOfObjects;ULONG TotalNumberOfHandles;ULONG HighWaterNumberOfObjects;ULONG HighWaterNumberOfHandles;OBJECT_TYPE_INITIALIZER TypeInfo;
#ifdef POOL_TAGGINGULONG Key;
#endif //POOL_TAGGINGERESOURCE ObjectLocks[ OBJECT_LOCK_COUNT ];
} OBJECT_TYPE, *POBJECT_TYPE;

其中包含的一个特殊结构体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;

可以看到,其中包含了许多OB_XXXX_METHOD类型的成员,它们是一些函数指针。潘爱民老师在《Windows内核原理与实现》一书中说这里定义的是一些对象操作的方法基本方法。莫非打开进程时使用的就是OB_OPEN_METHOD OpenProcedure指定的方法(例程)么?下面就拿Explorer.exe验证一下。
二、实战
先在WinDBG中通过!procress 0 0命令得到Explorer.exe的PEPROCESS:0x8ad84720,这也是该进程的对象体的开始。那么减掉紧挨着的对象头的大小0x18,就可以得到对象头的起始地址:0x8ad84708。之后执行dt_OBJECT_HEADER 0x8ad84708命令观察explorer.exe进程的对象头:

代码:
0: kd> dt_OBJECT_HEADER 0x8ad84708
nt!_OBJECT_HEADER+0x000 PointerCount     : 0n108+0x004 HandleCount      : 0n4+0x004 NextToFree       : 0x00000004 Void+0x008 Type             : 0x8af7cca0 _OBJECT_TYPE+0x00c NameInfoOffset   : 0 ''+0x00d HandleInfoOffset : 0 ''+0x00e QuotaInfoOffset  : 0 ''+0x00f Flags            : 0x20 ' '+0x010 ObjectCreateInfo : 0x8a5a7008 _OBJECT_CREATE_INFORMATION+0x010 QuotaBlockCharged : 0x8a5a7008 Void+0x014 SecurityDescriptor : 0xe16cd8c5 Void+0x018 Body             : _QUAD

直接得到ProcessType的指针:0x8af7cca0,然后观察:

代码:
0: kd> dt_OBJECT_TYPE 0x8af7cca0
nt!_OBJECT_TYPE+0x000 Mutex            : _ERESOURCE+0x038 TypeList         : _LIST_ENTRY [ 0x8af7ccd8 - 0x8af7ccd8 ]+0x040 Name             : _UNICODE_STRING "Process"+0x048 DefaultObject    : (null) +0x04c Index            : 5+0x050 TotalNumberOfObjects : 0x1c+0x054 TotalNumberOfHandles : 0x7d+0x058 HighWaterNumberOfObjects : 0x20+0x05c HighWaterNumberOfHandles : 0x82+0x060 TypeInfo         : _OBJECT_TYPE_INITIALIZER+0x0ac Key              : 0x636f7250+0x0b0 ObjectLocks      : [4] _ERESOURCE

展开TypeInfo:

代码:
0: kd> dt _OBJECT_TYPE_INITIALIZER 0x8af7cd00
nt!_OBJECT_TYPE_INITIALIZER+0x000 Length           : 0x4c+0x002 UseDefaultObject : 0 ''+0x003 CaseInsensitive  : 0 ''+0x004 InvalidAttributes : 0xb0+0x008 GenericMapping   : _GENERIC_MAPPING+0x018 ValidAccessMask  : 0x1f0fff+0x01c SecurityRequired : 0x1 ''+0x01d MaintainHandleCount : 0 ''+0x01e MaintainTypeList : 0 ''+0x020 PoolType         : 0 ( NonPagedPool )+0x024 DefaultPagedPoolCharge : 0x1000+0x028 DefaultNonPagedPoolCharge : 0x2a8+0x02c DumpProcedure    : (null) +0x030 OpenProcedure    : (null) +0x034 CloseProcedure   : (null) +0x038 DeleteProcedure  : 0x80932460     void  nt!PspProcessDelete+0+0x03c ParseProcedure   : (null) +0x040 SecurityProcedure : 0x8091ce64     long  nt!SeDefaultObjectMethod+0+0x044 QueryNameProcedure : (null) +0x048 OkayToCloseProcedure : (null)

显然,这一堆的Procedure中,只有DeleteProcedure和SecurityProcedure为非零值,显然系统不会在内核去call 0x00000000。难道系统对于这些Procedure还有特殊的处理方式?
到这里,似乎得去翻WRK源码查找这些成员的所有引用了。但这似乎工作量不小,在这我们采取比较偷懒的方式:下断点。这里只选取了两个:OpenProcedure和SecurityProcedure。它们一个为NULL一个为非NULL指,并且从命名上来看一个是在打开对象时执行的,一个是在安全检查时执行的。

代码:
0: kd> ba r4 0x8af7cd30
0: kd> ba r4 0x8af7cd403: kd> bl0 e 8af7cd30 r 4 0001 (0001) 1 e 8af7cd40 r 4 0001 (0001)

似乎系统在登录后会调用NtOpenProcess()遍历一遍进程,随后我们静静地等待断点命中……
很快:

代码:
Breakpoint 1 hit
nt!ObGetObjectSecurity+0x22:
80924f0a 7518            jne     nt!ObGetObjectSecurity+0x3c (80924f24)

对应的源码:

代码:
if (ObpCentralizedSecurity(ObjectType))  {*SecurityDescriptor = ObpReferenceSecurityDescriptor( ObjectHeader );*MemoryAllocated = FALSE;return( STATUS_SUCCESS );}

其中ObpCentralizedSecurity(ObjectType)是个宏:

代码:
#define ObpCentralizedSecurity(_ObjectType)                              \((_ObjectType)->TypeInfo.SecurityProcedure == SeDefaultObjectMethod)//
//  Declare a global table of object types.
//

看起来没啥意思,只是做了一下检查就返回成功了。
本来想直接GO掉继续等待,但是接着往下看代码会发现:

代码:
    ////  The security method will return an absolute format//  security descriptor that just happens to be in a self//  contained buffer (not to be confused with a self-relative//  security descriptor).//ObpBeginTypeSpecificCallOut( SaveIrql );Status = (*ObjectType->TypeInfo.SecurityProcedure)( Object,QuerySecurityDescriptor,&SecurityInformation,*SecurityDescriptor,&Length,&ObjectHeader->SecurityDescriptor,ObjectType->TypeInfo.PoolType,&ObjectType->TypeInfo.GenericMapping );ObpEndTypeSpecificCallOut( SaveIrql, "Security", ObjectType, Object );

可以看到,在ObjectType->TypeInfo.SecurityProcedure!= SeDefaultObjectMethod时,内核最终会去调用SecurityProcedure所指定的例程。同时也可以看到,内核对SecurityProcedure指定的地址未作任何检查,所以假如我们要修改它的话,必须保证其有效性。
  那么这个NTSTATUS ObGetObjectSecurity ()会在什么时候被调用呢?莫非翻翻NtDesignedBook看看么?还是查看一下调用层次吧:

代码:
ObCheckCreateObjectAccess();- ObpLookupObjectName();- ObInsertObject();- ObOpenObjectByName();-ObReferenceObjectByName();
ObCheckObjectAccess();
ObInsertObject();
ObpCheckObjectReference();
ObpCheckTraverseAccess();
PsCreateProcess()
PsCreateThread();
PspSetPrimaryToken();

其中仅对ObCheckCreateObjectAccess()深入追踪,在扒开几层外壳之后我们可以看到一个熟悉的函数:ObOpenObjectByName()。调用它的函数实在太多,这里就不列了,NtOpenProcess、NtOpenThread均在其中。此处可谓牵一发而动全身。
  继续追踪,GO之后断点0命中:

代码:
Breakpoint 0 hit
nt!ObpIncrementHandleCount+0x2b9:
80921d3f 85c0            test    eax,eax
对应的代码:
if (ObjectType->TypeInfo.OpenProcedure != NULL) {#if DBGKIRQL SaveIrql;
#endif////  Leave the object type mutex when call the OpenProcedure. If an exception//  while OpenProcedure the HoldObjectTypeMutex disable leaving the mutex//  on finally block//ObpBeginTypeSpecificCallOut( SaveIrql );Status = (*ObjectType->TypeInfo.OpenProcedure)( OpenReason,Process,Object,AccessState ?AccessState->PreviouslyGrantedAccess :0,ProcessHandleCount );ObpEndTypeSpecificCallOut( SaveIrql, "Open", ObjectType, Object );

这里很简洁明了,检查一下ObjectType->TypeInfo.OpenProcedure不是NULL就直接去调用OpenProcedure了(这样还是不够严格,好歹检查一下是否在内核地址范围内吧)。ObpIncrementHandleCount()看上去是增加句柄计数的,还是查看下调用层次看看最终谁会用到它:

代码:
ObDupHandleProcedure();
ObDuplicateObject();
ObpCreateHandle();- ObInsertObject();- ObOpenObjectByName();-ObOpenObjectByPointer();

这里也仅列出对ObpCreateHandle()深入追踪的结果,可以看到两个熟悉的函数:ObOpenObjectByName()、ObOpenObjectByPointer(),其意义和前面的ObCheckCreateObjectAccess()完全是一个水平,这里就不再重复了。

三、总结
小小的OBJECT_METHOD暗藏玄机,通过分析我们可以发现:这些OBJECT_METHOD虽然卡上去是“可选”的,甚至某些情况下默认值为NULL,,但这并不意味着它们不重要:一旦它们被设置为特殊的函数指针,其被调用的机会相当高,可以用来HOOK的事情也非常多。本文只是初窥,至于具体的应用,有兴趣的童鞋可以找出所有可能调用的地方,然后加以利用。

OBJECT_METHOD初窥相关推荐

  1. 并发编程-01并发初窥

    文章目录 引言 思维导图 基础知识构建 涉及的知识点一览 高并发处理思路与手段一览 并发初窥 概念 并发问题模拟 代码 引言 说来惭愧,一直没有系统的梳理过并发编程的知识,这次借着学习_Jimin_老 ...

  2. java aspectj_初窥AspectJ

    AspectJ可以说是Java中当之无愧的黑魔法.说它是黑魔法,一方面是因为它很强大,能够解决一些传统编程方法论解决不了的问题,而另一方面,它也相当的晦涩,有着比较陡峭的学习曲线. 本文将带大家探索下 ...

  3. Scrapy 1.4 文档 01 初窥 Scrapy

    初窥 Scrapy Scrapy 是用于抓取网站并提取结构化数据的应用程序框架,其应用非常广泛,如数据挖掘,信息处理或历史存档. 尽管 Scrapy 最初设计用于网络数据采集(web scraping ...

  4. 初窥R(基本说明、获取帮助、工作空间、输入输出、包)

    初窥R(基本说明.获取帮助.工作空间.输入输出.包) 本篇简要介绍使用R的一些基本概念,包括基本说明.获取帮助.工作空间.输入输出,每个知识点中都会通过一个例子来练习. 一.R基本情况说明 1.R是一 ...

  5. Spring Cloud Bus之RabbitMQ初窥

    和Spring Cloud Config一样,我们接下来要聊的Spring Cloud Bus也是微服务架构系统中的必备组件.Spring Cloud Bus可以将分布式系统的节点与轻量级消息代理链接 ...

  6. flutter 返回指定界面_Flutter 即学即用系列博客——04 Flutter UI 初窥

    前面三篇可以算是一个小小的里程碑. 主要是介绍了 Flutter 环境的搭建.如何创建 Flutter 项目以及如何在旧有 Android 项目引入 Flutter. 这一篇我们来学习下 Flutte ...

  7. 【重点】初窥Linux 之 我最常用的20多条命令

    [重点]初窥Linux 之 我最常用的20多条命令 玩过Linux的人都会知道,Linux中的命令的确是非常多,但是玩过Linux的人也从来不会因为Linux的命令如此之多而烦恼,因为我们只需要掌握我 ...

  8. 初窥Xcode4 -- Xcode4主题样式、快捷键等常用设置

    今天晚上开始使用Xcode4,初窥历程如下. 一.下载安装Xcode4 因为我需要让Xcode4和老版本共存,在安装Xcode4时选择其他安装路径,安装成功后在xcode4安装目录下改名Xcode.a ...

  9. [基础] AHK函数对象初窥 ① _实例2_实际可用版

    [基础] AHK函数对象初窥 ① _实例2_实际可用版 ;# 母文章 https://zhuanlan.zhihu.com/p/48977298;;;;;;;;;;;;;;;;;;;;;;;;;;;; ...

最新文章

  1. leetcode_894. All Possible Full Binary Trees
  2. RTX 30系游戏本来了!1月26日开售,售价999美元起
  3. 2018年企业运维开发经典面试题
  4. IDA pro 使用笔记
  5. CentOS 6.5 搭建NFS文件服务器
  6. C#部分面试题及答案
  7. linux如何创建备份文件,如何备份Linux 配置文件
  8. pythonutf8转gbk,Python实现把utf-8格式的文件转换成gbk格式的文件
  9. 绝不要在构造函数和析构过程中调用virtual函数
  10. workbench设置单元坐标系_ANSYS经典案例在Workbench中实现分享连载(三)
  11. JavaWeb——mybatis一对一、一对多查询
  12. jQuery-1.9.1源码分析系列(十) 事件系统——事件体系结构
  13. SEC0000007-Unable to locate security server?
  14. Aardio格式化代码工具
  15. java集成 腾讯信鸽_java后台集成腾讯信鸽消息推送
  16. kettle简单的更新与插入
  17. github 提交消息的emoji表情符号规范
  18. Excel VBA为表格设置“打开文件密码”
  19. 等保2.0控制点总结
  20. 鸿蒙系统 智能手表,wear os智能手表和鸿蒙系统智能手表对比

热门文章

  1. 如何让热点图支持大数据
  2. activemq - 浅析消息确认模式
  3. 彩色图像--色彩空间 YIQ 、YUV 、YCbCr 、YC1C2 和I1I2I3
  4. Gradle 1.12用户指南翻译——第三十五章. Sonar 插件
  5. 手握6亿把钥匙 能否打开“智能家居”的大门?
  6. 解析特殊locale的日期格式
  7. Ajax系列之JSON数据格式
  8. C++/Php/Python/Shell 程序按行读取文件或者控制台方法总结。
  9. Java8新特性系列(Lambda)
  10. 最全面的百度地图JavaScript离线版开发