Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

611 lines
19 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation
  3. Module Name:
  4. csrdbgmon.cpp
  5. Abstract:
  6. Author:
  7. Michael Grier (MGrier) June 2002
  8. Revision History:
  9. Jay Krell (Jaykrell) June 2002
  10. make it compile for 64bit
  11. tabs to spaces
  12. init some locals
  13. make some tables const
  14. --*/
  15. #include <windows.h>
  16. #include <stddef.h>
  17. #include <stdlib.h>
  18. #include <stdio.h>
  19. #include <stdarg.h>
  20. #include <dbghelp.h>
  21. #define ASSERT(x) do { /* nothing */ } while(0)
  22. #define NUMBER_OF(_x) (sizeof(_x) / sizeof((_x)[0]))
  23. static const char g_szImage[] = "csrdbgmon";
  24. static const char *g_pszImage = g_szImage;
  25. static HANDLE g_hWorkerThread;
  26. static DWORD g_tidWorkerThread;
  27. static HANDLE g_hCompletionPort;
  28. static HANDLE g_hCSRSS;
  29. static DWORD g_pidCSRSS;
  30. static bool g_fDebuggingCSRSS = false;
  31. static DWORD64 g_dw64NtdllBaseAddress;
  32. static DWORD g_dwOldKdFusionMask = 0;
  33. static bool g_fKdFusionMaskSet = false;
  34. void ReportFailure(const char szFormat[], ...);
  35. DWORD WINAPI WorkerThreadThreadProc(LPVOID);
  36. BOOL EnableDebugPrivilege();
  37. FILE *fp;
  38. void
  39. ReportFailure(
  40. const char szFormat[],
  41. ...
  42. )
  43. {
  44. const DWORD dwLastError = ::GetLastError();
  45. va_list ap;
  46. char rgchBuffer[4096];
  47. WCHAR rgchWin32Error[4096];
  48. // Stop debugging csrss so that we can actually issue the error message.
  49. if (g_fDebuggingCSRSS)
  50. ::DebugActiveProcessStop((DWORD) -1);
  51. va_start(ap, szFormat);
  52. ::_vsnprintf(rgchBuffer, sizeof(rgchBuffer) / sizeof(rgchBuffer[0]), szFormat, ap);
  53. va_end(ap);
  54. if (!::FormatMessageW(
  55. FORMAT_MESSAGE_FROM_SYSTEM,
  56. NULL,
  57. dwLastError,
  58. 0,
  59. rgchWin32Error,
  60. NUMBER_OF(rgchWin32Error),
  61. &ap))
  62. {
  63. const DWORD dwLastError2 = ::GetLastError();
  64. ::_snwprintf(rgchWin32Error, sizeof(rgchWin32Error) / sizeof(rgchWin32Error[0]), L"Error formatting Win32 error %lu (0x%08lx)\nError from FormatMessage is %lu", dwLastError, dwLastError, dwLastError2);
  65. }
  66. ::fprintf(stderr, "%ls: %s\n%ls\n", g_pszImage, rgchBuffer, rgchWin32Error);
  67. }
  68. BOOL
  69. FormatAndQueueString(
  70. const WCHAR szFormat[],
  71. ...
  72. )
  73. {
  74. BOOL fSuccess = FALSE;
  75. ::SetLastError(ERROR_INTERNAL_ERROR);
  76. WCHAR rgwchBuffer[2048];
  77. SIZE_T cch;
  78. LPOVERLAPPED lpo = NULL;
  79. PWSTR psz = NULL;
  80. const HANDLE Heap = ::GetProcessHeap();
  81. va_list ap;
  82. va_start(ap, szFormat);
  83. cch = ::_vsnwprintf(rgwchBuffer, NUMBER_OF(rgwchBuffer), szFormat, ap);
  84. va_end(ap);
  85. lpo = (LPOVERLAPPED) ::HeapAlloc(Heap, 0, sizeof(OVERLAPPED) + ((cch + 1) * sizeof(WCHAR)));
  86. if (lpo == NULL)
  87. {
  88. ::SetLastError(ERROR_OUTOFMEMORY);
  89. ::ReportFailure("HeapAlloc(%p, 0, %lu) failed", Heap, sizeof(OVERLAPPED) + ((cch + 1) * sizeof(WCHAR)));
  90. goto Exit;
  91. }
  92. ::ZeroMemory(lpo, sizeof(OVERLAPPED));
  93. psz = (PWSTR) (lpo + 1);
  94. ::wcscpy(psz, rgwchBuffer);
  95. if (!::PostQueuedCompletionStatus(g_hCompletionPort, 0, NULL, lpo))
  96. {
  97. ::ReportFailure("PostQueuedCompletionStatus(%p, 0, NULL, %p) failed", g_hCompletionPort, 0, NULL, lpo);
  98. goto Exit;
  99. }
  100. lpo = NULL;
  101. fSuccess = TRUE;
  102. Exit:
  103. if (lpo != NULL)
  104. {
  105. const DWORD dwLastError = ::GetLastError();
  106. ::HeapFree(Heap, 0, lpo);
  107. ::SetLastError(dwLastError);
  108. }
  109. return fSuccess;
  110. }
  111. BOOL CALLBACK EnumModules(
  112. LPSTR ModuleName,
  113. ULONG BaseOfDll,
  114. PVOID UserContext )
  115. {
  116. ::printf("%08X %s\n", BaseOfDll, ModuleName);
  117. return TRUE;
  118. }
  119. typedef HANDLE (__stdcall * PCSR_GET_PROCESS_ID)();
  120. extern "C" int __cdecl wmain(int argc, wchar_t** argv)
  121. {
  122. int iReturnStatus = EXIT_FAILURE;
  123. DEBUG_EVENT de;
  124. SYMBOL_INFO si = { sizeof(si) };
  125. PCSR_GET_PROCESS_ID pfn = NULL;
  126. DWORD dwNewKdMask = 0x40000000;
  127. SIZE_T nBytesWritten = 0;
  128. DWORD dwSymOptions = 0;
  129. const HANDLE Heap = ::GetProcessHeap();
  130. #if 0
  131. fp = fopen("csrdbgmon.log", "w");
  132. if (!fp)
  133. {
  134. perror("unable to create csrdbgmon.log");
  135. goto Exit;
  136. }
  137. #endif // 0
  138. if (!::EnableDebugPrivilege())
  139. {
  140. ::ReportFailure("EnableDebugPrivilege() failed");
  141. goto Exit;
  142. }
  143. if ((pfn = (PCSR_GET_PROCESS_ID) GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "CsrGetProcessId")) == NULL)
  144. {
  145. ::ReportFailure("GetProcessAddress(GetModuleHandleW(L\"ntdll\"), \"CsrGetProcessId\") failed");
  146. goto Exit;
  147. }
  148. g_pidCSRSS = (DWORD)(DWORD_PTR)(*pfn)();
  149. if ((g_hCSRSS = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, g_pidCSRSS)) == NULL)
  150. {
  151. ::ReportFailure("OpenProcess(PROCESS_ALL_ACCESS, FALSE, 0x%lx) failed", g_pidCSRSS);
  152. goto Exit;
  153. }
  154. dwSymOptions = ::SymGetOptions();
  155. if (!::SymSetOptions(dwSymOptions | SYMOPT_DEFERRED_LOADS))
  156. {
  157. ::ReportFailure("SymSetOptions(dwSymOptions (= 0x%08lx) | SYMOPT_DEFERRED_LOADS (= 0x%08x)) failed", dwSymOptions, SYMOPT_DEFERRED_LOADS);
  158. goto Exit;
  159. }
  160. if (!::SymInitialize(g_hCSRSS, NULL, TRUE))
  161. {
  162. ::ReportFailure("SymInitialize(%p, NULL, TRUE) failed", g_hCSRSS);
  163. goto Exit;
  164. }
  165. if (!::SymFromName(g_hCSRSS, "sxs!kd_fusion_mask", &si))
  166. {
  167. ::ReportFailure("SymFromName(%p, \"sxs!kd_fusion_mask\", %p) failed", g_hCSRSS, &si);
  168. goto Exit;
  169. }
  170. if (!::ReadProcessMemory(g_hCSRSS, (PVOID) si.Address, &g_dwOldKdFusionMask, sizeof(g_dwOldKdFusionMask), &nBytesWritten))
  171. {
  172. ::ReportFailure("ReadProcessMemory(%p, %p, %p, %lu, %p) failed", g_hCSRSS, (PVOID) si.Address, &g_dwOldKdFusionMask, sizeof(g_dwOldKdFusionMask), &nBytesWritten);
  173. goto Exit;
  174. }
  175. dwNewKdMask = g_dwOldKdFusionMask | 0x40000000;
  176. if (!::WriteProcessMemory(g_hCSRSS, (PVOID) si.Address, &dwNewKdMask, sizeof(dwNewKdMask), &nBytesWritten))
  177. {
  178. ::ReportFailure("WriteProcessMemory(%p, %p, %p (-> %lx), %lu, %p) failed", g_hCSRSS, (PVOID) si.Address, &dwNewKdMask, dwNewKdMask, sizeof(dwNewKdMask), &nBytesWritten);
  179. goto Exit;
  180. }
  181. g_fKdFusionMaskSet = true;
  182. if ((g_hCompletionPort = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1)) == NULL)
  183. {
  184. ::ReportFailure("CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1) failed");
  185. goto Exit;
  186. }
  187. if ((g_hWorkerThread = ::CreateThread(NULL, 0, &WorkerThreadThreadProc, NULL, 0, &g_tidWorkerThread)) == NULL)
  188. {
  189. ::ReportFailure("CreateThread(NULL, 0, %p (==&WorkerThreadThreadProc), NULL, 0, %p (== &g_tidWorkerThread))", &WorkerThreadThreadProc, &g_tidWorkerThread);
  190. goto Exit;
  191. }
  192. if (!::FormatAndQueueString(L"Found ntdll!kd_fusion_mask at %I64x\n", si.Address))
  193. {
  194. ::ReportFailure("FormatAndQueueString() failed");
  195. goto Exit;
  196. }
  197. if (!::DebugActiveProcess(g_pidCSRSS))
  198. {
  199. ::ReportFailure("DebugActiveProcess(0x%lx) failed", g_pidCSRSS);
  200. goto Exit;
  201. }
  202. // First, if we die, we don't want to take the system with us.
  203. if (!::DebugSetProcessKillOnExit(FALSE))
  204. {
  205. const DWORD dwLastError = ::GetLastError();
  206. ::DebugActiveProcessStop(g_pidCSRSS);
  207. ::SetLastError(dwLastError);
  208. ::ReportFailure("DebugSetProcessKillOnExit(FALSE) failed");
  209. goto Exit;
  210. }
  211. g_fDebuggingCSRSS = true;
  212. for (;;)
  213. {
  214. SIZE_T t = 0;
  215. PCSTR EventName = "<Event not in map>";
  216. if (!::WaitForDebugEvent(&de, INFINITE))
  217. {
  218. ::ReportFailure("WaitForDebugEvent(%p, INFINITE) failed", &de);
  219. goto Exit;
  220. }
  221. if (fp != NULL)
  222. {
  223. const static struct { PCSTR psz; DWORD dwEventCode; } s_rgMap[] = {
  224. #define ENTRY(x) { #x, x }
  225. ENTRY(EXCEPTION_DEBUG_EVENT),
  226. ENTRY(CREATE_THREAD_DEBUG_EVENT),
  227. ENTRY(CREATE_PROCESS_DEBUG_EVENT),
  228. ENTRY(EXIT_THREAD_DEBUG_EVENT),
  229. ENTRY(EXIT_PROCESS_DEBUG_EVENT),
  230. ENTRY(LOAD_DLL_DEBUG_EVENT),
  231. ENTRY(UNLOAD_DLL_DEBUG_EVENT),
  232. ENTRY(OUTPUT_DEBUG_STRING_EVENT),
  233. ENTRY(RIP_EVENT),
  234. #undef ENTRY
  235. };
  236. for (t=0; t<NUMBER_OF(s_rgMap); t++)
  237. {
  238. if (s_rgMap[t].dwEventCode == de.dwDebugEventCode)
  239. {
  240. EventName = s_rgMap[t].psz;
  241. break;
  242. }
  243. }
  244. ::fprintf(fp, "%s { p: %lu; t: %lu } ", EventName, de.dwProcessId, de.dwThreadId);
  245. }
  246. switch (de.dwDebugEventCode)
  247. {
  248. case CREATE_PROCESS_DEBUG_EVENT:
  249. g_hCSRSS = de.u.CreateProcessInfo.hProcess;
  250. break;
  251. case EXIT_PROCESS_DEBUG_EVENT:
  252. break;
  253. #if 0
  254. case LOAD_DLL_DEBUG_EVENT:
  255. {
  256. LOAD_DLL_DEBUG_INFO &rlddi = de.u.LoadDll;
  257. if (!::FormatAndQueueString(
  258. L"Loaded:\n"
  259. L" hFile: %p\n"
  260. L" lpBaseOfDll: %p\n"
  261. L" dwDebugInfoFileOffset: %lu (0x%lx)\n"
  262. L" nDebugInfoSize: %lu (0x%lx)\n"
  263. L" lpImageName: %p\n"
  264. L" fUnicode: %u\n",
  265. rlddi.hFile,
  266. rlddi.lpBaseOfDll,
  267. rlddi.dwDebugInfoFileOffset, rlddi.dwDebugInfoFileOffset,
  268. rlddi.nDebugInfoSize, rlddi.nDebugInfoSize,
  269. rlddi.lpImageName,
  270. rlddi.fUnicode))
  271. {
  272. ::ReportFailure("FormatAndQueueString() for LOAD_DLL_DEBUG_EVENT failed");
  273. goto Exit;
  274. }
  275. break;
  276. }
  277. #endif // 0
  278. case OUTPUT_DEBUG_STRING_EVENT:
  279. {
  280. DWORD BytesReadDword = 0;
  281. SIZE_T BytesReadSizeT = 0;
  282. OUTPUT_DEBUG_STRING_INFO &rodsi = de.u.DebugString;
  283. PWSTR pszLocalString = NULL;
  284. LPOVERLAPPED lpo = NULL;
  285. SIZE_T len = rodsi.nDebugStringLength;
  286. if (rodsi.nDebugStringLength != 0)
  287. {
  288. if (rodsi.fUnicode)
  289. {
  290. if ((lpo = (LPOVERLAPPED) ::HeapAlloc(Heap, 0, sizeof(OVERLAPPED) + ((len + 1) * sizeof(WCHAR)))) == NULL)
  291. {
  292. ::ReportFailure("HeapAlloc(%p (== GetProcessHeap()), 0, %lu) failed", GetProcessHeap(), sizeof(OVERLAPPED) + (rodsi.nDebugStringLength * sizeof(WCHAR)));
  293. goto Exit;
  294. }
  295. pszLocalString = (PWSTR) (lpo + 1);
  296. BytesReadSizeT = BytesReadDword;
  297. if (!::ReadProcessMemory(g_hCSRSS, rodsi.lpDebugStringData, pszLocalString, rodsi.nDebugStringLength * sizeof(WCHAR), &BytesReadSizeT))
  298. {
  299. ::ReportFailure("ReadProcessMemory(%p, %p, %p, %lu, %p) failed", g_hCSRSS, rodsi.lpDebugStringData, pszLocalString, rodsi.nDebugStringLength * sizeof(WCHAR), &BytesReadSizeT);
  300. goto Exit;
  301. }
  302. BytesReadDword = (DWORD)BytesReadSizeT;
  303. ASSERT(BytesReadDword == BytesReadSizeT);
  304. pszLocalString[rodsi.nDebugStringLength] = L'\0';
  305. }
  306. else
  307. {
  308. PSTR pszTempString = NULL;
  309. INT i = 0;
  310. INT j = 0;
  311. if ((pszTempString = (PSTR) ::HeapAlloc(Heap, 0, rodsi.nDebugStringLength)) == NULL)
  312. {
  313. ::ReportFailure("HeapAlloc(%p (== GetProcessHeap()), 0, %lu) failed", GetProcessHeap(), rodsi.nDebugStringLength);
  314. goto Exit;
  315. }
  316. BytesReadSizeT = BytesReadDword;
  317. if (!::ReadProcessMemory(g_hCSRSS, rodsi.lpDebugStringData, pszTempString, rodsi.nDebugStringLength, &BytesReadSizeT))
  318. {
  319. ::ReportFailure("ReadProcessMemory(%p, %p, %p, %lu, %p) failed", g_hCSRSS, rodsi.lpDebugStringData, pszTempString, rodsi.nDebugStringLength, &BytesReadSizeT);
  320. goto Exit;
  321. }
  322. BytesReadDword = (DWORD)BytesReadSizeT;
  323. ASSERT(BytesReadDword == BytesReadSizeT);
  324. if ((i = ::MultiByteToWideChar(CP_ACP, 0, pszTempString, BytesReadDword, NULL, 0)) == 0)
  325. {
  326. ::ReportFailure("MultiByteToWideChar(CP_ACP, 0, %p, %lu, NULL, 0) failed", pszTempString, BytesReadDword);
  327. goto Exit;
  328. }
  329. if ((lpo = (LPOVERLAPPED) ::HeapAlloc(Heap, 0, sizeof(OVERLAPPED) + ((i + 1) * sizeof(WCHAR)))) == NULL)
  330. {
  331. ::ReportFailure("HeapAlloc(%p (== GetProcessHeap()), 0, %lu) failed", GetProcessHeap(), sizeof(OVERLAPPED) + ((i + 1) * sizeof(WCHAR)));
  332. goto Exit;
  333. }
  334. pszLocalString = (PWSTR) (lpo + 1);
  335. if ((j = ::MultiByteToWideChar(CP_ACP, 0, pszTempString, BytesReadDword, pszLocalString, i * sizeof(WCHAR))) == 0)
  336. {
  337. ::ReportFailure("MultiByteToWideChar(CP_ACP, 0, %p, %lu, %p, %lu) failed", pszTempString, BytesReadDword, pszLocalString, i * sizeof(WCHAR));
  338. goto Exit;
  339. }
  340. pszLocalString[j] = L'\0';
  341. ::HeapFree(Heap, 0, pszTempString);
  342. }
  343. ::ZeroMemory(lpo, sizeof(OVERLAPPED));
  344. if (!::PostQueuedCompletionStatus(g_hCompletionPort, 0, NULL, lpo))
  345. {
  346. ::ReportFailure("PostQueuedCompletionStatus(%p, 0, NULL, %p) failed", g_hCompletionPort, 0, NULL, lpo);
  347. goto Exit;
  348. }
  349. }
  350. break;
  351. }
  352. }
  353. if (!::ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_EXCEPTION_NOT_HANDLED))
  354. {
  355. ::ReportFailure("ContinueDebugEvent(%lu, %lu, DBG_EXCEPTION_NOT_HANDLED) failed", de.dwProcessId, de.dwThreadId);
  356. goto Exit;
  357. }
  358. if (fp != NULL)
  359. {
  360. ::fprintf(fp, "\n");
  361. ::fflush(fp);
  362. }
  363. }
  364. iReturnStatus = EXIT_SUCCESS;
  365. Exit:
  366. if (g_fKdFusionMaskSet)
  367. {
  368. const DWORD dwLastError = ::GetLastError();
  369. ::WriteProcessMemory(g_hCSRSS, (PVOID) si.Address, &g_dwOldKdFusionMask, sizeof(g_dwOldKdFusionMask), &nBytesWritten);
  370. ::SetLastError(dwLastError);
  371. }
  372. return iReturnStatus;
  373. }
  374. DWORD
  375. WINAPI
  376. WorkerThreadThreadProc(
  377. LPVOID
  378. )
  379. {
  380. ULONG n = 0;
  381. LPOVERLAPPED lpoPrevious = NULL;
  382. ULONG nReps = 0;
  383. DWORD dwMSTimeout = 100;
  384. const HANDLE Heap = ::GetProcessHeap();
  385. for (;;)
  386. {
  387. DWORD dwNumberOfBytes = 0;
  388. ULONG_PTR ulpCompletionKey = 0;
  389. LPOVERLAPPED lpOverlapped = NULL;
  390. if (!::GetQueuedCompletionStatus(g_hCompletionPort, &dwNumberOfBytes, &ulpCompletionKey, &lpOverlapped, dwMSTimeout))
  391. {
  392. if (lpOverlapped == NULL)
  393. {
  394. if (lpoPrevious != NULL)
  395. {
  396. // timeout...
  397. PCWSTR psz2 = (PCWSTR) (lpoPrevious + 1);
  398. if (nReps != 0)
  399. ::printf("[%08lx:%lu] %ls", n++, nReps + 1, psz2);
  400. else
  401. ::printf("[%08lx] %ls", n++, psz2);
  402. ::fflush(stdout);
  403. ::HeapFree(Heap, 0, lpoPrevious);
  404. lpoPrevious = lpOverlapped;
  405. nReps = 0;
  406. dwMSTimeout = INFINITE;
  407. }
  408. }
  409. else
  410. {
  411. ::ReportFailure("GetQueuedCompletionStatus(%p, %p, %p, %p, INFINITE) failed", g_hCompletionPort, &dwNumberOfBytes, &ulpCompletionKey, &lpOverlapped);
  412. }
  413. }
  414. else
  415. {
  416. PCWSTR psz = (PCWSTR) (lpOverlapped + 1);
  417. dwMSTimeout = 100;
  418. if (lpoPrevious != NULL)
  419. {
  420. PCWSTR psz2 = (PCWSTR) (lpoPrevious + 1);
  421. if (::wcscmp(psz, psz2) == 0)
  422. {
  423. psz = NULL;
  424. nReps++;
  425. ::HeapFree(Heap, 0, lpOverlapped);
  426. }
  427. else
  428. {
  429. if (nReps != 0)
  430. ::printf("[%08lx:%lu] %ls", n++, nReps + 1, psz2);
  431. else
  432. ::printf("[%08lx] %ls", n++, psz2);
  433. ::fflush(stdout);
  434. ::HeapFree(Heap, 0, lpoPrevious);
  435. lpoPrevious = lpOverlapped;
  436. nReps = 0;
  437. }
  438. }
  439. else
  440. {
  441. // first one...
  442. lpoPrevious = lpOverlapped;
  443. }
  444. }
  445. }
  446. return 0;
  447. }
  448. BOOL
  449. EnableDebugPrivilege()
  450. {
  451. LUID PrivilegeValue;
  452. BOOL Result = FALSE;
  453. TOKEN_PRIVILEGES TokenPrivileges;
  454. TOKEN_PRIVILEGES OldTokenPrivileges;
  455. DWORD ReturnLength = 0;
  456. HANDLE TokenHandle = NULL;
  457. //
  458. // First, find out the LUID Value of the privilege
  459. //
  460. if(!::LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &PrivilegeValue)) {
  461. ::ReportFailure("LookupPrivilegeValueW(NULL, L\"SeDebugPrivilege\", %p) failed", &PrivilegeValue);
  462. goto Exit;
  463. }
  464. //
  465. // Get the token handle
  466. //
  467. if (!::OpenProcessToken (
  468. ::GetCurrentProcess(),
  469. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  470. &TokenHandle
  471. ))
  472. {
  473. ::ReportFailure("OpenProcessToken() failed");
  474. goto Exit;
  475. }
  476. //
  477. // Set up the privilege set we will need
  478. //
  479. TokenPrivileges.PrivilegeCount = 1;
  480. TokenPrivileges.Privileges[0].Luid = PrivilegeValue;
  481. TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  482. ReturnLength = sizeof(TOKEN_PRIVILEGES);
  483. if (!::AdjustTokenPrivileges (
  484. TokenHandle,
  485. FALSE,
  486. &TokenPrivileges,
  487. sizeof(OldTokenPrivileges),
  488. &OldTokenPrivileges,
  489. &ReturnLength
  490. ))
  491. {
  492. ::ReportFailure("AdjustTokenPrivileges(%p, FALSE, %p, %lu, %p, %p) failed", TokenHandle, &TokenPrivileges, sizeof(OldTokenPrivileges), &OldTokenPrivileges, &ReturnLength);
  493. goto Exit;
  494. }
  495. Result = TRUE;
  496. Exit:
  497. if (TokenHandle != NULL)
  498. {
  499. const DWORD dwLastError = ::GetLastError();
  500. ::CloseHandle(TokenHandle);
  501. ::SetLastError(dwLastError);
  502. }
  503. return Result;
  504. }