WINDOWS内核对象及其理解

article/2025/10/5 19:42:00

一.前言

Windows中有很多像进程对象、线程对象、文件对象等等这样的对象,我们称之为Windows内核对象。内核对象是系统地址空间中的一个内存块,由系统创建并维护,这个内存对象是一个数据结构,维护着与对象相关的信息(如计数器)。内核对象为内核所拥有,而不为进程所拥有,所以不同进程可以访问同一个内核对象。

二.内核对象结构

每个对象都有对象头和对象体组成。所有类型的对象头结构都是相同的,而结构体部分却各不相同的。下面是内核对象的结构图:

 内核对象结构图

图中灰色部分是可能出现的。每个对象中是否存在这些部分主要由OBJECT_HEADER结构中的相关标志来指定。上面的5个结构的格式是固定的;而OBJECT结构体部分却是各个对象各不同的。需要注意的是:指向对象的指针POBJECT是指向对象体部分,而不是指向对象头的。所以,若需要访问OBJECT_HEADER,需要将POBJCECT减去0x18而获得。

下面是OBJECT_HEADER的结构

typedef struct _OBJECT_HEADER
{DWORD PointerCount; // 指针引用的数目DWORD HandleCount; // 打开句柄的数目POBJECT_TYPE ObjectType;  //指向类型对象的指针BYTE NameOffset; //对象名的偏移BYTE HandleDBOffset; // HANDLE DB的偏移BYTE QuotaChargesOffset; //QUOTA CHARGES的偏移BYTE ObjectFlags; // 对象标志union
{ // 对象标志中OB_FLAG_CREATE_INFO ? ObjectCreateInfo : QuotaBlockPQUOTA_BLOCK QuotaBlock;POBJECT_CREATE_INFO ObjectCreateInfo;};PSECURITY_DESCRIPTOR SecurityDescriptor;}OBJECT_HEADER, *POBJECT_HEADER;

三.目录对象

WINDOWS中有20几类无数的内核对象,它们都独立地存在于系统地址空间中。系统利用目录对象将所有的这些对象组织起来。目录对象是一个有37个数组元素组成的哈希(HASH)树。数据结构如下:

Typedef struct _OBJECT_DIRECTORY_ENTY
{Struct _OBJECT_DIRECTORY_ENTRY *NextEntry;POBJECT Object
}OBJECT_DIRECTORY_ENTRY, *POBJECT_DIRECTORY_ENTRY,**PPOBJECT_DIRECTORY_ENTRY;Typedef struct _OBJECT_DIRECTORY
{POBJECT_DIRECTORY_ENTRY HashTable[37];POBJECT_DIRECTORY_ENTRY CurrentEntry;BOOLEAN CurrentEntryValid;BYTE  Reserved1;WORD Reserved2;DWORD Reserved3;
}OBJECT_DIRECTORY, *POBJECT_DIRECTORY;

系统将对象名称进行一定的算法得出一个HASH值,算法如下:

//根据名字计算HASH值。
hash = 0;
p = (PSHORT)wStr; //存放名称的一个WCHAR数组
while(*p)
{Symb = (CHAR)*p;hash = hash * 3 + (hash >> 1);if (Symb < 'a')  //<ahash= hash  + Symb;else if (Symb <= 'z')   //即 a~zhash = hash + Symb - 0x20;else          // > zhash = hash + (CHAR)RtlUpcaseUnicodeChar((WCHAR)*p);p ++;
}
hash = hash % 37;  //最终的hash值。

系统将所有相同HASH值的对象链接到响应的数组项中,于是系统中所有元素将排列成如下的结构图:

系统根目录的对象的指针由ObpRootDirectoryObject来指定。

按理说,系统中只需要一个目录对象就够了,系统中所有的内核对象都将链接在这个目录对象上。但是不知什么原因,系统中并不是这样,系统中存在着多个目录对象,它们以根目录对象为根,组成一个“对象树”。每个目录对象中的哈希树的hash值的计算规则都是一样的。

我们可以根据系统中“对象树”的结构来遍历系统中所有的对象。

四.类型对象

内核对象中还有一种比较特殊的对象——类型对象。系统中每种类型对象只有一个类型对象,也就是说,系统中最多只有20几个类型对象。每种类型的对象都在其对象体中存在一个指向其类型对象的指针,因为一种类型对象只有一个实体,所以每种类型对象的指针都是固定的,这样我们就可以通过对象体中的类型对象指针来判断和访问对象的类型了。

各个类型对象的对象体内并没有链表结构使得它们相互链接起来。但是假如对象头部前面有OBJECT_CREATOR_INFO结构(见下表),则相同类型的对象就可以通过它的成员ObjectList相互链接起来了。但是,不幸的是:缺省情况下,只有Port和WaitPort两中类型的对象有这种结构。所以一般情况下,我们是不能通过类型对象来遍历这个系统中所有对象的。

typedef struct _OBJECT_CREATOR_INFO
{
LIST_ENTRY ObjectList; // OBJECT_CREATOR_INFO
HANDLE UniqueProcessId;
WORD Reserved1;
WORD Reserved2;
}OBJECT_CREATOR_INFO, *POBJECT_CREATOR_INFO, **PPOBJECT_CREATOR_INFO;

五.对象的遍历

上面分析过了,下面可以目录对象的遍历,来进行系统中所有对象的遍历。

//写一个递归函数。用来分析树型目录。void AnalyseDirectory(POBJECT_DIRECTORY pDirectory, ULONG DirectoryType, int Level)
{POBJECT_DIRECTORY_ENTRY pDirectoryEntry;POBJECT_HEADER pObjectHeader;POBJECT_NAME pObjectName;PWCHAR wStr[200];char Space[100];  //为生成空格用的。int i, j;for(i = 0; i < 36; i ++)  //DIR对象的对象体(BODY)是37个元素的数组。{pDirectoryEntry = pDirectory->HashTable[i];while(pDirectoryEntry){pObjectHeader = (POBJECT_HEADER)((ULONG)pDirectoryEntry->pObject - sizeof(OBJECT_HEADER));//生成空格RtlZeroMemory(Space, 100);for(j = 0; j < 5 * Level; j ++)Space[j] = ' ';if (pObjectHeader->NameOffset){pObjectName = (POBJECT_NAME)((ULONG)pObjectHeader  - pObjectHeader->NameOffset);RtlZeroMemory(wStr, 200 * sizeof(WCHAR));RtlCopyMemory(wStr, pObjectName->Name.Buffer, pObjectName->Name.Length);DbgPrint("%s pObject: 0x%08X Name: %S", Space, pDirectoryEntry->pObject, wStr);}elseDbgPrint("%s pObject: 0x%08X Name: noname", Space, pDirectoryEntry->pObject);//pObject对象是属性对象吗if ((ULONG)pObjectHeader->pObjectType == DirectoryType)AnalyseDirectory(pDirectoryEntry->pObject, DirectoryType, Level + 1);pDirectoryEntry = pDirectoryEntry->NextEntry;}}//end of 遍历37个记录
}

六.对象的访问

内核中知道了内核对象的地址就可以直接访问这个内核对象了,但是在用户程序中却不能这样访问。Windows为内核对象的访问提供了一系列的函数。当调用一个用于创建内核对象的函数时,函数调用完便返回一个句柄值。句柄值是进程独立的,一个进程中的句柄值在另一个进程中是无效的。

句柄值是一个进程句柄表的索引。每个进程都有一个进程句柄表,而所有进程的句柄表串成一个句柄表链。这个链的头部地址保存在内核变量HandleTableListHead中。

下面具体看一下句柄表结构。系统将句柄表组织成和线性地址解析一样的结构。句柄表是个三层的表结构,而句柄值也被分成三部分,用来分别索引这三个部分。下面是句柄解析图:

七.总结

本文可以说是一个读书笔记。在参考了很多文章的基础上,然后作一些试验才完成本文的。内核对象是Windows内部的重要数据结构。通过本文可以大致了解Windows是如何组织众多的对象的。

八. 实践

一、进程如何操作内核对象

应用程序通过句柄访问内核对象,多进程需要共享内存对象才能进行跨进程调用句柄。

二、内核对象何时销毁

操作系统内核通过计数器知道有多少进程在使用这个内核对象.(因为进程终止后,内核对象不一定销毁,只有当计数器归0 时,操作系统内核才会销毁内核对象)

三、内核对象安全性

所有创建内核对象的函数都指向一个SECURITY_ATTRIBUTES结构作为参数。

四、理解进程内核对象句柄表

进程在初始化时,系统会为它分配一个句柄表,此句柄表仅供内核对象使用,像用户对象和GUI对象都无法使用,句柄表的结构是由一个数据结构组成的数组,这个数据结构包含指向内核对象的指针、一个访问掩码和一些标志

五、如何创建内核对象

1、一个进程在初始化的时候,句柄表是空的,当进程里面的一个线程调用一个创建内核对象的函数时,操作系统内核就会为这个内核对象分配一个内存块,并且扫描进程的句柄表,找到一个空的记录项,对其进行初始化。这个记录项的指针指针成员将会被设置成内核对象的数据结构的内部内存地址。

2、任何创建内核对象的函数都会返回一个句柄,这个句柄可以由这个进程内的所有线程使用。句柄的值除以4(右移两位),就是内核对象的信息保存在这个进程句柄表中的具体位置,实际是一个索引。

3、因为句柄值实际是一个索引,它需要右移4位才是实际索引值,所以句柄值可能会出现4、8之类的值。如果创建内核对象函数调用失败,那么返回的句柄值是0(NULL)。注意:有几个函数在调用失败后会返回句柄值-1(INVALID_HANDLE _VALUE),所以需要仔细检查创建内存对象函数的返回值是-1还是0。

六、函数如何操作句柄

1、调用一个函数,如果函数的传入参数是句柄,那么这个函数将会根据句柄查找进程的句柄表,通过句柄表的成员指针,获得内存对象的地址,再进行操纵内存对象。

2、如果作为传入参数的句柄是无效的,GetLastError会返回6(ERROR_INVALID_HANDLE)。

七、为什么句柄不能跨进程调用

1、这个句柄的值是作为进程句柄表中的索引来使用的,所以这个句柄是与当前进程相关的,其他进程无法使用。

2、如果跨进程调用句柄,那么实际引用到的只是这个进程句柄表中的位于同一个索引项的内核对象———只是索引值相同而已

八、创建内存对象函数调用失败的原因

1、系统内存不足。

2、遇到一个安全问题

九、关闭内核对象

1、无论是什么方式创建内核对象的,都需要用CloseHandle向系统表明我们已经结束使用对象。

2、在这个CloseHandle 函数内部,首先检查主调进程的句柄表,验证“传给CloseHandle函数的句柄值 ”标识的是“主调进程确实有权访问的一个对象”。如果这个句柄是有效的,那么操作系统内核将根据进程的句柄表获得内核对象的数据结构的地址,并将结构中的“使用计数”成员递减,如果计数为0,内核对象将被销毁名并且从内存中去除。

3、如果传给CloseHandle 函数的是一个无效句柄,(1)如果进程是正常运行的,CloseHandle将会返回FLASE,而GetLastError返回ERROR_INVALID_HANDLE。(2)如果进程正在被调试,那么系统将会抛出0xC0000008异常(“指定了无效句柄”)。

九.参考

1.《Undocumented Windows 2000 Secrets》

2.Anathema《Inside Windows Nt Object Manager》

3.webcrazy《剖析Windows NT/2000内核对象组织》

4.《Inside Windows 2000》

5.《Windows核心编程》

6. WINDOWS内核对象_misterliwei的博客-CSDN博客

7. windows 内核对象理解_AndyQiu222的博客-CSDN博客

注意:本节描述的句柄是再WIN2K下的句柄.WINXP下句柄表结构已经完全不同.

转自:WINDOWS内核对象及其理解_lyz_cs的博客-CSDN博客_内核对象结构体是固定结构吗 


http://chatgpt.dhexx.cn/article/x6pxNVbc.shtml

相关文章

windows内核——基石

友链 内存布局 用户内存空间和内核内存空间之间的gap是为了避免不经意的越界而导致安全问题 CPU的初始化 系统启动期间&#xff0c;会对所有的处理器进行初始化操作 大部分初始化操作我们都不必了解&#xff0c;因为你了不了解都不影响你的逆向&#xff0c;毕竟你又不是开发…

Windows内核原理与实现--Windows基本结构概述

一、Windows系统结构概述 1、Windows采用双模式来保护操作系统本身,内核模式和用户模式。在Windows中,用户代码和内核代码有各自的运行环境,而且它们可以访问的内存空间也并不相同。在x86中,内核代码可以访问当前进程的4GB虚拟地址空间,而用户代码只能访问底端2GB虚拟地址…

《Windows内核原理与实现笔记》(一)Windows系统结构和基本概念

Windows内核结构 上图是windows内核的组成结构 如图Windows内核分三层&#xff0c;与硬件直接打交道的是硬件抽象层HAL&#xff0c;这一层把所有与硬件相关代码逻辑隔离到一个专门模块中&#xff0c;从而是上层尽可能独立于硬件平台。HAL是一个独立动态链接库&#xff0c;wind…

windows内核基础

windows分层模型 硬件抽象层屏蔽了硬件实现功能的细节。 IRP为内核层重要的数据结构。 物理地址和虚拟地址 x64的cpu仅仅支持64位地址中的前48位。其中若虚拟地址为内核&#xff0c;则前16位为ffff&#xff1b;若虚拟地址为用户模式&#xff0c;则前16位为0000。用户能看到的…

WebService的工作原理

WebService的工作原理如下&#xff1a; 服务提供者WebService2和WebService3通过UDDI协议将服务注册WebService目录中服务消费者WebService1通过UDDI协议从WebService目录中查询服务&#xff0c;并获得服务的WSDL服务描述文件服务消费者WebService1通过WSDL语言远程调用WebSer…

WebService原理

1、WebService实际上就是两个应用程序之间的远程调用&#xff0c;而且这种调用是跨语言的。 2、应用程序调用WebService的接口&#xff0c;实际上就是解析XML语言。也就是说两个应用程序之间的交流实际上就是通过XML来交流的。 3、WebService内部的实现是基于HTTP协议的&…

Web Service 的工作原理

Web Service基本概念 Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求&#xff0c;轻量级的独立的通讯技术。是:通过SOAP在Web上提供的软件服务&#xff0c;使用WSDL文件进行说明&#xff0c;并通过UDDI进行注册。…

JavaScript高级程序设计(第三版)pdf版 下载

分享关于JavaScript高级程序设计&#xff08;第3版&#xff09;[美] Nicholas C.Zakas著 李松峰 曹力 译 一书供大家学习&#xff01;&#xff01;&#xff01; 链接: https://pan.baidu.com/s/1RD4EXuQnTqH3kUfHWFaOYw 提取码: vias 复制这段内容后打开百度网盘手机App&#…

JS高级程序设计(14)

DOM 文档对象模型&#xff08;DOM&#xff09;是HTML和XML文档的编程借口。DOM表示由多层节点构成的文档&#xff0c;通过它开发者可以添加、删除和修改页面的各个部分。 文章目录 DOM一、节点层级1.Node类型2.Document类型3.Element类型4.Text类型5.Comment属性6.CDATASectio…

javascript高级程序设计(红宝书)记录

故心故心故心故心小故冲啊 文章目录 第3章.语法基础第4章.变量作用域与内存第5章.基本引用类型第6章.集合引用类型第8章.对象、类与面向对象编程第10章.函数第11章.期约与异步函数第12章.BOM第17章事件第24章.网络请求与远程资源第25章.客户端存储 第3章.语法基础 undefined 值…

JavaScript高级编程

原文地址&#xff1a; http://www.onlamp.com/pub/a/onlamp/2007/07/05/writing-advanced-javascript.html Web应用程序&#xff08;Web Applications&#xff09; 从计算机纪元的黎明刚刚来临开始&#xff0c;不同平台间软件的互用性就一直是关注的焦点。为了尽可能实…

javascript高级程序设计 第三版

网盘地址 提取码&#xff1a;vh81 笔记 第二章 2.1script标签 <script>元素属性&#xff1a;async、charset、defer、language、src、type async和defer只对外部脚本有效&#xff0c;language已废弃&#xff0c;type默认为“text/javascript”defer属性可以让脚本在文…

js高级程序设计(一) —— js简介

学习《JavaScript高级程序设计》的知识总结&#xff0c;以及对部分内容的扩展~ 1、一个完整的js实现的三个部分 核心&#xff08;ECMAScript) 文档对象模型&#xff08;DOM) 浏览器对象模型&#xff08;BOM) 1-1&#xff1a;ECMAScript ECMA-262的近一版是第 5版&#xf…

JS高级程序设计(12)

BOM 浏览器对象模型BOM提供了与网页无关的浏览器功能对象。 文章目录 BOM一、window对象1.Global作用域2.窗口关系3.窗口位置与像素比4.窗口大小5.视口位置6.导航与打开新窗口7.定时器8.系统对话框 二、location对象1.查询字符串2.操作地址 三、navigator对象1.检测插件2.注册…

JavaScript 高级程序设计

理解原型对象 无论什么时候&#xff0c;只要创建一个新函数&#xff0c;就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。 在默认的情况下,所有的原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个包含一个指向prototyp…

js高级程序设计(第一章)

1.什么是JavaScript 1.1 简短的历史回顾 出现背景&#xff1a;当时&#xff0c;验证简单的表单&#xff0c;需要大量与服务器的往返通信成为用户的痛点。 出现时间&#xff1a;1997 年&#xff0c;JavaScript 1.1 作为提案被提交给欧洲计算机制造商协会&#xff08;Ecma&…

JavaScript高级程序

文章目录 1. JavaScript 实现1.1 ECMAScript1.2 DOM1.3 BOM1.4 小结 1. JavaScript 实现 虽然 JavaScript 和 ECMAScript 基本上是同义词&#xff0c;但 JavaScript 远远不限于 ECMA-262 所定义的那样。 完整的 JavaScript 实现包含以下几个部分:  核心(ECMAScript)  文档…

JavaScript高级程序设计——读书笔记

文章目录 第1章 JavaScript简介第2章 在HTML中使用JavaScript第3章 基本概念第4章 变量、作用域、内存问题第5章 引用类型第6章 面向对象的程序设计第7章 匿名函数第8章 BOM第9章 客户端检测第10章 DOM第11章 DOM2和DOM3第12章 事件第13章 表单脚本第14章 错误处理与调试第15章…

javascript高级程序设计第四版(javascript高级程序设计第四版)

如何学习 JavaScript 先说说学js的条件 论条件&#xff0c;咱是文科生&#xff0c;大学专业工商管理&#xff0c;和计算机毛关系都没&#xff1b;有人说英语&#xff0c;读了四年大学&#xff0c;很遗憾&#xff0c;咱还四级没混过&#xff1b;就咱这条件都学得乐呵呵的&#x…

JavaScript高级程序设计学习笔记----初识JavaScript

一、JavaScript概述 1.JavaScript 介绍&#xff1a; 简称JS&#xff0c;是一种浏览器解释型语言&#xff0c;嵌套在HTML文件中交给浏览器解释执行。主要用来实现网页的动态效果&#xff0c;用户交互及前后端的数据传输等。 2.JavaScript 组成&#xff1a; 核心语法 - ECMAScr…