
基础知识
1.理解Windows应用程序编程接口(application programming interface,API),首先需要探讨一下内核对象(kernel object)及其句柄(handle)。在系统和我们写的应用程序中,内核对象用于管理进程、线程和文件等诸多种类的大量资源。
2.每个内核对象都只是一个内存块,它由操作系统内核分配,并只能由操作系统内核访问。这个内存块是一个数据结构,其成员维护着与对象相关的信息。少数成员(安全描述符和使用计数等)是所有对象都有的,但其他大多数成员都是不同类型的对象持有的。
3.内核对象的数据结构只能有操作系统访问,所以应用程序不能在内存中定位这些数据结构并直接修改内容。Microsoft有意强化了这个限制,所以Microsoft能自由地添加、删除和修改这些结构中的成员,同时不会干扰任何应用程序的正常运行。
4.利用Windows提供的一组函数,这组函数会以恰当的方式来操纵这些结构。我们始终可以使用这些函数来访问这些内核对象。调用一个会创建内核对象的函数以后,函数会返回一个句柄(handle),它标识了所创建的对象。
5.内核对象的所有者是操作系统内核而不是进程。如果进程调用一个函数来创建内核对象,进程终止运行时内核对象并不一定会销毁。每个对象都包含一个使用计数(usage count),是每个内核对象类型都有的一个数据成员。初次创建一个对象时,使用计数为1,另一个进程获得现有内核对象访问后,使用计数递增。进程终止后操作系统会自动递减该对象的使用计数。一旦对象的使用计数变为0,操作系统内核就会销毁该对象。
6.内核对象可以用一个安全描述符(security descriptor,SD)来保护。安全描述符描述了谁(通常是对象的创建者)拥有对象;哪些组和用户被允许访问或使用此对象;哪些组和用户被拒绝访问此对象。
7.一个进程初始化时,系统将会为它分配一个句柄表(handle table)。首次初始化的时候,其句柄表为空。当进程内的一个线程调用一个会创建内核对象的函数时,内核将为这个对象分配并初始化一个内存块。然后,内核扫描进程的句柄表,查找一个空白的记录项(empty entry),对其进行初始化。用于创建内核对象的任何函数都会返回一个与线程相关的句柄,这个句柄可由同一个进程中运行的所有线程使用。
8.句柄被设计成“与进程相关的”(process-relative),其中最重要的是原因是健壮性/可靠性,还有安全性。
9.只有在进程之间有一个父-子关系的时候,才可以使用对象句柄继承。只有句柄才是可以继承的,对象本身不能继承。
10.为了使子进程得到它想要的一个内核对象的句柄值,最常见的方式是将句柄值作为命令行参数传给子进程。子进程初始化代码将解析命令行(通常调用_stscanf_s),并提取句柄值。子进程获得句柄值后,就会拥有和父进程一样的内核对象访问权限。句柄继承之所以能够实现,唯一的原因就是“共享的内核对象”的句柄值在父进程和子进程中是完全一样的。这正是父进程能将句柄值作为命令行参数来传递的原因。
11.跨进程边界共享内核对象的方法有:使用对象句柄继承、为对象命名、复制对象句柄。
12.终端服务(Terminal Service)情况不同,正在运行终端服务的计算机中,有多个用于内核对象的命名空间。其中一个是全局命名空间,所有客户端都能访问的内核对象都放在这个命名空间中。这命名空间主要由服务使用。
13.想确保我们的应用程序创建的内核对象名称永远不会和其他应用程序名称冲突,或者想确保它们免遭劫持,可以定义一个自定义的前缀,并把它作为自己的专有命名空间使用,这和使用Global和Local前缀是相似的。
常用函数
1.CloseHandle函数:创建内核对象后一定要用该函数向系统表明我们已经结束使用对象
1 2 3 |
BOOL WINAPI CloseHandle( _In_ HANDLE hObject ); |
2.SetHandleInformation函数:改变内核对象的继承标志
1 2 3 4 5 |
BOOL WINAPI SetHandleInformation( _In_ HANDLE hObject, _In_ DWORD dwMask, _In_ DWORD dwFlags ); |
本文链接地址: Windows via C/C++ Chapter 3 : 内核对象
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
如果您愿意为文章的内容或想法提供支持,欢迎点击下边的捐赠按钮,资助作者创作更多高价值高品质的内容。