二 内核对象
1 Windows的内核对象指的是由内核分配的一个内存空间,该空间仅可由内核访问。是一种数据结构,用于管理对象的各种信息。
2 内核对象由系统创建,用户程序仅仅是维护(拥有)一个对其的引用。系统对内核对象维护一个计数器。当一个进程调用CreateXXX函数创建
内核对象时,可以理解为初始化一个内核对象,并在内存中分配空间,而该内核对象的计数器置为1。其他进程也通过系统获取对该对象的使用
,则计数器加1。如果某使用该对象的进程结束(CloseHandle),计数器减1。计数器为0,系统析构该内核对象,回收内存空间。
3 用户进程通过Handle Table管理对内核对象的访问。每个进程由系统为其维护一个Handle Table。Handle Table如下格式:
Index Pointer to Kernel Object Access Mask Flags
1 0x???????? ox???????? 0x????????
2 0x???????? ox???????? 0x????????
......
当用户进程通过系统获取对内核对象的访问,系统返回一个Handle值,该Handle值除以4(或者右移2位,因为低两位保留Windows使用)即
为该对象在该进程中的Index,而用户进程通过Handle Table 查找该内核对象的地址和访问权限。因此该Handle值是进程特定的,它表示的是
在该进程的Handle Table 中的位置,而不是Pointer to Kernel Object。
注意,CreateFile会返回-1(INVALID_HANDLE_VALUE),其他Create*失败的时候会返回0(NULL),要看清Create*的返回结果定义。
4 调用CloseHandle之后,Handle Table 中相应的记录会被删除(如果没有显式调用CloseHandle,系统会在Process退出时自动进行该工作)
。应该养成同时将保存Handle的变量赋值NULL的习惯,避免该变量被错误使用而指向了Handle Table 中的空记录,或者Handle Table 为另一
个内核对象分配的新记录。
5. 内核对象有安全描述符的保护,安全描述符描述了谁创建了该对象以及谁能够使用该对象。用于创建内核对象的函数几乎都有一个指向SEC
URITY_ATTRIBUTES 结构的指针作为其参数。
大多数应用程序通过传NULL值创建具有默认安全性的对象(对象管理小组的任何成员及创建者拥有全部访问权,而其他任何人均无权访问
)。如果你想限制别人对对象的访问,你就需要单独创建一个SECURITY_ATTRIBUTES对象并对其初始化。代码如下:
1 SECURITY_ATTRIBUTES sa;
2 sa.nLength = sizeof(sa); //Used for versioning
3 sa.lpSecuntyDescriptor = pSD, //Address of an initialized SD
4 sa.bInheritHandle = FALSE; //Discussed later
5 HANDLE hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE,
6 &sa, PAGE_REAOWRITE, 0, 1024, "MyFileMapping");
当一个进程被初始化时,系统会为其分配一个句柄表。句柄表用于内核对象。
7 创建内核对象
进程初次初始化时,句柄表是空的。进程中的线程调用创建内核对象的函数时,内核就为相应的内核对象分配一个内存块,并初始化。内核
对进程的句柄表进行扫描,找到一个空项,用对象的数据结构的内存地址进行初始化。
进程在运行时有可能出现内存泄漏。在进程终止运行时,系统会自动扫描进程的句柄表。若表中拥有任何无效项目(进程终止前没关闭的
对象),系统将关闭这些对象的句柄。
8 跨进程边界共享内核对象
很多时候,不同进程的线程需要共享内核对象。如邮箱和指定的管道使应用程序能在联网的计算机上不同的进程之间发送数据块。
对象句柄的兼容性:只有当进程之间是父子关系时,才能使用对象句柄的继承性。在这种情况下,父进程可以使用一个或多个内核对象句柄
,并且该父进程可以决定生成一个子进程,为子进程赋予对父进程的内核对象的访问权。
实现过程:1.父进程创建内核对象,并指明对象的句柄是可继承的句柄,注意内核对象本身不具备继承性。
2.使用对象句柄继承性时要执行的下一个步骤是让父进程生成子进程。
CreateProcess的第五个参数为TRUE,表明句柄可继承,FALSE不可。
在这个过程中,系统将拷贝可继承的句柄的每个项目到子继承的句柄表中。并递增内核对象相应的计数器。CreateProcess函数返回后,父进程
会立即关闭对象的句柄,而不影响子进程对该对象进行相关操作。
命名对象:共享跨越进程边界的内核对象的另一种方法。大部分内核对象可以被命名。若要按名字共享对象,则必须为对象赋予同一个名
字。