1. 一个进程在初始化时,系统将会他分配一个空的句柄表,这个句柄表仅供内核对象使用,不供用户对象和GDI对象使用。进程在首次
初始化时,该句柄表为空。句柄表是一个由数据结构组成的数组,包含一个内核对象指针,一个访问掩码和一些标志。系统用索引来
标识内核对象的信息保存在进程句柄表中的具体位置,句柄值除以4即为索引值(针对不同版本的windows系统可能不同)。
2. 要注意内核对象返回值INVALID_HANDLE_VALUE(-1)和NULL(0)的区别,凡是用于创建内核对象的函数,在检查它们的返回值时
务必相当仔细。一般只有CreateFile才返回INVALID_HANDLE_VALUE。
3. 对于内核对象,操作系统执行操作过程为:进程终止时,系统会自动扫描进程句柄表。如果这个表中有任何有效的记录项(即进程进程终
止前未关闭的对象),操作系统会为我们关闭这些对象句柄(只要这些对象的使用计数为0)。
4. 进程共享内核对象机制
(1). 对象句柄继承
a.创建内核对象时将SECURITY_ATTRIBUTES结构的bInheritHandle设为TRUE
b.将CreateProcess参数的bInheritHandle设为TRUE,注:对象句柄的继承只会在生成子进程的时候发生
c.子进程无法查看自己继承了任何的对象句柄,在开发阶段最好用文档表明
(2). 改变句柄标志
SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags)
两个关键标志HANDLE_FLAG_INHERIT和HANDLE_FLAG_PROTECT_FROM_CLOSE
(3). 为对象命名
许多(但不是全部)内核对象都可以进行命名
(4). 复制对象句柄
从一个进程的句柄表中获得一个记录项,然后在另一个进程的句柄表中创建这个记录项的副本(DuplicateHandle)
5. 实例
typedef HANDLE (_stdcall* BOUNDARYDESCRIPTOR)(LPTSTR, DWORD); BOUNDARYDESCRIPTOR CreateBoundaryDescriptor; typedef BOOL (_stdcall* ADDSIDTOBOUNDARYDESCRIPTOR)(HANDLE *, PSID); ADDSIDTOBOUNDARYDESCRIPTOR AddSIDToBoundaryDescriptor; typedef HANDLE (_stdcall *CREATEPRIVATENAMESPACE)(LPSECURITY_ATTRIBUTES, LPVOID, LPCSTR); CREATEPRIVATENAMESPACE CreatePrivateNamespace; typedef HANDLE (_stdcall* OPENPRIVATENAMESPACE)(PVOID, LPCSTR); OPENPRIVATENAMESPACE OpenPrivateNamespace; void CSingletonDlg::CheckInstances() { HMODULE hKernel = LoadLibrary("Kernel32.dll"); if (hKernel) { CreateBoundaryDescriptor = (BOUNDARYDESCRIPTOR)GetProcAddress(hKernel, "CreateBoundaryDescriptorA"); // 注意函数名AddSIDToBoundaryDescriptor后面没带'A' AddSIDToBoundaryDescriptor = (ADDSIDTOBOUNDARYDESCRIPTOR)GetProcAddress(hKernel, "AddSIDToBoundaryDescriptor"); CreatePrivateNamespace = (CREATEPRIVATENAMESPACE)GetProcAddress(hKernel, "CreatePrivateNamespaceA"); OpenPrivateNamespace = (OPENPRIVATENAMESPACE)GetProcAddress(hKernel, "OpenPrivateNamespaceA"); } ); if (NULL == m_hBoundaryDescriptor) { // 创建边界描述符失败 return; } // 为边界描述符关联一个SID BYTE localAdminSID[SECURITY_MAX_SID_SIZE]; PSID pLocalAdminSID = &localAdminSID; DWORD cbSID = sizeof(localAdminSID); if (!CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, pLocalAdminSID, &cbSID)) { // 创建SID失败 return; } if (!AddSIDToBoundaryDescriptor(&m_hBoundaryDescriptor, pLocalAdminSID)) { // 链接边界描述符失败 return; } SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); sa.bInheritHandle = FALSE; if (!ConvertStringSecurityDescriptorToSecurityDescriptor( TEXT("D:(A;;GA;;;BA)"), SDDL_REVISION_1, &sa.lpSecurityDescriptor, NULL)) { return; } // 创建命名空间 /*HANDLE */m_hPrivateNameSpace = CreatePrivateNamespace(&sa, m_hBoundaryDescriptor, "3-Namespace"); LocalFree(sa.lpSecurityDescriptor); // 检查已创建的命名空间:看是否有权限访问ERROR_ACCESS_DENIED和该命名空间是否存在ERROR_ALREADY_EXISTS DWORD dwLastErro = GetLastError(); if (m_hPrivateNameSpace == NULL) { if (ERROR_ACCESS_DENIED == dwLastErro) { return; } else if (ERROR_ALREADY_EXISTS == dwLastErro) { m_hPrivateNameSpace = OpenPrivateNamespace(m_hBoundaryDescriptor, "3-Namespace"); if (NULL == m_hPrivateNameSpace) { return; } } } // 创建互斥对象 }; StringCchPrintf(cMutexName, sizeof(MAX_PATH), "%s\\%s", "3-Namespace", "Single"); g_hSingleton = CreateMutex(NULL, FALSE, cMutexName); if (GetLastError() == ERROR_ALREADY_EXISTS) { AddText(TEXT("Another instance of Singleton is running:\r\n")); AddText(TEXT("--> Impossible to access application features.\r\n")); } else { AddText(TEXT("First instance of Singleton:\r\n")); AddText(TEXT("--> Access application features now.\r\n")); } FreeLibrary(hKernel); }
6. 附录
boundary 分界线;范围
descriptor 描述符
(1). HANDLE CreateBoundaryDescriptor(PCTSTR pszName, DWORD dwFlags)
创建边界描述符
第二个参数为以后保留,目前没什么用,为0
函数返回的并非是一个句柄,而是一个指向用户模式结构的指针,结构中包含了边界的定义,不要把返回的句柄值传给
CloseHandle,而要传给DeleteBoundaryDescriptor
(2). BOOL AddSIDToBoundaryDescriptor(HANDLE *phBoundaryDescriptor, PSID pRequireSid)
将一个特权用户组的SID与边界描述符关联起来
(3). HANDLE CreatePrivateNameSpace(PSECURITY_ATTRIBUTES, PVOID pvBoundaryDescriptor, pszAliasPrefix)
对创建好的命名空间进行检查GetLastError(),ERROR_ACCESS_DENIED、ERROR_ALREADY_EXISTS
(4). HANDLE OpenPrivateNameSpace( PVOID pvBoundaryDescriptor, pszAliasPrefix)
(5). ClosePrivateNameSpace(HANDLE hNamespace, DWORD dwFlags)
如果我们已经创建了命名空间,而且不希望它在关闭后仍然可见,可以用PRIVATE_NAMESPACE_FLAG_DESTRPY作为第二个参数,反之则为0
(6). BOOL WINAPI CreateWellKnowSid()
创建一个SID
参数1:WellKnowSidType SID类型,WELL_KNOW_SID_TYPE是枚举类型,它包含一系列的安全描述符类型
参数2: _in_opt PSID DomainSid 指向创建了SID的域的指针,为NULL时表示本地计算机
参数3:_out_opt PSID pSid 指向存储SID地址
参数4: _inout DWORD *cbSid 指向存储pSid的大小的地址