windows内核对象

来源:互联网 发布:大唐通讯科技软件 编辑:程序博客网 时间:2024/05/29 11:01
Index
  • 内核对象
  • 进程的内核对象句柄表
  • 跨越进程边界共享内核对象

内核对象

1.什么是内核对象?
内核对象是windows内核分配的一个内存块,该内存块是一种数据结构,存储了该对象的各种信息。

2.内核对象的操作权限

内核对象的数据结构只能被内核访问,应用程序无法在内存中找到这些数据结构并直接改变它们的内容,但我们的应用程序可以使用windows提供的API函数来对这些构进行操作。

3.内核对象的数据成员

不同的内核对象具有不同的数据成员,但基本上所有的内核对象都有安全性描述和使用计数这两个成员。

a.使用计数

内核对象由内核所拥有,而不是进程所拥有。因此一个内核对象可以被多个进程所使用,使用计数就是内核用来统计该内核

对象被多少个进程所使用。

b.安全性

内核对象能够得到安全描述符的保护。安全描述符用于描述谁创建了该对象,谁能够访问或使用该对象,谁无权访问该对象。大多数应用程序将安全
描述符设成NULL,表示创建默认安全性的内核对象。


4.区分内核对象

Windows操作系统下,除了内核对象外,还有其他类型的对象,如菜单,窗口,字体等,这些对象属于用户对象,而不是内核对象,那么如何来判断一个对象是否是内核对象呢?最容易的方法是观察创建该对象的函数是否有安全属性信息。

进程的内核对象句柄表

一个应用程序执行时,内核对象的创建过程:

1. 进程初始化时,系统会创建一个进程内核对象,使用计数为1,然后,系统为进程创建一个虚拟地址空间,并将可执行文件或任何必要的DLL文件的代码和数据加载到该进程的地址空间中。然后,系统自动创建一个主线程用于进程代码的执行,同时会为主线程创建一个线程内核对象,通过执行C/C++运行期启动代码,主线程开始运行,最终调用WinMain、Main等函数。

2.在进程初始化时,系统要为它分配一个空的句柄表,该表只用于内核对象,不用于用户对象。然后,当该进程创建新的内核对象的时候,内核就对进程的句柄表进行扫描,找到一个空项,对其进行初始化的工作。

3. 当一个内核对象使用完毕时,应该用CloseHandle来结束对该对象的操作。该函数首先检查调用进程的句柄表,以确保句柄值有效,然后清除句柄表中对应的项目,将内核对象的使用计数减一,如果此时内核对象的使用计数为0,则内核会释放该内核对象。

NOTE:如果忘记使用CloseHandle,有可能发生内存泄露。如果此时进程正在进行则可能会造成内存泄露。但是如果进程终止运行时,则系统会自动调用该函数,不会造成内存泄露的情况发生。

跨越进程边界共享内核对象

内核对象的句柄是与进程相关的,因此进程中的任意线程都可以使用该内核对象,但是很多时候在不同进程中运行的线程也需要共享内核对象,那么怎么处理呢?

主要有三种方法实现跨越进程边界共享内核对象:
1. 继承。
2. 命名。
3. 复制对象句柄。

继承

只有当进程具有父子关系的时候,才可以用内核对象句柄的继承性共享内核对象。在这种情况下,父进程可以使用一个或多个内核对象句柄,并且该父进程可以决定生成一个子进程,为子进程赋予对父进程的内核对象的访问权。

首先,当父进程创建内核对象时,必须向系统指明,该对象的句柄是否是可以继承的句柄(内核对象的句柄具有继承性,但是内核对象不具有继承性),主要通过将安全描述符中的bInheritHandle设置为true,然后传入内核对象的创建函数。


此时,该进程句柄表中对应的内核对象句柄中的标志就会发生变化。当标志为0时,表明该句柄不可被继承,为1则表示可以被继承。

然后,该进程使用CreateProcess创建一个子进程,其中要将bInheritHandle参数设置为TRUE,这样,子进程才可以继承父进程的可继承句柄值。

此时,子进程创建的过程中还要进行另外一项操作,就是它要遍历父进程的句柄表,找到可继承的句柄值,然后将这些句柄值准确的COPY到子进程的句柄表中,他们的位置也要一样,即最后相同的对象,父进程和子进程具有相同的句柄值。

命名对象

共享跨越进程边界的内核对象的第二种方法是给内核对象命名。

比如上面的创建一个互斥对象的函数,其最后一个参数lpName就是对象的名字,当设置为NULL时表示创建一个未命名的内核对象,否则传递如一个字符串(最大260个字符)。但是微软没有提供为内核对象赋予名字的指导原则,比如:如果创建一个名字为"JiangNan"的内核对象,那么不能保证系统中不存在一个名字为"JiangNan"的对象,因此当出现重名,并且不是相同的对象时,对象创建就会出错。

那么就让我们说明一下它的工作原理:假设当前有一个ProcessA,它创建了一个名字为"JN"的互斥对象,过了一段时间,ProcessB运行,它也要创建一个名字为"JN"的内核对象("JN"这个名字就这么好吗,大家都想创建?),此时系统就会查看是否已经存在一个名字为"JN"d的内核对象,如果存在,则检查对象的类型,如果类型也一样,那么系统会执行一次安全检查,以确定调用者是否具有对该对象的完整访问权,如果也可以访问,那么系统就会在ProcessB的句柄表中创建一个句柄值来指向ProcessA中的内核对象。此时并不创建新的内核对象,而是指向原来的内核对象,并将使用计数加一。
除此之外,也可以使用各种Open**函数打开已存在的内核对象。

复制对象句柄

共享跨越进程边界的内核对象的最后一种方法是使用DuplicateHandle函数。该函数涉及到三个进程。
参数列表:
hSourcePrecessHandle和hTargetProcessHandle:是原进程和目标进程的句柄,且这两个进程都必须与当前调用进程相关。
hSourceHandle和lpTargetHandle:是要复制的内核对象。
剩下的三个参数用于指明该目标进程的内核对象句柄表项目中使用的访问屏蔽值和继承性标志。





原创粉丝点击