• Windows核心编程 第3章 内核对象


    3.1 什么是内核对象

    内核对象就是内核中的一块内存,是一个结构,并且只能由内核对象访问,应用程序只能通过调用Windows提供的函数来操作内核对象。每个内核对象都有相同的部分比如安全属性和使用计数器。

    3.1.1 内核对象的使用计数

    内核对象中的使用计数和进程无关,当进程第一次创建某个内核对象时候使用计数变为1,当另一个进程也调用此内核对象时计数变为2。当进程释放时或者关闭内核对象时(CloseHandle),内核的使用计数减去1,如果使用计数不为0的话,内核不会释放此内核对象。

    3.2.2 安全性

    内核对象能够得到安全描述符的保护,安全描述符定义了谁能够创建,访问和使用该对象,一般在服务器代码中使用,客户端可以忽略。

    所有创建内核对象的函数的参数都有一个指向SECURITY_ATTRIBUTES结构的指针。

    typedef struct _SECURITY_ATTRIBUTES {
        DWORD nLength;
        LPVOID lpSecurityDescriptor;
        BOOL bInheritHandle;
    } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;

    只有

    lpSecurityDescriptor

    成员和安全属性有关。一般此参数传递NULL,表示默认的安全描述。如果需要:

    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = FALSE;
    HANDLE h = CreateMutex(&sa, FALSE, "XI");
    其余进程可用用OpenMutex函数打开,如果权限可以就返回句柄,如果失败返回NULL,GetLastError被设置为ERROR_ACCESS_DENIED。
    Windows除了内核对象之外还有GDI和用户对象,区分它们的简单办法就是,创建函数中带有安全描述符参数的就是内核对象。
     
    3.2 进程的内核对象句柄表

    索引                         内核对象内存块得指针                             访问屏蔽(标志位的DWORD)                      标志(标志位的DWORD)


    1                               0x????????                                                0x????????                                                        0x????????
    2                               0x????????                                                0x????????                                                        0x????????
    …                               …                                                               …                                                                       …

    3.2.1 创建内核对象

    调用Create&函数族来创建相应的内核对象,返回的是内核对象的句柄(也有个说法就是句柄表的索引),如果创建失败一般会返回0(NULL),也有的会返回INVALID_HANDLE_VALUE=-1,比如CreateFile失败后会返回后者,失败的原因有可能是内存不足或者是安全问题等等。其他对内核操作的函数都需要此句柄值作为参数传递进去,如果传递一个无效的句柄进去,那么GetLastError函数的值将被置为6(ERROR_INVALID_HANDLE)。

    3.2.2 关闭内核对象

    BOOL CloseHandle(HANDLE hobj);

    调用此函数,系统会了清理进程的句柄表中的对应项目,如果使用计数器为0,内核释放该内核对象的资源,如果使用计数器不为0,说明其他进程还在使用此内核对象,则不释放资源。

    当进程忘记调用CloseHandle函数,可能造成内存泄露,但是当进程结束的时候资源一样会被释放。

    如果传递的参数无效,则函数返回FALSE,并且GetLastError函数的值被设置成ERROR_INVALID_HANDLE。如果是DEBUG阶段,则返回错误信息。

     
    3.3 跨越进程边界共享内核对象
    3.3.1 对象句柄的继承性
    1. 在父进程创建子进程的时候,将参数SECURITY_ATTRIBUTES结构的Inherithandle字段设置为TRUE的话,再父进程句柄表中标示该内核对象的项的标志位的值将会变成TRUE,标示该内核对象是可以让子进程继承的,具有可继承性(仅仅标示 该句柄值具有可继承性,而内核对象没有可继承性)。
    2. 将CreateProcess的参数bInherithandle参数的值设置为TRUE,标示创建的进程可以继承有继承性的父进程句柄。
    3. 子进程创建后不会先加载程序,它先搜索父进程的句柄表将有继承性的项目原封不动的拷贝给自己(索引也没有变,所以句柄值也不变)。
    4. 父进程可以有3种方式将句柄值传递给子进程,参数传递,进程间通信和环境变量(GetEnvironmentVariavle函数解析)。
    BOOL
    WINAPI
    CreateProcess(
        __in_opt    LPCSTR lpApplicationName,
        __inout_opt LPSTR lpCommandLine,
        __in_opt    LPSECURITY_ATTRIBUTES lpProcessAttributes,
        __in_opt    LPSECURITY_ATTRIBUTES lpThreadAttributes,
        __in        BOOL bInheritHandles,
        __in        DWORD dwCreationFlags,
        __in_opt    LPVOID lpEnvironment,
        __in_opt    LPCSTR lpCurrentDirectory,
        __in        LPSTARTUPINFOA lpStartupInfo,
        __out       LPPROCESS_INFORMATION lpProcessInformation
        );

    注意:子进程再创建其子进程,如果满足上面方式,可以继续继承。如果父进程再创建子进程后,再创建句柄,子进程不会被继承。3.3.2 改变句柄标志

    BOOL SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);
    改变句柄的标志,目前可改变的标志有两种
    #define HANDLE_FLAG_INHERIT   0x00000001  // 继承标志
    #define HANDLE_FLAG_PROJECT_FROM_CLOSE   0x00000001 // 保护不允许关闭句柄标志
    可以用OR操作同时设置2个标志。第一个参数是要设置的句柄值,第二个就是要改变的标志,第三个参数是将标志改编成什么值。
    BOOL GetHandleInformation(HANDLE hObkect, PDWORD pdwFlags);
    获取当前句柄的标志的值。
    // 设置句柄值可继承:
    SetHandleInformation(hObject, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
    // 设置句柄不可继承:
    SetHandleInformation(hObject, HANDLE_FLAG_INHERIT, 0);
    // 设置句柄值不可关闭,受保护:
    SetHandleInformation(hObject, HANDLE_FLAG_PROJECT_FROM_CLOSE, HANDLE_FLAG_PROJECT_FROM_CLOSE);
    // 设置句柄值可关闭,不受保护:
    SetHandleInformation(hObject, HANDLE_FLAG_PROJECT_FROM_CLOSE, 0);
    3.3.3 命名对象
    创建内核对象函数族Create&中的最后一个参数是pszName,该参数是如果传递NULL,表示是匿名内核对象,可以通过其他俩种方式来使用其他进程的内核对象。当pszName参数传递以’\0’(最多长度为MAX_PATH 260字符)结尾的字符串时,表示启用命名对象,比如进程A调用CreateMutex(NULL, FALSE, “XI”)的时候,他将创建内核对象名字为“XI”,之后某一时刻如果进程B也调用CreateMutex(NULL, FALSE, “XI”)函数他将经过以下几步:
    1. 判断内核对象名称是否相同。
    2. 判断内核对象类型是否相同,如果名字相同但是类型不相同则Create&函数族返回NULL,GetLastError函数值为6(ERROR_INVALID_HANDLE)。
    3. 判断安全性,返回同2步,GetLastError值同2步。
    4. 如果验证通过则返回句柄(返回句柄的值和该内核对象其他句柄的值不一定相同),GetLastError的值等于ERROR_ALREADY_EXISTS。

    也可以用Open&函数族来打开已经创建的句柄,成功后GetLastError也不会被设置。具体如下

    HANDLE Open&(DWORD, BOOL, PCSTR);

    第一个参数:表示访问权限。

    第二个参数:表示新创建的句柄是否有继承性(注意不是内核对象!)。

    第三个参数:不能传递NULL。如果该句柄不存在则返回NULL,GetLastError被设置为2(ERROR_FILE_NOT_FOUND)。

    3.3.4 终端服务器的名字空间

    Globad,Local,Session程序保留关键字,具体的没弄明白,理解的就是说当服务器的时候,客户端可以访问以这些名字开头的内核对象。

    3.3.5 复制对象句柄

    BOOL DuplicateHandle(
    HANDLE hSourceProcessHandle,
    HANDLE hSourceHandle,
    HANDLE TargetProcessHandle,
    PHANDLE phTargetHandle,
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    DWORD dwOptions);

    执行DuplicateHandle函数的进程为ProcessC,原进程为ProcessS,目标进程为ProcessT。则hSourceProcessHandle为进程ProcessS的进程句柄,TargetProcessHandle为进程ProcessT的进程句柄,ProcessC将句柄hSourceHandle从ProcessC拷贝到ProcessT中,值存在phTargetHandle中,dwDesiredAccess新句柄的反问权限,bInheritHandle新句柄的继承性,参数dwOptions有两种类型分别是:

    DUPLICATE_SAME_ACCESS忽略参数dwDesiredAccess,新句柄和原进程句柄具有相同的反问权限。

    DUPLICATE_CLOSE_SOURCE关闭ProcessS中的拷贝句柄,内核对象的计数不变。

    HANDLE hObjProcessS = CreateMutex(NULL, FALSE, NULL);
    HANDLE hProcessT = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessIdT);
    HANDLE hObjProcessT;
    DuplicateHandle(GetCurrentProcess(), hObjProcessS, hProcessT , &hObjProcessT, 0, FALSE, DUPLICATE_SAME_ACCESS);
    CloseHandle(hObjProcessS);
    CloseHandle(hProcessT);

    注意:
    一般DuplicateHandle函数没有在三个进程中使用,因为很难知道原进程的句柄值。
    要使用IPC机制通知目标进程,新句柄已经拷贝过去。

     
     
     
     
     
  • 相关阅读:
    学习笔记Jmail收发邮件
    ModalPopup
    学习笔记:UpdatePanel控件
    转AjaxControlToolkit的安装与使用详解
    转linq中的Single()、First()、Take(1) LINQ 标准的查询操作符 排序 orderby、thenby、Take
    转Global.asax文件
    转<%%>、<%=%>、<%$%>、<%@%>的区别
    C++文件包含处理—#include
    GISer学习之道(转载)
    OSG中的示例程序简介
  • 原文地址:https://www.cnblogs.com/xi52qian/p/1967510.html
Copyright © 2020-2023  润新知