内核对象:
定义:
内核对象是一个内存块(以数据结构的方式存在),由操作系统内核分配,并只能由操作系统内核访问。
使用计数:
用来表征当前有多少个进程正在使用该内核对象,当计数为0时,操作系统会自动释放该内核对象。
安全描述符:
描述了用户对该内核对象的访问权限(如继承性等), 是一个SECURITY_ATTRIBUTES结构。
句柄:
定义:
应用程序不能直接修改内核对象,只能通过句柄来访问内核对象,句柄是一个无符号长整型数(在32位进程中为32位,64位进程中为64位)。
句柄是与进程相关的。因此,内核对象在一个进程中的句柄值,只能为该进程中的所有线程共享,而不能将句柄值从一个进程传给另一个进程调用。
进程内核对象句柄表:
进程在初始化的时候,会分配一个句柄表,包含该内核对象的指针、访问掩码和一些标志(表征内核对象和句柄的映射关系,如下表所示)。
索引 |
指向内核对象内存块的指针 |
访问掩码(包含标志位的一个DWORD) |
标志 |
1 |
0XF0000000 |
0X???????? |
0X00000000 |
2 |
0X00000000 |
(不可用) |
(不可用) |
3 |
0XF0000010 |
0X???????? |
0X00000000 |
创建和关闭内核对象:
创建内核对象:
调用Create开头的函数(如CreateThread),将创建内核对象,并在进程的句柄表中添加一项纪录,同时将内核对象的使用计数置1。
关闭内核对象:
调用CloseHandle()函数,将在该进程的句柄表中移除相关内核对象的纪录,同时将内核对象使用计数减1。当每个进程不再使用某个内核对象时,必须调用CloseHandle()来递减对内核对象的使用计数。
注意:调用CloseHandle()并不直接销毁该内核对象,而是将计数减1,只有当计数为0时,系统才销毁内核对象。调用完CloseHandle(),最好将句柄设置为NULL,以表征该内核对象已不能使用,避免在程序中继续访问该句柄。
内核对象的共享:
(1) 句柄继承:
适用范围:
只适用于父子关系的进程之间。
内核对象的设置:
句柄表中,每个纪录都有指明该句柄是否可继承的标志位。在创建内核对象时,如果PSECURITY_ATTRIBUTES参数传入NULL,则返回的句柄不可继承,标志位为0;如果PSECURITY_ATTRIBUTES结构中的bInheritHandle成员设置为TRUE,则返回的句柄可继承,标志位为1。
进程的设置:
调用CreateProcess()函数创建进程时,有一个bInheritHandles参数,该参数设置为TRUE,表示该进程支持继承,否则,表示该进程不支持继承。
句柄继承的实现:
若父进程的bInheritHandles标志位TRUE,在创建子进程时,系统会遍历父进程句柄表的所有纪录,凡发现一个有效的“可继承句柄”项,都会被完整复制到子进程的句柄表,同时对该句柄指向的内核对象计数加1。
句柄标志:
#define HANDLE_FLAG_INHERIT 0x00000001 可继承
#define HANDLE_FLAG_PROTECT_FROM_CLOSE 0x00000002 可关闭
SetHandleInformation:设置句柄标志
GetHandleInformation:获取句柄标志
(2)为对象命名:
创建一个内核对象时,可为其命名,命名的内核对象可在不同进程间共享。当一个进程试图创建一个命名内核对象,而该内核对象的名称已经存在时,系统只会为内核对象分配一个新的句柄供该进程调用,而不会重复创建内核对象。因此,实际上进程调用的还是已经存在的那个内核对象。
应用:
命名内核对象的共享(如CreateMutex),常用于判定一个进程是否已经存在。
Create*函数和Open*函数的区别:
如果对象不存在,Create*函数会创建一个新的内核对象,而Open*函数只会返回错误信息,而不创建该内核对象。
(3)句柄复制:
DuplicateHandle():调用该函数,将获得一个进程句柄表中一个记录项,然后在另一个进程句柄表中创建该记录的一个副本。
phTargetHandle参数:
DUPLICATE_SAME_ACCESS: 目标句柄与源句柄有同样的访问掩码。类似于复制。
DUPLICATE_CLOSE_SOURCE: 复制源句柄到目标句柄,并关闭源句柄。类似于剪切。