- 注册时间
- 2011-3-6
- 最后登录
- 1970-1-1
该用户从未签到
|
有三种不同的机制允许进程共享内核对象:
1、使用对象句柄继承
2、为对象命名
3、复制对象句柄
一、使用对象句柄继承
进程之间的关系为父子进程关系的时候才能继承。实质为:父进程在创建子进程的时候,如果需要继承对象句柄,那么系统的扫描父进程的句柄表,将需要继承的对象句柄表中的信息,复制到子进程的句柄表中,然后通过某种方式将句柄值传给子进程,这样子进程就可以使用的这内核对象了。在这儿父进程、子进程对于内核对象而言是相同级别的,对两个进程而言,他们得到的句柄值是相同的,而且指向的是内核中的相同的一个对象。
所以我们可以看出,在这儿继承的不是对象,而仅仅是对象的句柄!!!
那么我们如何去设置呢?
首先我们要知道,实现继承必须要有三个开关:
1、创建内核对象时,有个参数开关 //针对于某个对象
2、父进程句柄表的记录项中有个标志位的开关 //针对于某个对象
3、父进程创建子进程的时候,有个参数开关 //针对于子进程(所有的可继承对象)
那么我们分别来看:
1、创建内核对象时,有个参数开关
父进程必须分配并初始化一个SECURITY_ATTRIBUTES结构,然后将这个结构传给Create函数:
SECURITY_ATTRIBUTES sa ;
sa.nLength = sizeof( sa );
sa.lpSecurityDescriptor = NULL ; //默认安全属性
sa.bInhertHandle = TRUE ; //make the returned handle interitable 可继承的
HANDLE hMutex = CreateMutex ( &sa , FLASE , NULL ) ;
2、父进程句柄表的记录项中有个标志位的开关
那就是句柄表中每一个记录项中保存的标志:
如果标志位0x00000000则表示此对象不可继承 如果标志位为0x00000001表示可以继承。
在这儿用函数SetHandleInformation在程序中可以改变句柄标志,
宏定义两个标志
#define HANDLE_FLAG_INHERIT 0x00000001 //打开继承标志
#define HANDLE_FLAG_RPOTECT_FROM_CLOSE 0x00000002 //标志此对象不允许用CloseHandle关闭句柄
BOOL SetHandleInformation (
HANDLE hObject , //标识某个句柄
DWORD dwMask , //表示我们想要修改哪个标志 有两个标志可以再这儿修改
DWORD dwFlags ) ;
例如
SetHandleInformation (hObj , HANDLE_FLAG_INHERIT , HANGDLE_FLAG_INHERIT ) ; 打开继承
SetHandleInformation (hObj , HANDLE_FLAG_INHERIT , 0) ; 关闭继承
SetHandleInformation (hObj , HANDLE_FLAG_RPOTECT_FROM_CLOSE ,
HANDLE_FLAG_RPOTECT_FROM_CLOSE) ; 不允许关闭
CloseHandle(hObj); //这样会引发异常 因为标志设为不允许关闭!!!
注:还有一个函数GetHandleInformation可以在程序中查询当前标志位!!!
3、父进程创建子进程的时候,有个参数开关 CreateProcess函数
BOOL CreateProcess(
PCTSTR pszApplicationName ,
PTSTR pszCommandLine , //命令行参数
PSECURITY_ATTRIBUTES psaProcess ,
PSECURITY_ATTRIBUTES psaThread ,
BOOL bInheritHandles , //设置子进程是否可以继承的属性 这又是一个开关!!!
DWORD dwCreationFlags ,
PVOID pvEnvironment ,
PCTSTR pszCurrentDirectory ,
LPSTARTUPINFO pStartupInfo ,
PPROCESS_INFORMATION pProcessInformation ) ;
最后这儿有一个问题:子进程是继承了父进程的对象,复制工作操作系统也都已经完成,但是子进程还不知道所继承的对象的句柄呢...这个问题的解决方案是:在创建子进程的时候,将所继承的句柄通过命令行参数传进去,让子进程取得这个句柄!!!
二、为对象命名
只是许多(而不是全部)内核对象可以进行命名,必须为对象指定一个名称才能用这种方法实现对象的共享。
如 CreateMutex CreateEvent CreateSemphore CreateWaitableTimer CreateFileMapping CreateJobObject等,这些函数的最后一个参数是pszName。如果向这个参数传入NULL,那么这个对象就是匿名的,如果传入一个名称,那么这个对象就是有名称的。
实质:例如A进程调用CreateMutex
HANDLE hMutexPeocessA = CreateMutex ( NULL, FLASE, TEXT("AAA") ) ;
当B进程也调用CreateMutex时 HANDLE hMutexPeocessB = CreateMutex ( NULL, FLASE, TEXT("AAA") ) ;
系统首先会查看是否存在一个名为“AAA”的内核对象,如果不存在,那么创建,如果存在,内核会接着检查对象的类型,如果类型相符(A的是互斥,B也想创建互斥),那么这时候系统会进行安全检查,验证调用者是否拥有对该对象的完全访问权限。如果有权限,那么系统会再B进程的句柄表中找到一个空白位置,初始化这个记录项指向现有的那个对象。
特点:1、A进程和B进程得到的两个句柄值很有可能不相等,但是两者都指向的是同一个内核对象
2、B进程可以不是A进程的子进程。
用这个方式为对象命名的时候,出现的情况是,如果要创建的对象没有,则会创建,如果要创建的对象存在,则实现共享。
但是后者才是我们想实现的,即我们想仅仅实现共享。
所以在这儿我们可以调用OpenMutex OpenEvent OpenSemphore OpenWaitableTimer OpenFileMapping OpenJobObject这些函数的最后一个参数也是pszName,用这些函数可以实现,如果要打开的对象没有,则会返回失败,如果要打开的对象存在,则实现共享。
三、复制对象句柄
跨进程边界共享内核对象的最后一招是 使用 DuplicateHandle函数:
这个参数的第一个和第三个是进程内核对象,必须是相对于调用这个函数的那个进程(需要理解)
BOOL DuplicateHandle (
HANDLE hSourceProcessHandle ; //源进程的进程内核对象,是进程内核对象 传进其他内核对象会调用失败
HANDLE hSourceHandle ; //想要复制的内核对象的句柄,这个句柄和本进程无关,是源进程中的内核对象
HANDLE hTargetPeocessHandle ; //目标进程的进程内核对象 传进其他内核对象会调用失败
PHANDLE phTargetHandle ; //得到新复制的内核对象在本进程中体现的句柄值
DWORD dwDesiredAccess , //设置新复制的对象的访问掩码
BOOL bInheritHandle ; //设置新复制的对象的继承标志
DWORD dwOptions ) ; //这个参数的设定和上面两个参数相关联,这个参数可以设置为0或
//DUPLICATE_SAME_ACCESS或DUPLICATE_CLOSE_SOURCE
//如果这个参数设置为0,则上面两个参数就会起作用.
//如果这个参数设置为DUPLICATE_SAME_ACCESS,那么目标句柄会获得和源进程的句柄一样的访问掩码
//而且如果设置这个参数的话,程序将不会在看其他两个参数
//如果这个参数设置为DUPLICATE_CLOSE_SOURCE,会关闭源进程中的句柄,如果是这样,内核对象的
//计数就不会增加,刚好加1又减一,注意:1、这儿是关闭的源进程的对应的对象的句柄
// 2、DUPLICATE_SAME_ACCESS和DUPLICATE_CLOSE_SOURCE也可以联合使用
简单的说,这个函数就是获得一个进程的句柄表中的一个记录项,然后在另一个进程的句柄表中创建一个副本。 在这儿两个进程得到的某对象的句柄值也不一定相等,复制的是句柄表中记录项,而不是复制的句柄值
例子:进程S是源进程,它拥有一个对象的访问权。进程T是目标进程,它想要获得这个内核对象的访问权。进程C则是一个催化剂过程,它执行DuplicateHandle 这个函数。见《windows核心编程》P60
|
|