内核对象

来源:互联网 发布:上海行知小学官网 编辑:程序博客网 时间:2024/04/30 07:37
 windows 核心编程
    Jeffrey Richter
chapter 3     内核对象

1.什么是内核对象
    内核对象是内核分配的一个内存块,并且只能由该内核访问。该内存块是一种数据结构,其成员负责维护该对象的各种信息。
内核对象的数据结构只能被内核访问,应用程序无法在内存中找到这些数据结构并直接改变它们的内容。

1.1内核对象的引用计数
    内核对象由内核拥有,而不是进程。内核建立引用计数。

1.2安全性
    内核对象能得到安全描述符的保护。安全描述符描述了谁创建了该对象,谁能访问或使用该对象,谁无权访问该对象。
typedef struct _SECURITY_ATTRIBUTES{
    DWORD    nLength;
    LPVOID    lpSecurityDescriptor;
    BOOL     bInheritHandle;
}SECURITY_ATTRIBUTES;

eg.     SECURITY_ATTRIBUTES sa;
    sa.nLength= sizeof(sa);
    sa.lpSecurityDescriptor=pSD;
    sa.bInheritHandle=FALSE;
    HANDLE     hFileMapping=CreatFileMapping(INVALID_HANDLE_VALUE,&sa,
                    PAGE_READWRITE,0,1024,"MyFileMapping");

    除了内核对象外,还有其它类型的对象。如菜单、窗口、光标、刷子、字体等。这些对象是GDI对象,不是内核对象,没有安全属性。


2.进程的内核对象句柄表
    进程被初始化时,系统为其分配一个句柄表。句柄表是一个数据结构的数组,每个结构都有一个指向内核对象的指针、一个访问屏蔽和一些标志。

2.1创建内核对象
    列举创建内核对象的一些函数:
    HANDLE CreateThread(PSECURITY_ATTRIBUTES psa,DWORD dwStackSize,
            LPTHREAD_START_ROUTINE pfnStartAddr,PVOID pvParam,
            DWORD dwCreationFlags,PDWORD pdwThreadID);

    HANDLE CreateFile(PCTSTR pszFileName, DWORD dwDesiredAccess,DWORD dwShareMode,
            PSECURITY_ATTRIBUTES psa,DWORD dwCreationDistribution,
            DWORD dwFlagsAndAttributes,HANDLE hTemplatesFile);
    HANDLE CreateFileMapping(…);
    HANDLE CreateSemaphore(…);

    用于创建内核对像的所有函数均返回与进程相关的句柄,这些句柄可以被要相同进程中运的任何或所有线程成功地加以使用。该句柄值实际是放入进程的句柄表的索引,它用于标识内核对象的信息存放的位置。   
   
    一般如果建立不成功,都返回NULL,但也有返回INVALID_HANDLE_VALUE(CreateFile)

2.2关闭内核对象
    关闭内核对象用 CloseHandle()。

    将无效句柄传给CloseHandle(),将出现两种情况之一。若进程运行正常,CloseHandle()返回FALSE,而GetLastError()返回ERROR_INVALID_HANDLE;若进程正在排除错误,则系统将通知调试程序。

    CloseHandle()返回之前将会清除进程的句柄表中的项目。

    忘记调用CloseHandle()可能造成资源泄漏。但当进程中止,系统将收回资源。

3.跨越进程边界共享内核对象

3.1对象句柄的可继承性
    只有当进程具有父子关系时,才可以继承。要实现这种继承性,首先父进程创建子进程时必须指明它希望对象的句柄是个可继承的句柄。要创建可继承的句柄,必须指定一个SECURITY_ATTRIBUTES结构并对它初始化。
eg.    SECURITY_ATTRIBTES sa;
    sa.nLength=sizeof(sa);
    sa.lpSecurityDescriptor=NULL;
    sa.bInheritHandle=TRUE;
   
    HANDLE hMutex=CreateMutex(&sa,FALSE,NULL);

    其次,让父进程创建子进程时使用CreateProcess()时将bInheriteHandle设为TRUE。这样,系统将把父进程中可继承的句柄的项目复制到子进程的句柄表中,同时增加对象的引用计数。在父子进程中,标识内核对象所用的句柄值是完全一样的。

   对象句柄的继承性有个特征:当使用它时,子进程不知道它已经继承了任何句柄。子进程为了确定它期望的内核对象的句柄值,最常用的方法是将句柄值作为一个命令参数递给子进程。该子进程的初始化代码对命令行进行分析(如用sscanf()),并取出句柄。可以这样做的原因是父子进程中共享内核对象的句柄值是一样的。
    其它的方法如父进程等待子进程完成初始化(使用WaitForInputIdle()),然后发送一条消息给子进程的某线程。
    第三种方法是父进程将一个环境变量加到环境程序块中。该变量的名字是子进程知道要查找的某种信息,而变量的信息则是内核对象要继承的值。可以用GetEnviromentVariable()获取环境变量。

3.2改变句柄的标志
    有时父进程只想让一个子进程继承内核对象的句柄。可以调用SetHandleInformation()来改变句柄的继承标志。
    BOOL SetHandleInformation(HANDLE hObject,DWORD dwMask,DWORD dwFlags);

    目前,dwMask可以是HANDLE_FLAG_INHERIT,HANDLE_FLAG_PROTECT_FROM_CLOSE.关闭一个受保护的句柄会产生一个异常条件。将句柄保护起来一般没用,但父进程生成子进程,子进程生成孙进程时可能有用。

    可以用GetHandleInformation()得到HANDLE的信息。
eg.    DWORD dwFlags;
    GetHandleInformation(hObj,&dwFlags)
    BOOL fHandleInheritable=(0!=(dwFlags & HANDLE_FLAG_INHERIT));

3.3命名对象
    共享对象时可以将对象命名。使用命名对象时,不要求对象是可继承的。另一个进程可以用Create*()或者Open*()来取得句柄。

eg.    HANDLE hMetux= CreateMetux(NULL,FALSE,"metux");
    if (GetLastError()==ERROR_ALREDY_EXISTS)
    {    //DO SOMETHING;
    }
    else
    {    //DO SOMETHING ELSE;
    }
   
    若对象不存在,Create*()将创建之,而Open*()将失败。

3.4复制对象
    共享对象的最后一个方法是用DuplicateHandle()
    BOOL DuplicateHandle(HANDLE hSourceProcessHandle,HANDLE hSourceHandle,HANDLE hTargetProcessHandle,
            PHANDLE phTargetHandle,DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwOptions);
   
    该函数实际上涉及三个进程:SourceProcess,TargetProcess和调用DuplicateHandle()的进程。但实际上两个进程之间的调用为多。
eg.    //process S exexutes the following code, sharing the handle with process T.
    HANDLE hProcessT= OpenProcess( PROCESS_ALL_ACCESS,FALSE,dwProcessIDT);
   
    HANDLE hObjProocessT;          //an uninttialized handle relative to process T;

    DuplicateHandle( GetCurrentProcess(),hObjProcessS,hProcessT,&hObjProocessT,0,FALSE,DUPLICATE_ALL_ACCESS);
   
    //do something;

    CloseHandle(hProcessT);
    CloseHandle(hObjProcessS);

    下面是DuplicateHandle()的另一种用法。假设一进程拥有对一个文件映射对象的读写访问权,可以使用DuplicateHandle()为现有对象复制一个句柄,并确保该句柄拥有只读访问权。
eg.    HANDLE hFileMapRW= CreateFileMapping(INVALID_HANDLE_VALUE,NULL, PAGE_READWIRITE,0,1024,NULL);

    HANDLE hFileMapRo;
    DuplicateHandle(GetCurrentProcess(),hFileMapRW,GetCurrentProcess(),&hFileMapRo,FILE_MAP_READ,FALSE,0);

    //do something using hFileMapRo, READ ONLY;
   
    CloseHandle(hFileMapRo);
   
原创粉丝点击