Windows 之 内核对象

1.什么是内核对象:

内核对象的类型有很多,比如存取符号对象、事件对象、文件对象、文件映射对象、I/O完成端口对象、作业对象、信箱对象、互斥对象、管道对象、进程对象、信标对象、线程对象和等待计时器对象等。
内核对象是内核分配的一个内存块,并且只能由该内核访问。该内存块是一种数据结构,它的成员负责维护该对象的各种信息。有的数据成员(安全描述符,使用计数器)
在所有对象类型中是相同的,但大多数数据成员数据特定的对象类型。应用程序无法在内存中找内核对象并直接改变它们,需要通过Windows提供的api。
调用一个创建内核对象的函数时,会返回一个句柄用于标识这个对象,在这个进程中可以使用这个值,但是这个值不可作用于其他进程(在其他进程中使用该值操作内核对象就会失败)。

2.内核对象的计数器:

内核对象归内核所拥有,而不是进程,进程调用函数创建一个内核对象后,如果进程终止,该内核对象大多数情况下会被撤销,但如果另一个进程正在使用该进程创建的内核对象,在另一个进程终止使用该对象之前是不会被撤销的。所以,内核对象的存在时间可以比创建该对象的进程长。
内核知道有多少进程正在使用某个内核对象,因为每个对象包含一个使用计数。当一个对象刚刚创建时,它的使用计数被置为 1。然后,当另一个进程访问一个现有的内核对象时,使用计数就递增 1,当进程终止运行时,内核就自动确定该进程仍然打开的所有内核对象的使用计数,如果内核对象的使用计数降为 0,内核就撤消该对象。(调用CloseHandle之后,引用计数减1,如果没有调用,进程结束时系统会释放内核对象的句柄,计数器减1)。

3.内核对象的安全性:

内核对象能够得到安全描述符的保护。安全描述符用于描述谁创建了该对象,谁能够访问或使用该对象,谁无权访问该对象。
用于创建内核对象的函数几乎都有一个指向 SECURITY_ATTRIBUTES结构的指针作为其参数。传入NULL,创建默认安全属性的内核对象。(默认安全性意味着对象的管理小组的任何成员和对象的创建者都拥有对该对象的全部访问权,其他人无权)。
SECURITY_ATTRIBUTES的结构:
typedef struct _SECURITY_ATTRIBUTES
{
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
}SECURITY_ATTRIBUTES;

4.进程的内核对象句柄表:

当一个进程被初始化时系统要为它分配一个句柄表,该句柄表只用来存储内核对象(用户对象和GDI对象不在其中)。
句柄表中的每一项分为三部分:内核对象内存块的指针,访问屏蔽,继承标志。

句柄表结构:
在这里插入图片描述

当进程初次被初始化时,它的句柄表是空的,当进程中的线程调用创建内核对象的函数时,内核就为该对象分配一个内存块,并对它进行初始化。内核对进程的句柄表进行扫描,找出一个空项,空项将被设置为内核对象的数据结构的内存地址,防问屏蔽设置为全部防问权,各个标志也作了设置。注意的是,函数返回的句柄是该对象在句柄表中的索引。

5.内核对象的创建:

通过上面的知识,我们明了一个内核对象的创建过程,让我们来梳理一下。
首先在进程中调用相应内核对象的创建函数,传入相应的参数,这时内核为该对象分配一个内存块,然后在进程的句柄表中找到一个空项,利用该内核对象和函数调用时传入的参数对这个空项进行初始化,并且将该项索引作为函数返回值。

6.内核对象的关闭:

无论怎样创建内核对象,都要向系统指明将通过调用 CloseHandle来结束对该对象的操作。该函数首先检查调用进程的句柄表,以确保传递给它的索引(句柄)不是一个进程实际上无权访问的操作。如果该索引是有效的,那么系统就可以获得内核对象的数据结构的地址,并且对其计数器减1,如果计数器为0,内核便从内存中撤销该对象。值得注意的是,只要调用CloseHandle函数,进程就会从句柄表中清除该对象,不在拥有该对象的访问权。 所以,那些没有调用CloseHandle函数的内核对象,在进程结束的时候还会在句柄表中存在,这时系统会关闭这些句柄,相应对象的计数器减1,如果为0,对象被撤销。

猜你喜欢

转载自blog.csdn.net/weixin_45074185/article/details/105466337