royrui 2010-05-04
今天想把看的第三章的心得写下来。
这章主要介绍了内核对象。在WINDOWS中,内核对象是由操作系统来管理,尽管用户能够创建它,但是在创建之后,用户对该内核对象的操作都是委托给操作系统。
通常用户创建和使用的对象分为:用户对象(如 HICON CreateIcon(prama))、GDI对象函数以及内核对象。像访问令牌、事件对象、文件对象、文件映像对象、进程对象、信号量等都属于内核对象。在内核对象第一次被创建的是,系统会根据给定的参数初始化,其中有一个参数:PSECURITY_ATTRIBUTES,是SECURITY_ATTRIBUTES的指针,SECURITY_ATTRIBUTES的结构如下:
typedef struct _SECUTIRTY_ATTRIBUTES { DWORD nLength;//length of itself LPVOID lpSecurityDescirptor;//安全描述符 BOOL bInheritHandle;//表明该内核对象是否可以被继承 } SECURITY_ATTRIBUTES;
并返回一个该内核对象的句柄给创建函数所在的进程(首先要在该进程的句柄表中,找到一块空白的位置,存放该内核对象所在的地址)。
以后如果自己进程的其他线程的函数创建该内核对象时,操作系统会将该内核对象的计数器加一,然后返回该内核对象的一个句柄(对于中间可能出项的异常,就不在讨论)。关闭内核对象,类似地,操作系统会先通过传入的对象的句柄在进程的句柄表中验证该对象确实有权限访问它。然后进行相反的操作,当该内核对象的计数器为0时,操作系统会自动销毁它。
然而,在WINDOWS内部,内核对象的句柄是与进程相关的。但是,进程间有时候必须要共享内核对象,解决这个问题,有几种方法:
首先是使用对象句柄继承。也就是说,对于父子进程间,可以在父进程中创建该内核对象的时候,设置它的bInheritHandle值为TRUE。然后使用CreateProcess创建子进程的时候,同样需要设这个值为TRUE。当然也可以在父进程中设置一个变量的方法来实现。
如果想让某个进程的子进程部分由继承该内核对象,可以通过SetHandleInformationg来设置,该内核对象是否能够被继承。
BOOL SetHandleInformation( HANDLE hObject, DWORD dwMask, DWORD dwFlags ) /* *@hObject:标志一个有效地句柄 *@dwMask:告诉函数我想修改那个或者哪些标志,目前每个句柄都关联了两个标志 */ #define HANDLE_FLAG_INHERIT 0x00000001 //告诉句柄是否需要继承 #define HANDLE_FLAG_FROM_CLOSE 0X00000002 //告诉句柄是否允许关闭句柄
通过上面的方式,就可以让内核对象可选择性的被继承。
第二种方式是为对象命名。事实上,大多数的内核对象都可以命名。例如:
HANDLE hMutex= CreateMutex(NULL, FALSE, TEXT("test")); //创建一个叫做“test”的互斥量内核对象,并获得它的句柄 HANDLE hMutex1 = OpenMutex(NULL,FALSE,Text("test")); //获得一个叫做“test”的互斥量对象 if(GetLastError()==ERROR_FILE_NOT_FOUND){ //说明没有找到“test” }else if(GetLastError()==ERROR_INVALID_HANDLE){ //找到,但是类型不同 } //上面两种情况下,hMutex1=NULL //如果找到,且匹配,则会查看OpenMutex的第一个参数:dwDesirdAccess,看他想要的权限是否是允许的
最后一种方式是复制对象的句柄。使用DuplicateHandle函数:
BOOL DuplicateHandle ( HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, PHANDLE hTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions ); //这个函数是获得一个进程的句柄表的一条记录,然后copy到目标进程的进程句柄表中
考虑这样一个场景,假设进程S能够访问一个对象,并希望进程T也能够访问,那么就可以在S中调用DuplicatieHandle函数:
//create a mutex object accessible by Process S HANDLE hObjInProcessS = CreateMutex(NULL, FALSE,NULL); //get a handle to Process T's kernel object //dwProcessIdT 就是T的进程标识符,假设已经获得 HANDLE hProcessT = OpenProcess(PROCESS_ALL_ACCESS,FLASE, dwProcessIdT); HANDLE hObjInProcessT; //give T access to our hObjInProcessS DuplicateHandle(GetCurrentProcess(),hObjInProcessS, hProcessT, &hObjInProcessT, 0, FALSE, DUPLICATE_SAME_ACCESS); //通过进程间通信的方式,是T获得这个句柄。 CloseHandle(hProcessT); CloseHandle(hProcessS);
注意:进程S中永远不要调用CloseHandle(hObjInProcessT);另外,这种方式存在潜在的失败风险,如果进程S中的hObjInProcessT的句柄值已经被另外的内核对象占据,那么会强行将先前的内核对象关闭。
未完待续……