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攻击再次成为可能。

posted @ 2013-05-03 22:48  小角  阅读(228)  评论(0)    收藏  举报