转载自http://hi.baidu.com/csw8923/blog/item/8ba92b3498f8e047241f14d6.html
3.3.1 对象句柄的继承
要点记录: 父(内核对象权限)子句柄的继承可以利用 SECURITY_ATTRIBUTES 结构(内核对象安全)
中的bInheritHandle参数来设置是否开放给子进程继承权。
进程句柄继承,就是父进程将自己的句柄继承给子进程使子进程可访问父进程的内核对象。
因此父进程要分配一个初始化的SECURITY_ATTRIBUTES 结构,结构地址传给
Create函数,并将结构中sa.bInheritHandle参数设置为TRUE(反之就是不希望继承) 就可以了。
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecuntyDescriptor = NULL;
//Make the returned handle inheritable.
sa.bInheritHandle = TRUE;
HANDLE hMutex = CreateMutex(&sa, FALSE, NULL);
如下:CreateProcess函数可将SECURITY_ATTRIBUTES结构引入。
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);
这时,系统就会将父进程中所有可继承的句柄,完全继承到子进程中。同时内核计数也会增加。
如果有孙进程,只要子进程运行 那么孙进程同时也可以继承到父进程和子进程的权限。
3.3.2 改变句柄的标志
要点记录:
BOOL SetHandleInformation( HANDLE hObject, DWORD dwMask, DWORD dwFlags);
BOOL GetHandleInformation(hObj, &dwFlags);
这个两个函数可以改变句柄标志。
可以控制多个子进程的继承内核对象句柄权限。可调用SetHandleInformation函数来改变内核
对象句柄的继承标志。
BOOL SetHandleInformation(
HANDLE hObject,
DWORD dwMask,
DWORD dwFlags);
参数1,hObject 标识一个有效句柄。参数2,dwMask 告诉我们函数想更改哪个标志。
#define HANDLE_FLAG_INHERIT 0x00000001
#define HANDLE_FLAG_PROTECT_FROM_CLOSE 0x00000002
若想吧每个标志用一遍,可对两个标志采用或运算。
参数3, dwFlags 希望标志的内容。 例如:打开一个内核对象继承标志:
SetHandleInformation(hObj, HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);
关闭,这个标志。
SetHandleInformation(hObj, HANDLE_FLAG_INHERIT, 0);
若要告诉系统阻止关闭句柄使用以下标记:
HANDLE_FLAG_PROTECT_FROM_CLOSE
======================================================
SetHandleInformation(hObj, HANDLE_FLAG_PROTECT_FROM_CLOSE,
HANDLE_FLAG_PROTECT_FROM_CLOSE);
CloseHandle(hObj); // 会引发异常。
======================================================
关闭阻止关闭句柄标记
SetHandleInformation(hObj, HANDLE_FLAG_PROTECT_FROM_CLOSE,0);
SetHandleInformation(hobj, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0);
CloseHandle(hObj);
======================================================
GetHandleInformation函数,可获取当前标志状态:
该函数返回dwFlags指向的DWORD中特定句柄的当前标志的设置值。
若要知道该句柄是否可继承的,请使用下面的代码:
DWORD dwFlags;
GetHandleInformation(hObj, &dwFlags);
BOOL fHandleIsInheritable = (0 != (dwFlags & HANDLE_FLAG_INHERIT));
3.3.3 为对象命名
大部分的(不全部)内核对象都可以进行命名。如下函数:PCTSTR pszName 参数都可以传入NULL
既创建一个匿名的内核对象。
HANDLE CreateMutex(
PSECURITY_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 pszName);
HANDLE CreateWaitableTimer(
PSECURITY_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 。向该参数传递NULL,应该传递一个以0 结尾的字符串名字的地址。
该名字的长度最多可以达到MAX_PATH (定义为2 6 0 )个字符。
但是,Microsoft没有提供任何专门的机制来保证为内核对象指定的名称是唯一的。
假如,我们试图创建一个名为"JeffObj"对象,那么没有任何一个一种机制保证当前不存在一个名为
"JeffObj"对象,更糟是,所有这这些对象都共享一个命名空间,即使他们类型不相同。
CreateSemaphore函数鲦鱼肯定会返回NULL,因为已经有 同名的互斥量。
HANDLE hMutex = CreateMutex(NULL, FALSE, TEXT("JeffObj"));
HANDLE hSem = CreateSemaphore(NULL, 1, 1, TEXT("JeffObj"));
DWORD dwErrorCode = GetLastError();
这时 dwErrorCode 会返回 代码6, (ERROR_INVALID_HANDLE).
命名对象的共享对象方式:
HANDLE hMutexProcessA = CreateMutex(NULL, FALSE, TEXT("JeffMutex"));
HANDLE hMutexProcessB = CreateMutex(NULL, FALSE, TEXT("JeffMutex"));
这两个进程内核对象A、B并不没有继承关系。然而 他们却可以通过对象命名来实现
共享。同时,同名的句柄并不一定会相同。
在执行过程中,系统会先进行检测确定调用者是否有对对象的完全访问权,有的话
则会在句柄表中查找空白记录项,并初始化现有内核对象,对象若不匹配者别调用者则被拒绝访问。
这时函数会返回NULL。
我们完全可以再调用Create*后,马上调用GetLastError,判断自己是否真创建了一个新内核对象,
还是仅打开了现有的:
HANDLE hMutex = CreateMutex(&sa, FALSE, TEXT("JeffObj"));
if (GetLastError() == ERROR_ALREADY_EXISTS) {
// Opened a handle to an existing object.
// sa.lpSecurityDescriptor and the second parameter
// (FALSE) are ignored.
} else {
// Created a brand new object.
// sa.lpSecurityDescriptor and the second parameter
// (FALSE) are used to construct the object.
}
实现内核对象共享,我们还可以考虑用打开方式(Open*),如下面函数:
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 OpenJobObject(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);
他们的使用方法基本和上面的创建一样,这些函数会在同一个内核对象命名空间搜索。
如果没有寻找到这个内核那么函数会返回NULL可以用GetLastError返回2(ERROR_FILE_NOT_FOUND)
如果有寻找到这个内核那么函数,但类型不同会返回NULL可以用GetLastError返回6(ERROR_INVALID_HANDLE)
如果在名称和类型上都相同的话,系统会要求通过 dwDesiredAccess 是指定是否运行,若允许则对象使用计数递增。
可以利用命名对象来防止,一个应用程序的多实例运行。只要在 _tmain or _tWinMain 函数中调用
Create* 函数来创建命名对象 函数返回后,再调用GetLastError 如果函数返回 ERROR_ALREADY_EXISTS
则表明已经有另个实例在运行了。
int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine,
int nCmdShow) {
HANDLE h = CreateMutex(NULL, FALSE,
TEXT("{FA531CC1-0497-11d3-A180-00105A276C3E}"));
// 命名对象的名称
if (GetLastError() == ERROR_ALREADY_EXISTS) {
// 目前已有一个正在运行的应用实例.
// 关闭对象,并立即返回。
CloseHandle(h);
return(0);
}
// 这是正在运行的应用程序的第一个实例。
...
// 在退出之前,关闭对象。
CloseHandle(h);
return(0);
}
3.3.4 终端服务器的名字空间
要点记录:
"Global\", "Local\" 可决定将命名对象是放到全局或者当前的命名空间。
默认情况,应用程序自己的内核对象在会话的命名空间内。我们可强制把一个命名对象放入全局命名空间,
具体方法是 在名称前加上 "Global\",如下所示:
HANDLE h = CreateEvent(NULL, FALSE, FALSE, TEXT("Global\\MyName"));
也可以显示指出我们希望把一个内核对象放入当前会话的命名空间, 在名称前加上 "Local\",
HANDLE h = CreateEvent(NULL, FALSE, FALSE, TEXT("Local\\MyName"));
浙公网安备 33010602011771号