windows 核心编程代码笔记(3)
1 /****************************************************************************** 2 Module: Singleton.cpp 3 Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre 4 ******************************************************************************/ 5 6 7 #include "resource.h" 8 9 #include "..\CommonFiles\CmnHdr.h" /* See Appendix A. */ 10 #include <windowsx.h> 11 #include <Sddl.h> // for SID management 12 #include <tchar.h> 13 #include <strsafe.h> 14 15 16 17 /////////////////////////////////////////////////////////////////////////////// 18 19 20 // Main dialog 21 HWND g_hDlg; 22 23 // Mutex, boundary and namespace used to detect previous running instance 24 HANDLE g_hSingleton = NULL; 25 HANDLE g_hBoundary = NULL; 26 HANDLE g_hNamespace = NULL; 27 28 // Keep track whether or not the namespace was created or open for clean-up 29 BOOL g_bNamespaceOpened = FALSE; 30 31 // Names of boundary and private namespace 32 PCTSTR g_szBoundary = TEXT("3-Boundary"); 33 PCTSTR g_szNamespace = TEXT("3-Namespace"); 34 35 36 #define DETAILS_CTRL GetDlgItem(g_hDlg, IDC_EDIT_DETAILS) 37 38 39 /////////////////////////////////////////////////////////////////////////////// 40 41 42 // Adds a string to the "Details" edit control 43 void AddText(PCTSTR pszFormat, ...) { 44 45 va_list argList; 46 va_start(argList, pszFormat); 47 48 TCHAR sz[20 * 1024]; 49 50 /* 51 获取指向编辑的文本 52 hwndCtl 53 一个指向编辑控件的句柄. 54 lpch 55 一个指向用来存放控件中字符串的字符缓存区. 56 cchMax 57 字符缓存区lpch最多容纳的字符数,包含NULL所占字符. 58 59 */ 60 61 Edit_GetText(DETAILS_CTRL, sz, _countof(sz)); 62 63 /* 64 从一个字符串中查找字符。 65 例如: 66 CString str1 = "ABCD#EFGH"; 67 CString str2 = _tcschr(str1,TEXT('#')); 68 str2 结果为"#EFGH". 69 */ 70 _vstprintf_s( 71 _tcschr(sz, TEXT('\0')), _countof(sz) - _tcslen(sz), 72 pszFormat, argList); 73 Edit_SetText(DETAILS_CTRL, sz); 74 va_end(argList); 75 } 76 77 78 /////////////////////////////////////////////////////////////////////////////// 79 80 81 void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { 82 83 switch (id) { 84 case IDOK: 85 case IDCANCEL: 86 // User has clicked on the Exit button 87 // or dismissed the dialog with ESCAPE 88 EndDialog(hwnd, id); 89 break; 90 } 91 } 92 93 94 /////////////////////////////////////////////////////////////////////////////// 95 96 97 void CheckInstances() { 98 99 // Create the boundary descriptor 100 g_hBoundary = CreateBoundaryDescriptor(g_szBoundary, 0); 101 102 // Create a SID corresponding to the Local Administrator group 103 BYTE localAdminSID[SECURITY_MAX_SID_SIZE]; 104 PSID pLocalAdminSID = &localAdminSID; 105 DWORD cbSID = sizeof(localAdminSID); 106 107 /* 108 创建一个SID(安全描述符) 109 BOOL WINAPI CreateWellKnownSid( __in WELL_KNOWN_SID_TYPE WellKnownSidType, __in_opt PSID DomainSid, __out_opt PSID pSid, __inout DWORD *cbSid); 110 参数: 111 WellKnownSidType SID类型,WELL_KNOWN_SID_TYPE是枚举类型,它包含一系列的安全描述符类型 112 DomainSid 指向创建了SID的域的指针,为NULL时表示使用本地计算机 113 pSid 指向存储SID的地址 114 cbSid 指向存储pSid的大小的地址 115 */ 116 117 if (!CreateWellKnownSid( 118 WinBuiltinAdministratorsSid, NULL, pLocalAdminSID, &cbSID) 119 ) { 120 AddText(TEXT("AddSIDToBoundaryDescriptor failed: %u\r\n"), 121 GetLastError()); 122 return; 123 } 124 125 // Associate the Local Admin SID to the boundary descriptor 126 // --> only applications running under an administrator user 127 // will be able to access the kernel objects in the same namespace 128 if (!AddSIDToBoundaryDescriptor(&g_hBoundary, pLocalAdminSID)) { 129 AddText(TEXT("AddSIDToBoundaryDescriptor failed: %u\r\n"), 130 GetLastError()); 131 return; 132 } 133 134 // Create the namespace for Local Administrators only 135 SECURITY_ATTRIBUTES sa; 136 sa.nLength = sizeof(sa); 137 sa.bInheritHandle = FALSE; 138 139 /* 140 ConvertStringSecurityDescriptorToSecurityDescriptor 141 函数可以将一个按安全描述符格式的字符串转换成一个有效的安全描述符结构。 142 本函数和ConvertSecurityDescriptorToStringSecurityDescriptor函数的功能相反。 143 144 StringSecurityDescriptor[in] 145 一个字符串,以'\0'为结束符,是一个符合安全描述符的字符串,是本函数用来转换的源字符串。 146 StringSDRevision[in] 147 指定StringSecurityDescriptor的修订级别,目前的版本必须是SDDL_REVISION_1。 148 SecurityDescriptor[out] 149 一个指向用来存储转换后的SID变量。返回的安全描述符是自相关的。要释放返回的缓冲区,需要调用LocalFree函数。 150 为了将安全描述符转换成一个绝对安全描述符,需要调用MakeAbsoluteSD函数。 151 SecurityDescriptorSize[out] 152 一个指向存储返回的缓冲区大小的变量。如果不需要,可以将这个参数传入NULL。 153 编辑本段返回值如果函数成功,则返回非0值,否则返回0。可以调用GetLastError获得更详细的错误原因。GetLastError可能返回的错误代码如下: 154 返回值 描述 155 ERROR_INVALID_PARAMETER 某个参数是无效的。 156 ERROR_UNKNOWN_REVISION 参数StringSDRevision是不正确。 157 ERROR_NONE_MAPPED 输入字符串描述的SID在账户查询操作中找不到 158 159 */ 160 if (!ConvertStringSecurityDescriptorToSecurityDescriptor( 161 TEXT("D:(A;;GA;;;BA)"), 162 SDDL_REVISION_1, &sa.lpSecurityDescriptor, NULL)) { 163 AddText(TEXT("Security Descriptor creation failed: %u\r\n"), GetLastError()); 164 return; 165 } 166 167 g_hNamespace = 168 CreatePrivateNamespace(&sa, g_hBoundary, g_szNamespace); 169 170 // Don't forget to release memory for the security descriptor 171 LocalFree(sa.lpSecurityDescriptor); 172 173 174 // Check the private namespace creation result 175 DWORD dwLastError = GetLastError(); 176 if (g_hNamespace == NULL) { 177 // Nothing to do if access is denied 178 // --> this code must run under a Local Administrator account 179 if (dwLastError == ERROR_ACCESS_DENIED) { 180 AddText(TEXT("Access denied when creating the namespace.\r\n")); 181 AddText(TEXT(" You must be running as Administrator.\r\n\r\n")); 182 return; 183 } else { 184 if (dwLastError == ERROR_ALREADY_EXISTS) { 185 // If another instance has already created the namespace, 186 // we need to open it instead. 187 AddText(TEXT("CreatePrivateNamespace failed: %u\r\n"), dwLastError); 188 g_hNamespace = OpenPrivateNamespace(g_hBoundary, g_szNamespace); 189 if (g_hNamespace == NULL) { 190 AddText(TEXT(" and OpenPrivateNamespace failed: %u\r\n"), 191 dwLastError); 192 return; 193 } else { 194 g_bNamespaceOpened = TRUE; 195 AddText(TEXT(" but OpenPrivateNamespace succeeded\r\n\r\n")); 196 } 197 } else { 198 AddText(TEXT("Unexpected error occured: %u\r\n\r\n"), 199 dwLastError); 200 return; 201 } 202 } 203 } 204 205 // Try to create the mutex object with a name 206 // based on the private namespace 207 TCHAR szMutexName[64]; 208 209 /* 210 http://baike.baidu.com/view/10100911.htm 211 */ 212 StringCchPrintf(szMutexName, _countof(szMutexName), TEXT("%s\\%s"), 213 g_szNamespace, TEXT("Singleton")); 214 215 g_hSingleton = CreateMutex(NULL, FALSE, szMutexName); 216 if (GetLastError() == ERROR_ALREADY_EXISTS) { 217 // There is already an instance of this Singleton object 218 AddText(TEXT("Another instance of Singleton is running:\r\n")); 219 AddText(TEXT("--> Impossible to access application features.\r\n")); 220 } else { 221 // First time the Singleton object is created 222 AddText(TEXT("First instance of Singleton:\r\n")); 223 AddText(TEXT("--> Access application features now.\r\n")); 224 } 225 } 226 227 228 /////////////////////////////////////////////////////////////////////////////// 229 230 231 BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { 232 233 chSETDLGICONS(hwnd, IDI_SINGLETON); 234 235 // Keep track of the main dialog window handle 236 g_hDlg = hwnd; 237 238 // Check whether another instance is already running 239 CheckInstances(); 240 241 return(TRUE); 242 } 243 244 245 /////////////////////////////////////////////////////////////////////////////// 246 247 248 INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 249 250 switch (uMsg) { 251 chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); 252 chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); 253 } 254 255 return(FALSE); 256 } 257 258 259 /////////////////////////////////////////////////////////////////////////////// 260 261 262 int APIENTRY _tWinMain(HINSTANCE hInstance, 263 HINSTANCE hPrevInstance, 264 LPTSTR lpCmdLine, 265 int nCmdShow) 266 { 267 UNREFERENCED_PARAMETER(hPrevInstance); 268 UNREFERENCED_PARAMETER(lpCmdLine); 269 270 // Show main window 271 DialogBox(hInstance, MAKEINTRESOURCE(IDD_SINGLETON), NULL, Dlg_Proc); 272 273 // Don't forget to clean up and release kernel resources 274 if (g_hSingleton != NULL) { 275 CloseHandle(g_hSingleton); 276 } 277 278 if (g_hNamespace != NULL) { 279 if (g_bNamespaceOpened) { // Open namespace 280 ClosePrivateNamespace(g_hNamespace, 0); 281 } else { // Created namespace 282 ClosePrivateNamespace(g_hNamespace, PRIVATE_NAMESPACE_FLAG_DESTROY); 283 } 284 } 285 286 if (g_hBoundary != NULL) { 287 DeleteBoundaryDescriptor(g_hBoundary); 288 } 289 290 return(0); 291 } 292 293 294 //////////////////////////////// End of File //////////////////////////////////
注: 如果你已经创建了命名空间,而且不希望它在关闭后仍然可见,应该将PRIVATE_NAMESPACE_FLAG_DESTROY作为第二个参数传给上述函数,反之则传入0。边界将在以下两种情况下关闭:进程终止运行;或者调用DeleteBoundaryDescriptor,并将边界伪句柄作为惟一的参数传给它。如果还有内核对象正在使用,命名空间一定不能关闭。如果在内部还有内核对象时关闭了一个命名空间,就可以在同一个边界中,在重新创建的一个相同的命名空间中,创建一个同名的内核对象,使DoS攻击再次成为可能。

浙公网安备 33010602011771号