内核对象 《Windows核心编程》

      每个内核对象都只是一个内存块,它又操作系统内核分配,并只能由操作系统内核访问。这个内存块是一个数据结构,其成员维护着与对象相关的信息。少数成员(安全描述符和使用计数)是所有对象都有的,但其他大多数成员都是不同类型的成员特有的。由于内核对象的数据结构只能由操作系统内核访问,所以应用程序不能再内存中定位这些数据结构并直接更改内容。

    使用计数:操作系统知道当前有多少个进程正在使用一个特定的内核对象,是因为每个内核对象都包含一个使用计数。初次创建一个内核对象得到时候,其使用计数设为1。如果一旦对象的使用计数变为0,操作系统内核就会销毁该对象。

    安全属性:内核对象可以用一个安全描述符(security  descriptor, SD)来保护。用于创建内核对象的所有函数几乎都有一个SECURITY_ATTRIBUTES结构的指针作为参数,大多数应用程序都只是为这个参数传入NULL,这样创建的内核对象具有默认  

                       的安全属性。

     一个进程在初始化时,系统将为它分配一个句柄表。这个句柄表进攻内核对象使用,不适用于用户对象或GDI对象。一个进程首次初始化的时候,其句柄表为空。当进程内的一个线程调用一个会创建内核对象的函数时,内核将为这个对象分配并初始化一个内存块。然后内核扫描进程的句柄表,查找一个空白的记录项并对其进行初始化。用于创建内核对象的任何函数都会返回一个与进程相关的句柄,这个句柄可又同一个进程中运行的所有线程使用。系统用索引来表示内核对象的信息保存在句柄表中的具体位置,要得到实际的索引值,句柄值应该除以4。调用一个函数时,如果它接受一个内核对象句柄作为参数,就必须把Create* 函数返回的值传给它。在内部,这个函数会查找进程的句柄表,或的目标内核对象的地址,然后以一种恰当的方式操纵内核对象的数据结构。

     无论以什么方式创建内核对象,我们都要调用CloseHandle向系统表明我们已经结束使用对象,在内部,函数首先检查主调进程的句柄表,验证“传给函数的句柄值”标示的是“进程确实有劝访问的一个对象”。如果句柄是有效的,系统就将获得内核对象的数据结构的地址,并将结构中的“使用计数”成员递减。如果使用计数变为0,内核对象将被销毁,并从内存中去除。就在CloseHandle返回之前,它会清除进程句柄表中对应的纪录项——这个句柄现在对我们的进程来说是无效的,不要再试图用它。无论内核对象当前是否销毁,这个清除过程都会发生!一旦调用CloseHandle我们的进程就不能访问那个内核对象;但是,如果对象的使用计数还没有递减至0,它就不会被销毁。这是完全正常的;它表明另外还有一个或多个进程在使用该对象。当其他进程(通过调用CloseHandle)全部停止使用这个对象后,对象就会被销毁。假如忘记调用CloseHandle ,系统会确保进程使用的所有资源都被释放。对于内核对象,系统执行的操作时:当进程终止时,系统自动扫描该进程的句柄表,如果这个表中有任何有效的记录项(即进程终止前没有关闭的对象),操作系统会为我们关闭这些对象句柄。只要这些对象中有一个的使用计数递减至0,内核就会销毁对象。这适用于所有内核对象,资源(包括GDI对象在内)以及内存块。进程终止运行时,系统会确保我们的进程不留下任何东西。

  

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

    (1)使用对象句柄继承

              只有在进程之间有一个父—子关系的时候,才可以使用对象句柄继承。

              为创建一个可继承的句柄,父进程必须分配并初始化一个SECURITY_ATTRIBUTES结构,并将这个结构的地址传给具体的Create函数:

              SECURITY_ATTRIBUTES sa;

              sa.nLength = sizeof(sa);

              sa.bInheritHandle = TRUE;   //使返回的句柄成为可继承的

              HANDLE hMutex = ::CreateMutex(&sa,FALSE,NULL);

              句柄表中的每一个记录项都有一个指明句柄是否可以继承的标志位。如果在创建内核对象的时候将NULL作为PSECURITY_ATTRIBUTES的参数传入,则返回的句柄是不可继承的,这个标志位为0.将bInheritHandle成员设为TRUE,则导致这个

              标志位被设为1.

             为了使用对象句柄继承,下一步是由父进程生成子进程。这是通过CreateProcess函数来完成的

             BOOL WINAPI CreateProcess( __in LPCTSTR lpApplicationName,

                                                                     __in_out LPTSTR lpCommandLine,

                                                                     __in LPSECURITY_ATTRIBUTES lpProcessAttributes,

                                                                     __in LPSECURITY_ATTRIBUTES lpThreadAttributes,

                                                                     __in BOOL bInheritHandles,

                                                                     __in DWORD dwCreationFlags,

                                                                     __in LPVOID lpEnvironment,

                                                                     __in LPCTSTR lpCurrentDirectory,

                                                                     __in LPSTARTUPINFO lpStartupInfo,

                                                                     __out LPPROCESS_INFORMATION lpProcessInformation);

            

                bInheritHandles,通常情况下,我们将该参数设为FALSE。这个值向系统表明:我们不希望子进程继承父进程句柄表中的”可继承的句柄“。如果向这个参数传递TRUE,子进程就会继承父进程的”可继承的句柄“的值。操作系统会创建新的

                子进程,但不允许子进程立即执行它的代码。系统会为子进程创建一个新的,空白的句柄表。系统会遍历父进程的句柄表,对它的每个记录项进行检查。凡是会包含一个有效的”可继承的句柄“的项,都会被完整的复制到子进程的句柄表。在子

                进程的句柄表中,复制想的位置与它在父进程的位置完全一样。它意味着:在父进程和子进程中,对一个内核对象进行标示的句柄中是完全一样的。除了复制句柄表的记录项,系统还会递增内核对象的使用计数,因为两个进程都在使用这个对象

                 。对象句柄的继承只会在生成子进程的时候发生。加入父进程后来又创建了新的内核对象,并同样将他们的句柄设为可继承的句柄。那么正在运行的子进程是不会继承这些新句柄的。为了使子进程得到它想要的一个内核对象的句柄值,最常见的

                方式是将句柄值作为命令行参数传给子进程。

        (2)为对象命名:

                  许多内核对象都可以进行命名。在创建内核对象的Create函数中的 pszName  传入NULL,向系统表明我们要创建一个未命名的内核对象。若要创建一个命名的内核对象,可以传入一个”以0 为终止符的名称字符串“。所有对象都共享同一个

                  命名空间,即使它们的类型并不相同。通过为对象命名来实现共享式,是否可以继承并非一个必要条件。

                  例如A进程通过CreateMutex函数创建一个名为 "JeffMutex"内核对象时,进程B可以通过CreateMutex函数打开这个内核对象,但两个进程中的句柄值不一定相同。内核对象的使用计数会递增。

        (3)复制对象句柄:

                  该方法通过使用 DuplicateHandle函数实现:

                  BOOL WINAPI DuplicateHandle( __in HANDLE hSourceProcessHandle,

                                                                              __in HANDLE hSourceHandle,

                                                                              __in HANDLE hTargetProcessHandle,

                                                                              __out LPHANDLE lpTargetHandle,

                                                                              __in DWORD dwDesiredAccess,

                                                                              __in BOOL bInheritHandle,

                                                                              __in DWORD dwOptions);

                   这个函数获得一个进程的句柄表中的一个记录项,然后再另一个进程的句柄表中创建这个记录项的一个副本。它的第一个参数和第三个参数是内核对象句柄。这两个句柄本身必须相对于调用DuplicateHandle函数的那个进程。此外,这两个

                  参数标示的必须是进程内核对象。第二个参数是指向任何类型的内核对象的句柄。但是,它的句柄值一定不能与调用DuplicateHandle 函数的那个进程相关,相反,该句柄必须与第一个参数句柄所标示的那个进程相关。函数会将源进程中的

                  句柄信息复制到第三个参数所标示进程的句柄表中。

                  通常在只涉及两个进程的时候,才会调用DuplicateHandle函数:

                  HANDLE  hObjInProcessS = CreateMutex(...);

                  HANDLE  hProcessT = OpenProcess(...);

                  HANDLE  hObjInProcessT ;

                  DuplicateHandle( GetCurrentProcess(),  hObjInProcessS,  hProcessT , &hObjInProcessT ,  0, FALSE,  DUPLICATE_SAME_ACCESS);

                  CloseHandle(hProcessT);

                  CloseHandle(hObjInProcessS);

 

                  还可以在同一个进程中使用这个函数为现有对象创建一个新句柄,并未这个新句柄指定新的权限。只要在第三个参数传递 GetCurrentProcess(), 该函数会返回一个伪句柄,该句柄始终标示主调进程。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值