windows核心编程学习笔记1-内核对象
主要的内核对象
§进程(Process)
§线程(Thread)
§作业(Job)
§可等待定时器(Timer)
§文件(File)
§信标(Semphore,Event)
§互斥对象(Mutex)
§控制台输入(Console Input,Output)
每个内核对象都只是一个内存块。它由操作系统内核分配,并只能由操作系统内核访问。这个内存块是一个数据结构,其成员维护者与对象相关的信息。少数成员(安全描述符和使用计数等)是所有对象都有的,但是其他大多数成员都是不同类型的对象特有的。
通过句柄来操作内核对象
句柄值是进程相关的
所有内核对象都有计数成员,对象创建时为1,另一个进程对其访问时累加,进程终止时递减,当计数为0时操作系统内核就会销毁该对象
安全描述符
安全描述符描述了谁(通常是对象的创建者)拥有对象;哪些组合用户被允许访问或使用此对象;哪些组和用户被拒绝访问此对象。
大多数应用程序只是为这个参数传入NULL,这样创建的内核对象具有默认的安全性——具体包括哪些默认的安全性,取决于当前进程的安全令牌(security token)。也可以分配一个SECURITY_ATTRIBUTES结构,并对其初始化,再传入这个参数。
typedef struct _SERCURITY_ATTRIBUTES { DWORD nLength; /*将此值设置为该结构的大小*/ LPVOID lpSecurityDescriptor; /*对该结构的 SECURITY_DESCRIPTOR 指针控制对对象的访问。 如果该成员的值是 Nothing ,对象分配默认安全说明符与调用来访问标记为已处理。这与授予对每个人的输入通过将 Nothing 自由访问 (DACL)控制列表 (acl)。在进程中访问标记的默认 DACL 只允许访问访问标记表示的用户*/ BOOL bInheritHandle; /*指定的布尔值返回的句柄是否继承,在更新过程中创建。 如果此字段为 true,更新过程继承处理*/ }SERCURITY_ATTRIBUTES;
初始化
SERCURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = pSD; sa.bInheritHandle = FALSE; HANDLE hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, 1024, TEXT("MyFileMapping"));
UAC(用户账户控制)会强制应用在一个受限的上下文运行。
句柄表
索引 内核对象内存块的指针 访问屏蔽(标志位的DWORD) 标志(标志位的DOWRD)
1 0x???????? 0x???????? 0x????????
2 0x???????? 0x???????? 0x????????
...
进程句柄表
内核对象的关闭
调用CloseHandle结束使用对象‘
如果传给CloseHandle函数的是一个无效的句柄,可能发生两种情况:如果进程是正常运行的,CloseHandle将返回FALSE,而GetLastError返回ERROR_INVALID_HANDLE。如果进程正在被调试,那么系统将抛出0xC0000008异常(“指定了无效的句柄”)。
句柄变量作为参数,调用CloseHandle函数后还应设置为NULL。如果此时用这个变量调用一个Win32函数可能发生两种意外情况:
1.由于变量引用的句柄表记录项已被清除,所以Windows会接受到一个无效的参数并报告错误。
2.要函数调用时,一旦错误地用这个尚未设为NULL的变量,就可能会定位到一个错误类型的内核对象(这时会报错)。还可能会定位到一个类型(和已经关闭的内核对象)相同的内核对象(不会报错),这是应用程序的状态将损坏,没有任何办法可以恢复。
跨进程边界共享内核对象情况:
利用文件映射对象,可以在同一台机器上运行两个不同进程之间共享数据块。
借助邮件槽和命名管道,在网络中的不同计算机上运行的进程可以相互发送数据库。
互斥量、信号量和事件运行不同进程中的线程同步执行。
允许进程共享内核对象的机制:
使用对象句柄的继承、为对象命名、复制对象句柄
- 使用对象句柄的继承
父子进程间才能使用对象句柄继承,创建一个可继承的句柄需要父进程分配并初始化一个SECURITY_ATTRIBUTES结构,把这个结构的地址传给具体的Create函数。
- 改变句柄的标志
控制部分子进程能继承内核对象句柄,可以调用SetHandleInformation函数来改变内核对象句柄的继承对象标志。
BOOL SetHandleInformation(
HANDLE hObject,
DWORD dwMask,
DWORD dwFlags);
hObject 标识了一个有效的句柄,dwMask指出需要更改哪些标志,dwFlag指出希望将标志设为多少
打开一个内核对象句柄的继承性
SetHandleInformation(hObj,HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);
关闭这个标志
SetHandleInformation(hObj,HANDLE_FLAG_INHERIT,0);
- 为对象命名规范
共享方式:创建为Create*函数,打开为Open*函数。
在调用Create*函数时参数pszName用于对内核对象命名,传入NULL时创建一个匿名对象。
对象都共享一个命名空间,无法保证当前不存在同名对象,创建一个同名而不同类型的内核对象时仅会获得ERROR_INVALID_HANDLE错误,同名且同类型时会获得ERROR_ALREADY_EXISTS。
利用创建函数共享:A进程创建并命名对象后,B进程创建相同类型且同名对象时,会先查看是否存在同名对象,再检查其类型,都一致时会验证调用者是否拥有该对象的完全访问权限。全部检查完毕后,B进程的句柄表中分配一个 指向该对象的句柄值(引用该对象,句柄值是对B进程而言,与该对象在A进程中的句柄值并不相同)。此时B进程调用的创建函数中的参数并无作用。
利用打开函数共享:B进程打开A进程创建的命名对象时,没有这个名称对象时返回NULL,获得2(ERROR_FILE_NOT_FOUND)。名称存在但类型不同,返回NULL,获得6(ERROR_INVALID_HANDLE)。都相同,检查请求的访问(dwDesiredAccess)是否允许。
对象句柄的继承性
§只有当进程具有父子关系时,才能使用对象句柄的继承性
§父进程创建子进程,为子进程赋予对父进程内核对象对象的访问权
§内核对象句柄如果能被继承,必须在创建内核对象的时候把安全描述符的成员赋值:
•bInherritHandle=TRUE
§父进程创建子进程的时候CreateProcess函数的bInheritHandle参数必须指定为TRUE
•通常把内核对象句柄作为进程启动命令行参数(CreateProcess的pszCommondLine)传递给子进程