内核对象与句柄
2015-02-09 22:53
204 查看
每个内核对象都只是一个内核块,它有操作系统内核分配,并只能由操作系统内核访问。这个内存块是个数据结构,其成员维护着与对象相关的信息。
Windows提供的一组函数,(以最恰当的方式)来操作内核对象。在调用一个会创建内核对象的函数后,函数会返回一个句柄(handle),它标识了所创建的对象。可以将句柄想象成一个不透明的值,它可以由进程中任何线程使用。
内核对象的所有者是操作系统内核,而非进程。
1)通过内核对象的使用计数(usage count)成员,操作系统知道当前有多少进程正在使用该内核对象,内核对象的生命期可能长于创建它的那个进程。
2)内核对象可以用一个安全描述符(security descriptor,SD)来保护。安全描述符描述了谁(通常是对象的创建者)拥有对象;那些组和用户被允许访问或使用此对象;那些组合用户被拒绝访问此对象。
(security descriptor:A structure and associated data that contains the security information for a securable object. A security descriptor identifies the object's owner and primary group. It can also contain a DACL (discretionary access-control list)that controls access to the object, and a SACL (self-relative security descriptor)that controls the logging of attempts to access the object.)
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES;
想要判断一个对象是不是内核对象,最简单的方式是查看创建这个对象函数。几乎所有创建内核对象的函数都有一个允许我们指定安全属性信息的参数。
一个进程在初始化时,系统将为它分配一个句柄表(handle table)。这个句柄表仅供内核对象使用,不适用于用户对象或GUI对象。
1、创建一个内核对象
一个进程首次初始化的时候,其句柄表为空。当进程内的一个线程调用一个会创建内核对象的函数时,内核将为这个对象分配并初始化一个内存块,然后,内核扫描进程的句柄表,查找一个空白的记录项(empty entry)进行设置。
2、关闭内核对象
BOOL CloseHandle(HANDLE hObject)
该函数先校验句柄的有效性,如果句柄有效,系统将获得内核对象数据结构的地址,并将结构中的“使用计数”递减,如果使用计数变成0,内核对象将被销毁,其占用的内存将会被释放。
该函数会清除进程句柄表中对应的记录项。
如果忘记调用CloseHandle,在进程运行期间,进程可能会发生资源泄露。但是,当进程终止运行时,操作系统会确保此进程所使用的所有资源都被释放。
3、跨进程边界共享内核对象
1)使用对象句柄继承
为创建一个可继承的句柄,父进程必须分配并初始化一个SECURITY_ATTRIBUTES结构,并将该结构的地址传给具体的Create函数。
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE hMutex = CreateMutex(&sa, FALSE, FALSE);
父进程有权访问两个内核对象(句柄1和句柄3),其中句柄1不可继承,句柄3可以继承,通过CreateThread生成子进程。
BOOL CreateProcess(
LPCTSTR lpApplicationName, // name of executable module
LPTSTR lpCommandLine, // command line string
LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
BOOL bInheritHandles, // handle inheritance option
DWORD dwCreationFlags, // creation flags
LPVOID lpEnvironment, // new environment block
LPCTSTR lpCurrentDirectory, // current directory name
LPSTARTUPINFO lpStartupInfo, // startup information
LPPROCESS_INFORMATION lpProcessInformation // process information
);
将CreateThread函数bInheritHandle设为TRUE,系统会遍历父进程的句柄表,对它的每个记录项进行检查。凡是有效的“可继承的句柄”记录项,都会被完整地复制到子进程的句柄表中(包括句柄值)。
(A)父进程句柄表
(B)子进程句柄表
子进程并不知道自己继承了任何句柄,为了让子进程得到它想要的一个内核对象的句柄值,常见的方式是将句柄值作为命名行参数传给子进程。另一种方式是让父进程向其环境块中添加一个环境变量,变量的名称应该是子进程知道的,而变量的值应该是准备被子进程继承的那个内核对象的句柄值。子进程会继承父进程的环境变量。当然,其他的进程间的通信技术也可用于将父进程中的句柄值传给子进程。
Windows提供的一组函数,(以最恰当的方式)来操作内核对象。在调用一个会创建内核对象的函数后,函数会返回一个句柄(handle),它标识了所创建的对象。可以将句柄想象成一个不透明的值,它可以由进程中任何线程使用。
内核对象的所有者是操作系统内核,而非进程。
1)通过内核对象的使用计数(usage count)成员,操作系统知道当前有多少进程正在使用该内核对象,内核对象的生命期可能长于创建它的那个进程。
2)内核对象可以用一个安全描述符(security descriptor,SD)来保护。安全描述符描述了谁(通常是对象的创建者)拥有对象;那些组和用户被允许访问或使用此对象;那些组合用户被拒绝访问此对象。
(security descriptor:A structure and associated data that contains the security information for a securable object. A security descriptor identifies the object's owner and primary group. It can also contain a DACL (discretionary access-control list)that controls access to the object, and a SACL (self-relative security descriptor)that controls the logging of attempts to access the object.)
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES;
想要判断一个对象是不是内核对象,最简单的方式是查看创建这个对象函数。几乎所有创建内核对象的函数都有一个允许我们指定安全属性信息的参数。
一个进程在初始化时,系统将为它分配一个句柄表(handle table)。这个句柄表仅供内核对象使用,不适用于用户对象或GUI对象。
索引 | 指向内核对象内存块的指针 | 访问掩码 | 标志 |
1 | 0xXXXXXXXX | 0xXXXXXXXX | 0xXXXXXXXX |
2 | 0xXXXXXXXX | 0xXXXXXXXX | 0xXXXXXXXX |
1、创建一个内核对象
一个进程首次初始化的时候,其句柄表为空。当进程内的一个线程调用一个会创建内核对象的函数时,内核将为这个对象分配并初始化一个内存块,然后,内核扫描进程的句柄表,查找一个空白的记录项(empty entry)进行设置。
2、关闭内核对象
BOOL CloseHandle(HANDLE hObject)
该函数先校验句柄的有效性,如果句柄有效,系统将获得内核对象数据结构的地址,并将结构中的“使用计数”递减,如果使用计数变成0,内核对象将被销毁,其占用的内存将会被释放。
该函数会清除进程句柄表中对应的记录项。
如果忘记调用CloseHandle,在进程运行期间,进程可能会发生资源泄露。但是,当进程终止运行时,操作系统会确保此进程所使用的所有资源都被释放。
3、跨进程边界共享内核对象
1)使用对象句柄继承
为创建一个可继承的句柄,父进程必须分配并初始化一个SECURITY_ATTRIBUTES结构,并将该结构的地址传给具体的Create函数。
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE hMutex = CreateMutex(&sa, FALSE, FALSE);
父进程有权访问两个内核对象(句柄1和句柄3),其中句柄1不可继承,句柄3可以继承,通过CreateThread生成子进程。
BOOL CreateProcess(
LPCTSTR lpApplicationName, // name of executable module
LPTSTR lpCommandLine, // command line string
LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
BOOL bInheritHandles, // handle inheritance option
DWORD dwCreationFlags, // creation flags
LPVOID lpEnvironment, // new environment block
LPCTSTR lpCurrentDirectory, // current directory name
LPSTARTUPINFO lpStartupInfo, // startup information
LPPROCESS_INFORMATION lpProcessInformation // process information
);
将CreateThread函数bInheritHandle设为TRUE,系统会遍历父进程的句柄表,对它的每个记录项进行检查。凡是有效的“可继承的句柄”记录项,都会被完整地复制到子进程的句柄表中(包括句柄值)。
(A)父进程句柄表
索引 | 指向内核对象内存块的指针 | 访问掩码 | 标志 |
1 | 0xF0000000 | 0xXXXXXXXX | 0x00000000 |
2 | 0x00000000 | (不可用) | (不可用) |
3 | 0xF0000010 | 0xXXXXXXXX | 0x00000001 |
(B)子进程句柄表
索引 | 指向内核对象内存块的指针 | 访问掩码 | 标志 |
1 | 0x00000000 | (不可用) | (不可用) |
2 | 0x00000000 | (不可用) | (不可用) |
3 | 0xF0000010 | 0xXXXXXXXX | 0x00000001 |
子进程并不知道自己继承了任何句柄,为了让子进程得到它想要的一个内核对象的句柄值,常见的方式是将句柄值作为命名行参数传给子进程。另一种方式是让父进程向其环境块中添加一个环境变量,变量的名称应该是子进程知道的,而变量的值应该是准备被子进程继承的那个内核对象的句柄值。子进程会继承父进程的环境变量。当然,其他的进程间的通信技术也可用于将父进程中的句柄值传给子进程。
相关文章推荐
- 《windows核心编程系列》三谈谈内核对象及句柄的本质
- windows核心编程--内核对象和句柄泄漏
- Windows内核对象(1) -- 内核对象与句柄
- 多线程--内核对象和句柄泄露&CloseHandle
- 读书笔记----《windows核心编程》第三章 内核对象1(句柄与安全性)
- 白话windows内核对象共享之复制对象句柄
- windows编程(1)-句柄,内核对象
- 跨越进程边界共享内核对象【复制对象句柄】
- windows笔记-进程的内核对象句柄表
- 内核对象句柄表
- 内核对象 复制对象句柄 DuplicateHandle 跨进程边界共享内核对象
- windows核心编程--内核对象和句柄泄漏
- windows笔记-跨越进程边界共享内核对象【对象句柄的继承性】
- windows笔记-跨越进程边界共享内核对象【复制对象句柄】
- 《windows核心编程系列》三谈谈内核对象及句柄的本质
- Windows核心编程笔记(三) 内核对象与句柄
- Windows进程内核对象句柄表
- 进程的内核对象句柄表
- windows核心编程--内核对象和句柄泄漏
- 多线程--内核对象和句柄泄露&CloseHandle