对象句柄继承
第一步把设置内核对象security attribute结构块的bInheritHandle参数为 TRUE
typedef struct _SECURITY_ATTRIBUTES
{
DWORD nLength,
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES;
他会设置句柄表中的句柄标志,来说明该内核对象希望被继承
第二部创建子线程的时候设置bInheritHandle参数
BOOL CreateProcess(
PCTSTR pszApplicationName,
PTSTR pszCommandLine,
PSECURITY_ATTRIBUTES psaProcess,
PSECURITY_ATTRIBUTES psaThread,
BOOL bInheritHandles,
DWORD fdwCreale,
PVOIO pvEnvironment,
PCTSTR pszCurDir,
PSTARTUPINFO psiStartInfo,
PPROCESS_INFORMATION ppiProcInfo);
当bInheritHandle设置为TRUE的时候,子进程会拷贝父进程句柄表中被标记为继承的句柄信息。这样子进程中和父进程就可以访问同样的内核对象了
设置句柄信息
BOOL SetHandleInformation(
HANDLE hObject,
DWORD dwMask,
DWORD dwFlags);
第一个参数是你想想改句柄
第二个参数是你想想改的参数
#define HANDLE FLAG_INHERIT 0x00000001
#define HANDLE FLAG PROTECT FROM CLOSE 0x00000002
第三个参数为你想修改成什么
例如
打开继承标志
SetHandleInformation(hobj,
HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
关闭继承标志
SetHandleInformation(hobj, HANDLE_FLAG_INHERIT, 0);
HANDLE FLAG PROTECT FROM CLOSE标记被设定。那么如果关闭这个收保护的句柄那么会出现异常。很少想要将句柄保护起来,使他人无法将它关闭。但是如果一个进程生成了子进程,而子进程又生成了孙进程,那么该标志可能有用。父进程可能希望孙进程继承赋予子进程的对象句柄。不过,子进程有可能在生成孙进程之前关闭该句柄。如果出现这种情况,父进程就无法与孙进程进行通信,因为孙进程没有继承该内核对象。通过将句柄标明为“受保护不能关闭”,那么孙进程就能继承该对象。
但是这种处理方法有一个问题。子进程可以调用下面的代码来关闭HANDLE_FLAG_PROTECT_CLOSE标志,然后关闭句柄。
SetHandleInformation(hobj, HANDLE_FLAG_PROlECl_FROM_CLOSE, 0);
CloseHandle(hobj);
讲一下G e t H a n d l e I n f o r m a t i o n 函数的情况:
BOOL GetHandleInformation(
HANDLE hObj,
PDWORD pdwFlags);
该函数返回p d w F l a g s 指向的D W O R D 中特定句柄的当前标志的设置值。若要了解句柄是否是可继承的,请使用下面的代码:
DWORD dwFlags;
GetHandleInformation(hObj, &dwFlags);
BOOL fHandleIsInheritable = (0 != (dwFlags &
HANDLE_FLAG_INHERIT));
命名对象
HANDLE CreateMutex(
PSLCURITY_ATTRIBUTES psa,
BOOL bInitialOwner,
PCTSTR pszName);
HANDLE CreateEvent(
PSECURITY_ATTRIBUTES psa,
BOOL bManualReset,
BOOL bInitialState,
PCTSTR pszName);
HANDLE CreateSemaphore(
PSECURITY_ATTRIBUTES psa,
LONG lInitialCount,
LONG lMaximumCount,
PCTSTR pszNarne);
HANDLE CreateWaitableTimer(
PSLCURITY_ATTRIBUTES psa,
BOOL bManualReset,
PCTSTR pszName);
HANDLE CreateFileMapping(
HANDLE hFile,
PSECURITY_ATTRIBUTES psa,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
PCTSTR pszName);
HANDLE CreateJobObject(
PSECURITY_ATTRIBUTES psa,
PCTSTR pszName);
如上面所示创建一个内核对象都有一个pszName 的参数。这个参数可以为对象命名。
当pszName为NULL时表示创建一个匿名对象。
当pszName不起NULL时应该传递一个以0 结尾的字符串名字的地址。该名字的长度最多可以达到M A X _ PAT H (定义为2 6 0 )个字符。这样我们以后就可以使用这个名字来在不同的进程中来访问这些对象了,通过俩个方法
一) CreateXXXX方法
HANDLE hMutexPronessA = CreateMutex(NULL, FALSE, “JeffMutex”);
当之前没有创建JeffMutex对象时。这个函数就会创建一个安全属性为NULL没有拥有者的互斥对象。但是如果之前有JeffMutex对象时,这个函数就会返回这个对象而。而前面的两个参数就没用了
注意当你的多个内核对象拥有相同的名字时,有一个非常重要的细节必须知道。那就是所有的内核对象命名都在一个空间中
HANDLE hMutex = CreateMutex(NULL. FALSE, "JeffObj");
HANDLE hSem = CreateSemaphore(NULL, 1, 1, "JeffObj");
DWORD dwErrorCode = GetLastError();
会看到返回的代码是6 (ERROR INVALID HANDLE)。
为了很好的结局这一点,推荐把对象名命名成GUID形式的字符串
int WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE,
PSTR pszCmdLine, int nCmdShow)
{
HANDLE h = CreateMutex(NULL, FALSE,
"{FA531CC1-0497-11d3-A180-00105A276C3E}");
lf (GetLastError() == ERROR_ALREADY_EXISTS)
{
//There is already an instance
//of the application running
return(0),
}
//This is the first instance of this application running.
//Before exiting ,close the object.
CloseHandle(h),
return(0);
}
我们可以使用visual studio 自带的 Visual Studio Command Prompt输入
C:>uuidgen
或者你可以使用WIN32函数
UuidCreate
UuidFromString
UuidToString
二) OpenXXX方法
HANDLE OpenMutex(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);
HANDLE OpenEvent(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName),
HANDLE OpenWaitableTimer(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);
HANDLE OpenFileMapping(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);
HANDLE Openjob0bject(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);
如果找不到相应的内核对象那么该函数返回N U L L 。GetLastError返回2 (ERROR _ FILE _ NOT _ FOUND)。但是,如果存在带有指定名字的内核对象,并且它是相同类型的对象,那么系统就要查看是否允许执行所需的访问(通过dwDesired Access 参数进行访问)。如果拥有该访问权,调用进程的句柄表就被更新,对象的使用计数被递增。如果为bInheritHandle 参数传递TRUE ,那么返回的句柄将是可继承的。
复制对象句柄
BOOL DuplicateHandle(
HANDLE hSourceProcessHandle,
HANDLE hSourceHandle,
HANDLE hTargetProcessHandle,
PHANDLE phTargetHandle,
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwOptions);
简单说来,该函数取出一个进程的句柄表中的项目,并将该项目拷贝到另一个进程的句柄表中。
被复制对象的进程我们称为源进程,复制给那个进程我们称为目标进程
hSourceProcessHandle:源进程内核句柄
hSourceHandle:源进程中被复制的句柄
hTargetProcessHandle目标进程内核句柄
phTargetHandle:目标进程的句柄
dwOptions:可以是0也可以是下面两个参数的组合
- DUPLICATE_SAME_ACCESS
如果有这个标志,那么DUPLICATE会忽略DesiredAccess变量,完全拷贝访问屏蔽标志 - DUPLICATE_CLOSE_SOURCE
拷贝玩之后自动在源进程中关闭句柄
具体的功能可以看下图
这是典型的三进程使用DuplicateHandle函数。其中我们在C线程中使用DuplicateHandle函数。我们把函数参数一个一个对号入座
hSourceProcessHandle为HandleS指源进程内核对象
hSourceHandle为ObjHandleS源进程中的内容句柄
hTargetProcessHandle为HandleT目标进程的内核对象
phTargetHandle为TempHandleT在ProcessT中OBJS句柄会被存在此值中
然后进过复制之后我们可以得到下面这张图
这样句柄就被成功复制了
但是有几点需要注意
1.TempHandleT和ProcessT中的值是是一样的。但是ProcessC中句柄表是没有这个句柄的。只是有一个TempHandle来存储着一个句柄值(一个整数)。所以你不能在ProcessC中使用这个句柄
如删除这个句柄
CloseHandle(TempHandleT);
2.当复制完成句柄之后。ProcessT是不知道已近复制完成了的。所以这个时候就需要我们使用别的手段来完成这个提醒工作了。因为T进程已经开始工作了。命令行参数和环境变量是不可以使用的。我可以使用其他进程见的通信技术来专递这个TempHandleT,也可以窗口消息和IPC(信号量、共享内存、消息队列,)机制来完成访问
3上面的三个进程使用DuplicateHandle函数是为了方便大家理解正常情况下是用两个进程
//我们在源进程中调用DuplicateHandle
//在源进程中穿件一个要被复制的对象
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(hProcessT);
CloseHandle(hObjProcessS);
【参考】
WINDOWS 核心编程