Source code of Windows XP (NT5)
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.

1259 lines
30 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. debug.c
  5. Abstract:
  6. This module implements Win32 Debug APIs
  7. Author:
  8. Mark Lucovsky (markl) 06-Feb-1991
  9. Revision History:
  10. --*/
  11. #include "basedll.h"
  12. #pragma hdrstop
  13. #define TmpHandleHead ((PTMPHANDLES *) (&NtCurrentTeb()->DbgSsReserved[0]))
  14. //
  15. // This structure is used to preserve the strange mechanisms used by win2k and nt4 to close the handles to open processes,
  16. // threads and main image file.
  17. //
  18. typedef struct _TMPHANDLES {
  19. struct _TMPHANDLES *Next;
  20. HANDLE Thread;
  21. HANDLE Process;
  22. DWORD dwProcessId;
  23. DWORD dwThreadId;
  24. BOOLEAN DeletePending;
  25. } TMPHANDLES, *PTMPHANDLES;
  26. VOID
  27. SaveThreadHandle (
  28. DWORD dwProcessId,
  29. DWORD dwThreadId,
  30. HANDLE HandleToThread)
  31. /*++
  32. Routine Description:
  33. This function saves away a handle to a thread in a thread specific list so we can close it later when the thread
  34. termination message is continued.
  35. Arguments:
  36. dwProcessId - Process ID of threads process
  37. dwThreadId - Thread ID of thread handle
  38. HandleToThread - Handle to be closed later
  39. Return Value:
  40. None.
  41. --*/
  42. {
  43. PTMPHANDLES Tmp;
  44. Tmp = RtlAllocateHeap (RtlProcessHeap(), 0, sizeof (TMPHANDLES));
  45. if (Tmp != NULL) {
  46. Tmp->Thread = HandleToThread;
  47. Tmp->Process = NULL;
  48. Tmp->dwProcessId = dwProcessId;
  49. Tmp->dwThreadId = dwThreadId;
  50. Tmp->DeletePending = FALSE;
  51. Tmp->Next = *TmpHandleHead;
  52. *TmpHandleHead = Tmp;
  53. }
  54. }
  55. VOID
  56. SaveProcessHandle (
  57. DWORD dwProcessId,
  58. HANDLE HandleToProcess
  59. )
  60. /*++
  61. Routine Description:
  62. This function saves away a handle to a process and file in a thread specific list so we can close it later
  63. when the process termination message is continued.
  64. Arguments:
  65. dwProcessId - Process ID of threads process
  66. HandleToProcess - Handle to be closed later
  67. HandleToFile - Handle to be closed later
  68. Return Value:
  69. None.
  70. --*/
  71. {
  72. PTMPHANDLES Tmp;
  73. Tmp = RtlAllocateHeap (RtlProcessHeap(), 0, sizeof (TMPHANDLES));
  74. if (Tmp != NULL) {
  75. Tmp->Process = HandleToProcess;
  76. Tmp->Thread = NULL;
  77. Tmp->dwProcessId = dwProcessId;
  78. Tmp->dwThreadId = 0;
  79. Tmp->DeletePending = FALSE;
  80. Tmp->Next = *TmpHandleHead;
  81. *TmpHandleHead = Tmp;
  82. }
  83. }
  84. VOID
  85. MarkThreadHandle (
  86. DWORD dwThreadId
  87. )
  88. /*++
  89. Routine Description:
  90. This function marks a saved thread handle so that the next time this thread is continued we close
  91. its handle
  92. Arguments:
  93. dwThreadId - Thread ID of thread handle
  94. Return Value:
  95. None.
  96. --*/
  97. {
  98. PTMPHANDLES Tmp;
  99. Tmp = *TmpHandleHead;
  100. while (Tmp != NULL) {
  101. if (Tmp->dwThreadId == dwThreadId) {
  102. Tmp->DeletePending = TRUE;
  103. break;
  104. }
  105. Tmp = Tmp->Next;
  106. }
  107. }
  108. VOID
  109. MarkProcessHandle (
  110. DWORD dwProcessId
  111. )
  112. {
  113. PTMPHANDLES Tmp;
  114. Tmp = *TmpHandleHead;
  115. while (Tmp != NULL) {
  116. if (Tmp->dwProcessId == dwProcessId && Tmp->dwThreadId == 0) {
  117. Tmp->DeletePending = TRUE;
  118. break;
  119. }
  120. Tmp = Tmp->Next;
  121. }
  122. }
  123. VOID
  124. RemoveHandles (
  125. DWORD dwThreadId,
  126. DWORD dwProcessId
  127. )
  128. /*++
  129. Routine Description:
  130. This function closes marked handles for this process and thread id
  131. Arguments:
  132. dwProcessId - Process ID of threads process
  133. dwThreadId - Thread ID of thread handle
  134. Return Value:
  135. None.
  136. --*/
  137. {
  138. PTMPHANDLES Tmp, *Last;
  139. Last = TmpHandleHead;
  140. Tmp = *Last;
  141. while (Tmp != NULL) {
  142. if (Tmp->DeletePending) {
  143. if (Tmp->dwProcessId == dwProcessId || Tmp->dwThreadId == dwThreadId) {
  144. if (Tmp->Thread != NULL) {
  145. CloseHandle (Tmp->Thread);
  146. }
  147. if (Tmp->Process != NULL) {
  148. CloseHandle (Tmp->Process);
  149. }
  150. *Last = Tmp->Next;
  151. RtlFreeHeap (RtlProcessHeap(), 0, Tmp);
  152. Tmp = *Last;
  153. continue;
  154. }
  155. }
  156. Last = &Tmp->Next;
  157. Tmp = Tmp->Next;
  158. }
  159. }
  160. VOID
  161. CloseAllProcessHandles (
  162. DWORD dwProcessId
  163. )
  164. /*++
  165. Routine Description:
  166. This function closes all saved handles when we stop debugging a single process
  167. Arguments:
  168. dwProcessId - Process ID of threads process
  169. Return Value:
  170. None.
  171. --*/
  172. {
  173. PTMPHANDLES Tmp, *Last;
  174. Last = TmpHandleHead;
  175. Tmp = *Last;
  176. while (Tmp != NULL) {
  177. if (Tmp->dwProcessId == dwProcessId) {
  178. if (Tmp->Thread != NULL) {
  179. CloseHandle (Tmp->Thread);
  180. }
  181. if (Tmp->Process != NULL) {
  182. CloseHandle (Tmp->Process);
  183. }
  184. *Last = Tmp->Next;
  185. RtlFreeHeap (RtlProcessHeap(), 0, Tmp);
  186. Tmp = *Last;
  187. continue;
  188. }
  189. Last = &Tmp->Next;
  190. Tmp = Tmp->Next;
  191. }
  192. }
  193. BOOL
  194. APIENTRY
  195. IsDebuggerPresent(
  196. VOID
  197. )
  198. /*++
  199. Routine Description:
  200. This function returns TRUE if the current process is being debugged
  201. and FALSE if not.
  202. Arguments:
  203. None.
  204. Return Value:
  205. None.
  206. --*/
  207. {
  208. return NtCurrentPeb()->BeingDebugged;
  209. }
  210. BOOL
  211. APIENTRY
  212. CheckRemoteDebuggerPresent(
  213. IN HANDLE hProcess,
  214. OUT PBOOL pbDebuggerPresent
  215. )
  216. /*++
  217. Routine Description:
  218. This function determines whether the remote process is being debugged.
  219. Arguments:
  220. hProcess - handle to the process
  221. pbDebuggerPresent - supplies a buffer to receive the result of the check
  222. TRUE - remote process is being debugged
  223. FALSE - remote process is not being debugged
  224. Return Value:
  225. TRUE - The function succeeded.
  226. FALSE - The function fail. Extended error status is available using
  227. GetLastError.
  228. --*/
  229. {
  230. HANDLE hDebugPort;
  231. NTSTATUS Status;
  232. if( (hProcess == NULL) || (pbDebuggerPresent == NULL) ) {
  233. SetLastError( ERROR_INVALID_PARAMETER );
  234. return FALSE;
  235. }
  236. Status = NtQueryInformationProcess(
  237. hProcess,
  238. ProcessDebugPort,
  239. (PVOID)(&hDebugPort),
  240. sizeof(hDebugPort),
  241. NULL
  242. );
  243. if( !NT_SUCCESS(Status) ) {
  244. BaseSetLastNTError( Status );
  245. return FALSE;
  246. }
  247. *pbDebuggerPresent = (hDebugPort != NULL);
  248. return TRUE;
  249. }
  250. //#ifdef i386
  251. //#pragma optimize("",off)
  252. //#endif // i386
  253. VOID
  254. APIENTRY
  255. DebugBreak(
  256. VOID
  257. )
  258. /*++
  259. Routine Description:
  260. This function causes a breakpoint exception to occur in the caller.
  261. This allows the calling thread to signal the debugger forcing it to
  262. take some action. If the process is not being debugged, the
  263. standard exception search logic is invoked. In most cases, this
  264. will cause the calling process to terminate (due to an unhandled
  265. breakpoint exception).
  266. Arguments:
  267. None.
  268. Return Value:
  269. None.
  270. --*/
  271. {
  272. DbgBreakPoint();
  273. }
  274. //#ifdef i386
  275. //#pragma optimize("",on)
  276. //#endif // i386
  277. VOID
  278. APIENTRY
  279. OutputDebugStringW(
  280. LPCWSTR lpOutputString
  281. )
  282. /*++
  283. Routine Description:
  284. UNICODE thunk to OutputDebugStringA
  285. --*/
  286. {
  287. UNICODE_STRING UnicodeString;
  288. ANSI_STRING AnsiString;
  289. NTSTATUS Status;
  290. RtlInitUnicodeString(&UnicodeString,lpOutputString);
  291. Status = RtlUnicodeStringToAnsiString(&AnsiString,&UnicodeString,TRUE);
  292. if ( !NT_SUCCESS(Status) ) {
  293. AnsiString.Buffer = "";
  294. }
  295. OutputDebugStringA(AnsiString.Buffer);
  296. if ( NT_SUCCESS(Status) ) {
  297. RtlFreeAnsiString(&AnsiString);
  298. }
  299. }
  300. #define DBWIN_TIMEOUT 10000
  301. HANDLE CreateDBWinMutex(VOID) {
  302. SECURITY_ATTRIBUTES SecurityAttributes;
  303. SECURITY_DESCRIPTOR sd;
  304. NTSTATUS Status;
  305. SID_IDENTIFIER_AUTHORITY authNT = SECURITY_NT_AUTHORITY;
  306. SID_IDENTIFIER_AUTHORITY authWorld = SECURITY_WORLD_SID_AUTHORITY;
  307. PSID psidSystem = NULL, psidAdmin = NULL, psidEveryone = NULL;
  308. PACL pAcl = NULL;
  309. DWORD cbAcl, aceIndex;
  310. HANDLE h = NULL;
  311. DWORD i;
  312. //
  313. // Get the system sid
  314. //
  315. Status = RtlAllocateAndInitializeSid(&authNT, 1, SECURITY_LOCAL_SYSTEM_RID,
  316. 0, 0, 0, 0, 0, 0, 0, &psidSystem);
  317. if (!NT_SUCCESS(Status))
  318. goto Exit;
  319. //
  320. // Get the Admin sid
  321. //
  322. Status = RtlAllocateAndInitializeSid(&authNT, 2, SECURITY_BUILTIN_DOMAIN_RID,
  323. DOMAIN_ALIAS_RID_ADMINS, 0, 0,
  324. 0, 0, 0, 0, &psidAdmin);
  325. if (!NT_SUCCESS(Status))
  326. goto Exit;
  327. //
  328. // Get the World sid
  329. //
  330. Status = RtlAllocateAndInitializeSid(&authWorld, 1, SECURITY_WORLD_RID,
  331. 0, 0, 0, 0, 0, 0, 0, &psidEveryone);
  332. if (!NT_SUCCESS(Status))
  333. goto Exit;
  334. //
  335. // Allocate space for the ACL
  336. //
  337. cbAcl = sizeof(ACL) +
  338. 3 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) +
  339. RtlLengthSid(psidSystem) +
  340. RtlLengthSid(psidAdmin) +
  341. RtlLengthSid(psidEveryone);
  342. pAcl = (PACL) GlobalAlloc(GMEM_FIXED, cbAcl);
  343. if (!pAcl) {
  344. goto Exit;
  345. }
  346. Status = RtlCreateAcl(pAcl, cbAcl, ACL_REVISION);
  347. if (!NT_SUCCESS(Status))
  348. goto Exit;
  349. //
  350. // Add Aces.
  351. //
  352. Status = RtlAddAccessAllowedAce(pAcl, ACL_REVISION, READ_CONTROL | SYNCHRONIZE | MUTEX_MODIFY_STATE, psidEveryone);
  353. if (!NT_SUCCESS(Status))
  354. goto Exit;
  355. Status = RtlAddAccessAllowedAce(pAcl, ACL_REVISION, MUTEX_ALL_ACCESS, psidSystem);
  356. if (!NT_SUCCESS(Status))
  357. goto Exit;
  358. Status = RtlAddAccessAllowedAce(pAcl, ACL_REVISION, MUTEX_ALL_ACCESS, psidAdmin);
  359. if (!NT_SUCCESS(Status))
  360. goto Exit;
  361. Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
  362. if (!NT_SUCCESS(Status))
  363. goto Exit;
  364. Status = RtlSetDaclSecurityDescriptor(&sd, TRUE, pAcl, FALSE);
  365. if (!NT_SUCCESS(Status))
  366. goto Exit;
  367. SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  368. SecurityAttributes.bInheritHandle = TRUE;
  369. SecurityAttributes.lpSecurityDescriptor = &sd;
  370. i = 0;
  371. while (1) {
  372. h = OpenMutex (READ_CONTROL | SYNCHRONIZE | MUTEX_MODIFY_STATE,
  373. TRUE,
  374. "DBWinMutex");
  375. if (h != NULL) {
  376. break;
  377. }
  378. h = CreateMutex(&SecurityAttributes, FALSE, "DBWinMutex");
  379. if (h != NULL || GetLastError () != ERROR_ACCESS_DENIED || i++ > 100) {
  380. break;
  381. }
  382. }
  383. Exit:
  384. if (psidSystem) {
  385. RtlFreeSid(psidSystem);
  386. }
  387. if (psidAdmin) {
  388. RtlFreeSid(psidAdmin);
  389. }
  390. if (psidEveryone) {
  391. RtlFreeSid(psidEveryone);
  392. }
  393. if (pAcl) {
  394. GlobalFree (pAcl);
  395. }
  396. return h;
  397. }
  398. VOID
  399. APIENTRY
  400. OutputDebugStringA(
  401. IN LPCSTR lpOutputString
  402. )
  403. /*++
  404. Routine Description:
  405. This function allows an application to send a string to its debugger
  406. for display. If the application is not being debugged, but the
  407. system debugger is active, the system debugger displays the string.
  408. Otherwise, this function has no effect.
  409. Arguments:
  410. lpOutputString - Supplies the address of the debug string to be sent
  411. to the debugger.
  412. Return Value:
  413. None.
  414. --*/
  415. {
  416. ULONG_PTR ExceptionArguments[2];
  417. //
  418. // Raise an exception. If APP is being debugged, the debugger
  419. // will catch and handle this. Otherwise, kernel debugger is
  420. // called.
  421. //
  422. try {
  423. ExceptionArguments[0]=strlen(lpOutputString)+1;
  424. ExceptionArguments[1]=(ULONG_PTR)lpOutputString;
  425. RaiseException(DBG_PRINTEXCEPTION_C,0,2,ExceptionArguments);
  426. }
  427. except(EXCEPTION_EXECUTE_HANDLER) {
  428. //
  429. // We caught the debug exception, so there's no user-mode
  430. // debugger. If there is a DBWIN running, send the string
  431. // to it. If not, use DbgPrint to send it to the kernel
  432. // debugger. DbgPrint can only handle 511 characters at a
  433. // time, so force-feed it.
  434. //
  435. char szBuf[512];
  436. size_t cchRemaining;
  437. LPCSTR pszRemainingOutput;
  438. HANDLE SharedFile = NULL;
  439. LPSTR SharedMem = NULL;
  440. HANDLE AckEvent = NULL;
  441. HANDLE ReadyEvent = NULL;
  442. static HANDLE DBWinMutex = NULL;
  443. static BOOLEAN CantGetMutex = FALSE;
  444. //
  445. // look for DBWIN.
  446. //
  447. if (!DBWinMutex && !CantGetMutex) {
  448. DBWinMutex = CreateDBWinMutex();
  449. if (!DBWinMutex)
  450. CantGetMutex = TRUE;
  451. }
  452. if (DBWinMutex) {
  453. WaitForSingleObject(DBWinMutex, INFINITE);
  454. SharedFile = OpenFileMapping(FILE_MAP_WRITE, FALSE, "DBWIN_BUFFER");
  455. if (SharedFile) {
  456. SharedMem = MapViewOfFile( SharedFile,
  457. FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, 0);
  458. if (SharedMem) {
  459. AckEvent = OpenEvent(SYNCHRONIZE, FALSE,
  460. "DBWIN_BUFFER_READY");
  461. if (AckEvent) {
  462. ReadyEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE,
  463. "DBWIN_DATA_READY");
  464. }
  465. }
  466. }
  467. if (!ReadyEvent) {
  468. ReleaseMutex(DBWinMutex);
  469. }
  470. }
  471. try {
  472. pszRemainingOutput = lpOutputString;
  473. cchRemaining = strlen(pszRemainingOutput);
  474. while (cchRemaining > 0) {
  475. int used;
  476. if (ReadyEvent && WaitForSingleObject(AckEvent, DBWIN_TIMEOUT)
  477. == WAIT_OBJECT_0) {
  478. *((DWORD *)SharedMem) = GetCurrentProcessId();
  479. used = (int)((cchRemaining < 4095 - sizeof(DWORD)) ?
  480. cchRemaining : (4095 - sizeof(DWORD)));
  481. RtlCopyMemory(SharedMem+sizeof(DWORD),
  482. pszRemainingOutput,
  483. used);
  484. SharedMem[used+sizeof(DWORD)] = 0;
  485. SetEvent(ReadyEvent);
  486. }
  487. else {
  488. used = (int)((cchRemaining < sizeof(szBuf) - 1) ?
  489. cchRemaining : (int)(sizeof(szBuf) - 1));
  490. RtlCopyMemory(szBuf, pszRemainingOutput, used);
  491. szBuf[used] = 0;
  492. DbgPrint("%s", szBuf);
  493. }
  494. pszRemainingOutput += used;
  495. cchRemaining -= used;
  496. }
  497. }
  498. except(STATUS_ACCESS_VIOLATION == GetExceptionCode()) {
  499. DbgPrint("\nOutputDebugString faulted during output\n");
  500. }
  501. if (AckEvent) {
  502. CloseHandle(AckEvent);
  503. }
  504. if (SharedMem) {
  505. UnmapViewOfFile(SharedMem);
  506. }
  507. if (SharedFile) {
  508. CloseHandle(SharedFile);
  509. }
  510. if (ReadyEvent) {
  511. CloseHandle(ReadyEvent);
  512. ReleaseMutex(DBWinMutex);
  513. }
  514. }
  515. }
  516. BOOL
  517. APIENTRY
  518. WaitForDebugEvent(
  519. LPDEBUG_EVENT lpDebugEvent,
  520. DWORD dwMilliseconds
  521. )
  522. /*++
  523. Routine Description:
  524. A debugger waits for a debug event to occur in one of its debuggees
  525. using WaitForDebugEvent:
  526. Upon successful completion of this API, the lpDebugEvent structure
  527. contains the relevant information of the debug event.
  528. Arguments:
  529. lpDebugEvent - Receives information specifying the type of debug
  530. event that occured.
  531. dwMilliseconds - A time-out value that specifies the relative time,
  532. in milliseconds, over which the wait is to be completed. A
  533. timeout value of 0 specified that the wait is to timeout
  534. immediately. This allows an application to test for debug
  535. events A timeout value of -1 specifies an infinite timeout
  536. period.
  537. Return Value:
  538. TRUE - The operation was successful.
  539. FALSE/NULL - The operation failed (or timed out). Extended error
  540. status is available using GetLastError.
  541. --*/
  542. {
  543. NTSTATUS Status;
  544. DBGUI_WAIT_STATE_CHANGE StateChange;
  545. LARGE_INTEGER TimeOut;
  546. PLARGE_INTEGER pTimeOut;
  547. pTimeOut = BaseFormatTimeOut(&TimeOut,dwMilliseconds);
  548. again:
  549. Status = DbgUiWaitStateChange(&StateChange,pTimeOut);
  550. if ( Status == STATUS_ALERTED || Status == STATUS_USER_APC) {
  551. goto again;
  552. }
  553. if ( !NT_SUCCESS(Status) && Status != DBG_UNABLE_TO_PROVIDE_HANDLE ) {
  554. BaseSetLastNTError(Status);
  555. return FALSE;
  556. }
  557. if ( Status == STATUS_TIMEOUT ) {
  558. SetLastError(ERROR_SEM_TIMEOUT);
  559. return FALSE;
  560. }
  561. Status = DbgUiConvertStateChangeStructure (&StateChange, lpDebugEvent);
  562. if (!NT_SUCCESS (Status)) {
  563. BaseSetLastNTError(Status);
  564. return FALSE;
  565. }
  566. switch (lpDebugEvent->dwDebugEventCode) {
  567. case CREATE_THREAD_DEBUG_EVENT :
  568. //
  569. // Save away thread handle for later cleanup.
  570. //
  571. SaveThreadHandle (lpDebugEvent->dwProcessId,
  572. lpDebugEvent->dwThreadId,
  573. lpDebugEvent->u.CreateThread.hThread);
  574. break;
  575. case CREATE_PROCESS_DEBUG_EVENT :
  576. SaveProcessHandle (lpDebugEvent->dwProcessId,
  577. lpDebugEvent->u.CreateProcessInfo.hProcess);
  578. SaveThreadHandle (lpDebugEvent->dwProcessId,
  579. lpDebugEvent->dwThreadId,
  580. lpDebugEvent->u.CreateProcessInfo.hThread);
  581. break;
  582. case EXIT_THREAD_DEBUG_EVENT :
  583. MarkThreadHandle (lpDebugEvent->dwThreadId);
  584. break;
  585. case EXIT_PROCESS_DEBUG_EVENT :
  586. MarkThreadHandle (lpDebugEvent->dwThreadId);
  587. MarkProcessHandle (lpDebugEvent->dwProcessId);
  588. break;
  589. case OUTPUT_DEBUG_STRING_EVENT :
  590. case RIP_EVENT :
  591. case EXCEPTION_DEBUG_EVENT :
  592. break;
  593. case LOAD_DLL_DEBUG_EVENT :
  594. break;
  595. case UNLOAD_DLL_DEBUG_EVENT :
  596. break;
  597. default:
  598. return FALSE;
  599. }
  600. return TRUE;
  601. }
  602. BOOL
  603. APIENTRY
  604. ContinueDebugEvent(
  605. DWORD dwProcessId,
  606. DWORD dwThreadId,
  607. DWORD dwContinueStatus
  608. )
  609. /*++
  610. Routine Description:
  611. A debugger can continue a thread that previously reported a debug
  612. event using ContinueDebugEvent.
  613. Upon successful completion of this API, the specified thread is
  614. continued. Depending on the debug event previously reported by the
  615. thread certain side effects occur.
  616. If the continued thread previously reported an exit thread debug
  617. event, the handle that the debugger has to the thread is closed.
  618. If the continued thread previously reported an exit process debug
  619. event, the handles that the debugger has to the thread and to the
  620. process are closed.
  621. Arguments:
  622. dwProcessId - Supplies the process id of the process to continue. The
  623. combination of process id and thread id must identify a thread that
  624. has previously reported a debug event.
  625. dwThreadId - Supplies the thread id of the thread to continue. The
  626. combination of process id and thread id must identify a thread that
  627. has previously reported a debug event.
  628. dwContinueStatus - Supplies the continuation status for the thread
  629. reporting the debug event.
  630. dwContinueStatus Values:
  631. DBG_CONTINUE - If the thread being continued had
  632. previously reported an exception event, continuing with
  633. this value causes all exception processing to stop and
  634. the thread continues execution. For any other debug
  635. event, this continuation status simply allows the thread
  636. to continue execution.
  637. DBG_EXCEPTION_NOT_HANDLED - If the thread being continued
  638. had previously reported an exception event, continuing
  639. with this value causes exception processing to continue.
  640. If this is a first chance exception event, then
  641. structured exception handler search/dispatch logic is
  642. invoked. Otherwise, the process is terminated. For any
  643. other debug event, this continuation status simply
  644. allows the thread to continue execution.
  645. DBG_TERMINATE_THREAD - After all continue side effects are
  646. processed, this continuation status causes the thread to
  647. jump to a call to ExitThread. The exit code is the
  648. value DBG_TERMINATE_THREAD.
  649. DBG_TERMINATE_PROCESS - After all continue side effects are
  650. processed, this continuation status causes the thread to
  651. jump to a call to ExitProcess. The exit code is the
  652. value DBG_TERMINATE_PROCESS.
  653. Return Value:
  654. TRUE - The operation was successful.
  655. FALSE/NULL - The operation failed. Extended error status is available
  656. using GetLastError.
  657. --*/
  658. {
  659. NTSTATUS Status;
  660. CLIENT_ID ClientId;
  661. ClientId.UniqueProcess = (HANDLE)LongToHandle(dwProcessId);
  662. ClientId.UniqueThread = (HANDLE)LongToHandle(dwThreadId);
  663. Status = DbgUiContinue(&ClientId,(NTSTATUS)dwContinueStatus);
  664. if ( !NT_SUCCESS(Status) ) {
  665. BaseSetLastNTError(Status);
  666. return FALSE;
  667. }
  668. RemoveHandles (dwThreadId, dwProcessId);
  669. return TRUE;
  670. }
  671. HANDLE
  672. ProcessIdToHandle (
  673. IN DWORD dwProcessId
  674. )
  675. {
  676. OBJECT_ATTRIBUTES oa;
  677. HANDLE Process;
  678. CLIENT_ID ClientId;
  679. NTSTATUS Status;
  680. if (dwProcessId == -1) {
  681. ClientId.UniqueProcess = CsrGetProcessId ();
  682. } else {
  683. ClientId.UniqueProcess = LongToHandle(dwProcessId);
  684. }
  685. ClientId.UniqueThread = NULL;
  686. InitializeObjectAttributes (&oa, NULL, 0, NULL, NULL);
  687. Status = NtOpenProcess (&Process,
  688. PROCESS_SET_PORT|PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|
  689. PROCESS_VM_WRITE|PROCESS_VM_READ,
  690. &oa,
  691. &ClientId);
  692. if (!NT_SUCCESS(Status)) {
  693. BaseSetLastNTError(Status);
  694. Process = NULL;
  695. }
  696. return Process;
  697. }
  698. BOOL
  699. APIENTRY
  700. DebugActiveProcess(
  701. DWORD dwProcessId
  702. )
  703. /*++
  704. Routine Description:
  705. This API allows a debugger to attach to an active process and debug
  706. the process. The debugger specifies the process that it wants to
  707. debug through the process id of the target process. The debugger
  708. gets debug access to the process as if it had created the process
  709. with the DEBUG_ONLY_THIS_PROCESS creation flag.
  710. The debugger must have approriate access to the calling process such
  711. that it can open the process for PROCESS_ALL_ACCESS. For Dos/Win32
  712. this never fails (the process id just has to be a valid process id).
  713. For NT/Win32 this check can fail if the target process was created
  714. with a security descriptor that denies the debugger approriate
  715. access.
  716. Once the process id check has been made and the system determines
  717. that a valid debug attachment is being made, this call returns
  718. success to the debugger. The debugger is then expected to wait for
  719. debug events. The system will suspend all threads in the process
  720. and feed the debugger debug events representing the current state of
  721. the process.
  722. The system will feed the debugger a single create process debug
  723. event representing the process specified by dwProcessId. The
  724. lpStartAddress field of the create process debug event is NULL. For
  725. each thread currently part of the process, the system will send a
  726. create thread debug event. The lpStartAddress field of the create
  727. thread debug event is NULL. For each DLL currently loaded into the
  728. address space of the target process, the system will send a LoadDll
  729. debug event. The system will arrange for the first thread in the
  730. process to execute a breakpoint instruction after it is resumed.
  731. Continuing this thread causes the thread to return to whatever it
  732. was doing prior to the debug attach.
  733. After all of this has been done, the system resumes all threads within
  734. the process. When the first thread in the process resumes, it will
  735. execute a breakpoint instruction causing an exception debug event
  736. to be sent to the debugger.
  737. All future debug events are sent to the debugger using the normal
  738. mechanism and rules.
  739. Arguments:
  740. dwProcessId - Supplies the process id of a process the caller
  741. wants to debug.
  742. Return Value:
  743. TRUE - The operation was successful.
  744. FALSE/NULL - The operation failed. Extended error status is available
  745. using GetLastError.
  746. --*/
  747. {
  748. HANDLE Process;
  749. NTSTATUS Status, Status1;
  750. //
  751. // Connect to dbgss as a user interface
  752. //
  753. Status = DbgUiConnectToDbg ();
  754. if (!NT_SUCCESS (Status)) {
  755. BaseSetLastNTError (Status);
  756. return FALSE;
  757. }
  758. //
  759. // Convert the process ID to a handle
  760. //
  761. Process = ProcessIdToHandle (dwProcessId);
  762. if (Process == NULL) {
  763. return FALSE;
  764. }
  765. Status = DbgUiDebugActiveProcess (Process);
  766. if (!NT_SUCCESS (Status)) {
  767. Status1 = NtClose (Process);
  768. ASSERT (NT_SUCCESS (Status1));
  769. BaseSetLastNTError (Status);
  770. return FALSE;
  771. }
  772. Status1 = NtClose (Process);
  773. ASSERT (NT_SUCCESS (Status1));
  774. return TRUE;
  775. }
  776. BOOL
  777. APIENTRY
  778. DebugActiveProcessStop(
  779. DWORD dwProcessId
  780. )
  781. /*++
  782. Routine Description:
  783. Arguments:
  784. dwProcessId - Supplies the process id of a process the caller
  785. wants to stop debugging.
  786. Return Value:
  787. TRUE - The operation was successful.
  788. FALSE/NULL - The operation failed. Extended error status is available
  789. using GetLastError.
  790. --*/
  791. {
  792. HANDLE Process, Thread;
  793. NTSTATUS Status;
  794. NTSTATUS Status1;
  795. DWORD ThreadId;
  796. Process = ProcessIdToHandle (dwProcessId);
  797. if (Process == NULL) {
  798. return FALSE;
  799. }
  800. //
  801. // Tell dbgss we have finished with this process.
  802. //
  803. CloseAllProcessHandles (dwProcessId);
  804. Status = DbgUiStopDebugging (Process);
  805. Status1 = NtClose (Process);
  806. ASSERT (NT_SUCCESS (Status1));
  807. if (!NT_SUCCESS(Status)) {
  808. SetLastError(ERROR_ACCESS_DENIED);
  809. return FALSE;
  810. }
  811. return TRUE;
  812. }
  813. BOOL
  814. APIENTRY
  815. DebugBreakProcess (
  816. IN HANDLE Process
  817. )
  818. /*++
  819. Routine Description:
  820. This functions creates a thread inside the target process that issues a break.
  821. Arguments:
  822. Process - Handle to process
  823. Return Value:
  824. TRUE - The operation was successful
  825. FALSE/NULL - The operation failed. Extended error status is available
  826. using GetLastError.
  827. --*/
  828. {
  829. NTSTATUS Status;
  830. Status = DbgUiIssueRemoteBreakin (Process);
  831. if (NT_SUCCESS (Status)) {
  832. return TRUE;
  833. } else {
  834. BaseSetLastNTError (Status);
  835. return FALSE;
  836. }
  837. }
  838. BOOL
  839. APIENTRY
  840. DebugSetProcessKillOnExit (
  841. IN BOOL KillOnExit
  842. )
  843. /*++
  844. Routine Description:
  845. This functions sets the action to be performed when the debugging thread dies
  846. Arguments:
  847. KillOnExit - TRUE: Kill debugged processes on exit, FALSE: Detatch on debug exit
  848. Return Value:
  849. TRUE - The operation was successful
  850. FALSE/NULL - The operation failed. Extended error status is available
  851. using GetLastError.
  852. --*/
  853. {
  854. HANDLE DebugHandle;
  855. ULONG Flags;
  856. NTSTATUS Status;
  857. DebugHandle = DbgUiGetThreadDebugObject ();
  858. if (DebugHandle == NULL) {
  859. BaseSetLastNTError (STATUS_INVALID_HANDLE);
  860. return FALSE;
  861. }
  862. if (KillOnExit) {
  863. Flags = DEBUG_KILL_ON_CLOSE;
  864. } else {
  865. Flags = 0;
  866. }
  867. Status = NtSetInformationDebugObject (DebugHandle,
  868. DebugObjectFlags,
  869. &Flags,
  870. sizeof (Flags),
  871. NULL);
  872. if (!NT_SUCCESS (Status)) {
  873. BaseSetLastNTError (Status);
  874. return FALSE;
  875. }
  876. return TRUE;
  877. }
  878. BOOL
  879. APIENTRY
  880. GetThreadSelectorEntry(
  881. HANDLE hThread,
  882. DWORD dwSelector,
  883. LPLDT_ENTRY lpSelectorEntry
  884. )
  885. /*++
  886. Routine Description:
  887. This function is used to return a descriptor table entry for the
  888. specified thread corresponding to the specified selector.
  889. This API is only functional on x86 based systems. For non x86 based
  890. systems. A value of FALSE is returned.
  891. This API is used by a debugger so that it can convert segment
  892. relative addresses to linear virtual address (since this is the only
  893. format supported by ReadProcessMemory and WriteProcessMemory.
  894. Arguments:
  895. hThread - Supplies a handle to the thread that contains the
  896. specified selector. The handle must have been created with
  897. THREAD_QUERY_INFORMATION access.
  898. dwSelector - Supplies the selector value to lookup. The selector
  899. value may be a global selector or a local selector.
  900. lpSelectorEntry - If the specified selector is contained withing the
  901. threads descriptor tables, this parameter returns the selector
  902. entry corresponding to the specified selector value. This data
  903. can be used to compute the linear base address that segment
  904. relative addresses refer to.
  905. Return Value:
  906. TRUE - The operation was successful
  907. FALSE/NULL - The operation failed. Extended error status is available
  908. using GetLastError.
  909. --*/
  910. {
  911. #if defined(i386)
  912. DESCRIPTOR_TABLE_ENTRY DescriptorEntry;
  913. NTSTATUS Status;
  914. DescriptorEntry.Selector = dwSelector;
  915. Status = NtQueryInformationThread(
  916. hThread,
  917. ThreadDescriptorTableEntry,
  918. &DescriptorEntry,
  919. sizeof(DescriptorEntry),
  920. NULL
  921. );
  922. if ( !NT_SUCCESS(Status) ) {
  923. BaseSetLastNTError(Status);
  924. return FALSE;
  925. }
  926. *lpSelectorEntry = DescriptorEntry.Descriptor;
  927. return TRUE;
  928. #else
  929. BaseSetLastNTError(STATUS_NOT_SUPPORTED);
  930. return FALSE;
  931. #endif // i386
  932. }