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.

1277 lines
31 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. //
  321. // Get the Admin sid
  322. //
  323. Status = RtlAllocateAndInitializeSid(&authNT, 2, SECURITY_BUILTIN_DOMAIN_RID,
  324. DOMAIN_ALIAS_RID_ADMINS, 0, 0,
  325. 0, 0, 0, 0, &psidAdmin);
  326. if (!NT_SUCCESS(Status)) {
  327. goto Exit;
  328. }
  329. //
  330. // Get the World sid
  331. //
  332. Status = RtlAllocateAndInitializeSid(&authWorld, 1, SECURITY_WORLD_RID,
  333. 0, 0, 0, 0, 0, 0, 0, &psidEveryone);
  334. if (!NT_SUCCESS(Status)) {
  335. goto Exit;
  336. }
  337. //
  338. // Allocate space for the ACL
  339. //
  340. cbAcl = sizeof(ACL) +
  341. 3 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) +
  342. RtlLengthSid(psidSystem) +
  343. RtlLengthSid(psidAdmin) +
  344. RtlLengthSid(psidEveryone);
  345. pAcl = (PACL) GlobalAlloc(GMEM_FIXED, cbAcl);
  346. if (!pAcl) {
  347. goto Exit;
  348. }
  349. Status = RtlCreateAcl(pAcl, cbAcl, ACL_REVISION);
  350. if (!NT_SUCCESS(Status)) {
  351. goto Exit;
  352. }
  353. //
  354. // Add Aces.
  355. //
  356. Status = RtlAddAccessAllowedAce(pAcl, ACL_REVISION, READ_CONTROL | SYNCHRONIZE | MUTEX_MODIFY_STATE, psidEveryone);
  357. if (!NT_SUCCESS(Status)) {
  358. goto Exit;
  359. }
  360. Status = RtlAddAccessAllowedAce(pAcl, ACL_REVISION, MUTEX_ALL_ACCESS, psidSystem);
  361. if (!NT_SUCCESS(Status)) {
  362. goto Exit;
  363. }
  364. Status = RtlAddAccessAllowedAce(pAcl, ACL_REVISION, MUTEX_ALL_ACCESS, psidAdmin);
  365. if (!NT_SUCCESS(Status)) {
  366. goto Exit;
  367. }
  368. Status = RtlCreateSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
  369. if (!NT_SUCCESS(Status)) {
  370. goto Exit;
  371. }
  372. Status = RtlSetDaclSecurityDescriptor(&sd, TRUE, pAcl, FALSE);
  373. if (!NT_SUCCESS(Status)) {
  374. goto Exit;
  375. }
  376. SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  377. SecurityAttributes.bInheritHandle = FALSE;
  378. SecurityAttributes.lpSecurityDescriptor = &sd;
  379. i = 0;
  380. while (1) {
  381. h = OpenMutex (READ_CONTROL | SYNCHRONIZE | MUTEX_MODIFY_STATE,
  382. FALSE,
  383. "DBWinMutex");
  384. if (h != NULL) {
  385. break;
  386. }
  387. h = CreateMutex(&SecurityAttributes, FALSE, "DBWinMutex");
  388. if (h != NULL || GetLastError () != ERROR_ACCESS_DENIED || i++ > 100) {
  389. break;
  390. }
  391. }
  392. Exit:
  393. if (psidSystem) {
  394. RtlFreeSid(psidSystem);
  395. }
  396. if (psidAdmin) {
  397. RtlFreeSid(psidAdmin);
  398. }
  399. if (psidEveryone) {
  400. RtlFreeSid(psidEveryone);
  401. }
  402. if (pAcl) {
  403. GlobalFree (pAcl);
  404. }
  405. return h;
  406. }
  407. VOID
  408. APIENTRY
  409. OutputDebugStringA(
  410. IN LPCSTR lpOutputString
  411. )
  412. /*++
  413. Routine Description:
  414. This function allows an application to send a string to its debugger
  415. for display. If the application is not being debugged, but the
  416. system debugger is active, the system debugger displays the string.
  417. Otherwise, this function has no effect.
  418. Arguments:
  419. lpOutputString - Supplies the address of the debug string to be sent
  420. to the debugger.
  421. Return Value:
  422. None.
  423. --*/
  424. {
  425. ULONG_PTR ExceptionArguments[2];
  426. DWORD WaitStatus;
  427. //
  428. // Raise an exception. If APP is being debugged, the debugger
  429. // will catch and handle this. Otherwise, kernel debugger is
  430. // called.
  431. //
  432. try {
  433. ExceptionArguments[0] = strlen (lpOutputString)+1;
  434. ExceptionArguments[1] = (ULONG_PTR)lpOutputString;
  435. RaiseException (DBG_PRINTEXCEPTION_C,0,2,ExceptionArguments);
  436. } except (EXCEPTION_EXECUTE_HANDLER) {
  437. //
  438. // We caught the debug exception, so there's no user-mode
  439. // debugger. If there is a DBWIN running, send the string
  440. // to it. If not, use DbgPrint to send it to the kernel
  441. // debugger. DbgPrint can only handle 511 characters at a
  442. // time, so force-feed it.
  443. //
  444. char szBuf[512];
  445. size_t cchRemaining;
  446. LPCSTR pszRemainingOutput;
  447. DWORD OldError;
  448. HANDLE SharedFile = NULL;
  449. LPSTR SharedMem = NULL;
  450. HANDLE AckEvent = NULL;
  451. HANDLE ReadyEvent = NULL;
  452. static HANDLE DBWinMutex = NULL;
  453. static BOOLEAN CantGetMutex = FALSE;
  454. OldError = GetLastError ();
  455. //
  456. // look for DBWIN.
  457. //
  458. if (!DBWinMutex && !CantGetMutex) {
  459. HANDLE MutexHandle;
  460. MutexHandle = CreateDBWinMutex();
  461. if (MutexHandle == NULL) {
  462. CantGetMutex = TRUE;
  463. } else {
  464. if (InterlockedCompareExchangePointer (&DBWinMutex, MutexHandle, NULL) != NULL) {
  465. CloseHandle (MutexHandle);
  466. }
  467. }
  468. }
  469. if (DBWinMutex) {
  470. WaitStatus = WaitForSingleObject(DBWinMutex, DBWIN_TIMEOUT);
  471. if (WaitStatus == WAIT_OBJECT_0 || WaitStatus == WAIT_ABANDONED) {
  472. SharedFile = OpenFileMapping(FILE_MAP_WRITE, FALSE, "DBWIN_BUFFER");
  473. if (SharedFile) {
  474. SharedMem = MapViewOfFile (SharedFile,
  475. FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, 0);
  476. if (SharedMem) {
  477. AckEvent = OpenEvent(SYNCHRONIZE, FALSE,
  478. "DBWIN_BUFFER_READY");
  479. if (AckEvent) {
  480. ReadyEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE,
  481. "DBWIN_DATA_READY");
  482. }
  483. }
  484. }
  485. if (!ReadyEvent) {
  486. ReleaseMutex(DBWinMutex);
  487. }
  488. }
  489. }
  490. try {
  491. pszRemainingOutput = lpOutputString;
  492. cchRemaining = strlen(pszRemainingOutput);
  493. while (cchRemaining > 0) {
  494. int used;
  495. if (ReadyEvent && WaitForSingleObject(AckEvent, DBWIN_TIMEOUT)
  496. == WAIT_OBJECT_0) {
  497. *((DWORD *)SharedMem) = GetCurrentProcessId();
  498. used = (int)((cchRemaining < 4095 - sizeof(DWORD)) ?
  499. cchRemaining : (4095 - sizeof(DWORD)));
  500. RtlCopyMemory(SharedMem+sizeof(DWORD),
  501. pszRemainingOutput,
  502. used);
  503. SharedMem[used+sizeof(DWORD)] = 0;
  504. SetEvent(ReadyEvent);
  505. } else {
  506. used = (int)((cchRemaining < sizeof(szBuf) - 1) ?
  507. cchRemaining : (int)(sizeof(szBuf) - 1));
  508. RtlCopyMemory(szBuf, pszRemainingOutput, used);
  509. szBuf[used] = 0;
  510. DbgPrint("%s", szBuf);
  511. }
  512. pszRemainingOutput += used;
  513. cchRemaining -= used;
  514. }
  515. } except (EXCEPTION_EXECUTE_HANDLER) {
  516. DbgPrint("\nOutputDebugString faulted during output\n");
  517. }
  518. if (AckEvent) {
  519. CloseHandle(AckEvent);
  520. }
  521. if (SharedMem) {
  522. UnmapViewOfFile(SharedMem);
  523. }
  524. if (SharedFile) {
  525. CloseHandle(SharedFile);
  526. }
  527. if (ReadyEvent) {
  528. CloseHandle(ReadyEvent);
  529. ReleaseMutex(DBWinMutex);
  530. }
  531. SetLastError (OldError);
  532. }
  533. }
  534. BOOL
  535. APIENTRY
  536. WaitForDebugEvent(
  537. LPDEBUG_EVENT lpDebugEvent,
  538. DWORD dwMilliseconds
  539. )
  540. /*++
  541. Routine Description:
  542. A debugger waits for a debug event to occur in one of its debuggees
  543. using WaitForDebugEvent:
  544. Upon successful completion of this API, the lpDebugEvent structure
  545. contains the relevant information of the debug event.
  546. Arguments:
  547. lpDebugEvent - Receives information specifying the type of debug
  548. event that occured.
  549. dwMilliseconds - A time-out value that specifies the relative time,
  550. in milliseconds, over which the wait is to be completed. A
  551. timeout value of 0 specified that the wait is to timeout
  552. immediately. This allows an application to test for debug
  553. events A timeout value of -1 specifies an infinite timeout
  554. period.
  555. Return Value:
  556. TRUE - The operation was successful.
  557. FALSE/NULL - The operation failed (or timed out). Extended error
  558. status is available using GetLastError.
  559. --*/
  560. {
  561. NTSTATUS Status;
  562. DBGUI_WAIT_STATE_CHANGE StateChange;
  563. LARGE_INTEGER TimeOut;
  564. PLARGE_INTEGER pTimeOut;
  565. pTimeOut = BaseFormatTimeOut(&TimeOut,dwMilliseconds);
  566. again:
  567. Status = DbgUiWaitStateChange(&StateChange,pTimeOut);
  568. if ( Status == STATUS_ALERTED || Status == STATUS_USER_APC) {
  569. goto again;
  570. }
  571. if ( !NT_SUCCESS(Status) && Status != DBG_UNABLE_TO_PROVIDE_HANDLE ) {
  572. BaseSetLastNTError(Status);
  573. return FALSE;
  574. }
  575. if ( Status == STATUS_TIMEOUT ) {
  576. SetLastError(ERROR_SEM_TIMEOUT);
  577. return FALSE;
  578. }
  579. Status = DbgUiConvertStateChangeStructure (&StateChange, lpDebugEvent);
  580. if (!NT_SUCCESS (Status)) {
  581. BaseSetLastNTError(Status);
  582. return FALSE;
  583. }
  584. switch (lpDebugEvent->dwDebugEventCode) {
  585. case CREATE_THREAD_DEBUG_EVENT :
  586. //
  587. // Save away thread handle for later cleanup.
  588. //
  589. SaveThreadHandle (lpDebugEvent->dwProcessId,
  590. lpDebugEvent->dwThreadId,
  591. lpDebugEvent->u.CreateThread.hThread);
  592. break;
  593. case CREATE_PROCESS_DEBUG_EVENT :
  594. SaveProcessHandle (lpDebugEvent->dwProcessId,
  595. lpDebugEvent->u.CreateProcessInfo.hProcess);
  596. SaveThreadHandle (lpDebugEvent->dwProcessId,
  597. lpDebugEvent->dwThreadId,
  598. lpDebugEvent->u.CreateProcessInfo.hThread);
  599. break;
  600. case EXIT_THREAD_DEBUG_EVENT :
  601. MarkThreadHandle (lpDebugEvent->dwThreadId);
  602. break;
  603. case EXIT_PROCESS_DEBUG_EVENT :
  604. MarkThreadHandle (lpDebugEvent->dwThreadId);
  605. MarkProcessHandle (lpDebugEvent->dwProcessId);
  606. break;
  607. case OUTPUT_DEBUG_STRING_EVENT :
  608. case RIP_EVENT :
  609. case EXCEPTION_DEBUG_EVENT :
  610. break;
  611. case LOAD_DLL_DEBUG_EVENT :
  612. break;
  613. case UNLOAD_DLL_DEBUG_EVENT :
  614. break;
  615. default:
  616. return FALSE;
  617. }
  618. return TRUE;
  619. }
  620. BOOL
  621. APIENTRY
  622. ContinueDebugEvent(
  623. DWORD dwProcessId,
  624. DWORD dwThreadId,
  625. DWORD dwContinueStatus
  626. )
  627. /*++
  628. Routine Description:
  629. A debugger can continue a thread that previously reported a debug
  630. event using ContinueDebugEvent.
  631. Upon successful completion of this API, the specified thread is
  632. continued. Depending on the debug event previously reported by the
  633. thread certain side effects occur.
  634. If the continued thread previously reported an exit thread debug
  635. event, the handle that the debugger has to the thread is closed.
  636. If the continued thread previously reported an exit process debug
  637. event, the handles that the debugger has to the thread and to the
  638. process are closed.
  639. Arguments:
  640. dwProcessId - Supplies the process id of the process to continue. The
  641. combination of process id and thread id must identify a thread that
  642. has previously reported a debug event.
  643. dwThreadId - Supplies the thread id of the thread to continue. The
  644. combination of process id and thread id must identify a thread that
  645. has previously reported a debug event.
  646. dwContinueStatus - Supplies the continuation status for the thread
  647. reporting the debug event.
  648. dwContinueStatus Values:
  649. DBG_CONTINUE - If the thread being continued had
  650. previously reported an exception event, continuing with
  651. this value causes all exception processing to stop and
  652. the thread continues execution. For any other debug
  653. event, this continuation status simply allows the thread
  654. to continue execution.
  655. DBG_EXCEPTION_NOT_HANDLED - If the thread being continued
  656. had previously reported an exception event, continuing
  657. with this value causes exception processing to continue.
  658. If this is a first chance exception event, then
  659. structured exception handler search/dispatch logic is
  660. invoked. Otherwise, the process is terminated. For any
  661. other debug event, this continuation status simply
  662. allows the thread to continue execution.
  663. DBG_TERMINATE_THREAD - After all continue side effects are
  664. processed, this continuation status causes the thread to
  665. jump to a call to ExitThread. The exit code is the
  666. value DBG_TERMINATE_THREAD.
  667. DBG_TERMINATE_PROCESS - After all continue side effects are
  668. processed, this continuation status causes the thread to
  669. jump to a call to ExitProcess. The exit code is the
  670. value DBG_TERMINATE_PROCESS.
  671. Return Value:
  672. TRUE - The operation was successful.
  673. FALSE/NULL - The operation failed. Extended error status is available
  674. using GetLastError.
  675. --*/
  676. {
  677. NTSTATUS Status;
  678. CLIENT_ID ClientId;
  679. ClientId.UniqueProcess = (HANDLE)LongToHandle(dwProcessId);
  680. ClientId.UniqueThread = (HANDLE)LongToHandle(dwThreadId);
  681. Status = DbgUiContinue(&ClientId,(NTSTATUS)dwContinueStatus);
  682. if ( !NT_SUCCESS(Status) ) {
  683. BaseSetLastNTError(Status);
  684. return FALSE;
  685. }
  686. RemoveHandles (dwThreadId, dwProcessId);
  687. return TRUE;
  688. }
  689. HANDLE
  690. ProcessIdToHandle (
  691. IN DWORD dwProcessId
  692. )
  693. {
  694. OBJECT_ATTRIBUTES oa;
  695. HANDLE Process;
  696. CLIENT_ID ClientId;
  697. NTSTATUS Status;
  698. if (dwProcessId == -1) {
  699. ClientId.UniqueProcess = CsrGetProcessId ();
  700. } else {
  701. ClientId.UniqueProcess = LongToHandle(dwProcessId);
  702. }
  703. ClientId.UniqueThread = NULL;
  704. InitializeObjectAttributes (&oa, NULL, 0, NULL, NULL);
  705. Status = NtOpenProcess (&Process,
  706. PROCESS_SET_PORT|PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|
  707. PROCESS_VM_WRITE|PROCESS_VM_READ,
  708. &oa,
  709. &ClientId);
  710. if (!NT_SUCCESS(Status)) {
  711. BaseSetLastNTError(Status);
  712. Process = NULL;
  713. }
  714. return Process;
  715. }
  716. BOOL
  717. APIENTRY
  718. DebugActiveProcess(
  719. DWORD dwProcessId
  720. )
  721. /*++
  722. Routine Description:
  723. This API allows a debugger to attach to an active process and debug
  724. the process. The debugger specifies the process that it wants to
  725. debug through the process id of the target process. The debugger
  726. gets debug access to the process as if it had created the process
  727. with the DEBUG_ONLY_THIS_PROCESS creation flag.
  728. The debugger must have approriate access to the calling process such
  729. that it can open the process for PROCESS_ALL_ACCESS. For Dos/Win32
  730. this never fails (the process id just has to be a valid process id).
  731. For NT/Win32 this check can fail if the target process was created
  732. with a security descriptor that denies the debugger approriate
  733. access.
  734. Once the process id check has been made and the system determines
  735. that a valid debug attachment is being made, this call returns
  736. success to the debugger. The debugger is then expected to wait for
  737. debug events. The system will suspend all threads in the process
  738. and feed the debugger debug events representing the current state of
  739. the process.
  740. The system will feed the debugger a single create process debug
  741. event representing the process specified by dwProcessId. The
  742. lpStartAddress field of the create process debug event is NULL. For
  743. each thread currently part of the process, the system will send a
  744. create thread debug event. The lpStartAddress field of the create
  745. thread debug event is NULL. For each DLL currently loaded into the
  746. address space of the target process, the system will send a LoadDll
  747. debug event. The system will arrange for the first thread in the
  748. process to execute a breakpoint instruction after it is resumed.
  749. Continuing this thread causes the thread to return to whatever it
  750. was doing prior to the debug attach.
  751. After all of this has been done, the system resumes all threads within
  752. the process. When the first thread in the process resumes, it will
  753. execute a breakpoint instruction causing an exception debug event
  754. to be sent to the debugger.
  755. All future debug events are sent to the debugger using the normal
  756. mechanism and rules.
  757. Arguments:
  758. dwProcessId - Supplies the process id of a process the caller
  759. wants to debug.
  760. Return Value:
  761. TRUE - The operation was successful.
  762. FALSE/NULL - The operation failed. Extended error status is available
  763. using GetLastError.
  764. --*/
  765. {
  766. HANDLE Process;
  767. NTSTATUS Status, Status1;
  768. //
  769. // Connect to dbgss as a user interface
  770. //
  771. Status = DbgUiConnectToDbg ();
  772. if (!NT_SUCCESS (Status)) {
  773. BaseSetLastNTError (Status);
  774. return FALSE;
  775. }
  776. //
  777. // Convert the process ID to a handle
  778. //
  779. Process = ProcessIdToHandle (dwProcessId);
  780. if (Process == NULL) {
  781. return FALSE;
  782. }
  783. Status = DbgUiDebugActiveProcess (Process);
  784. if (!NT_SUCCESS (Status)) {
  785. Status1 = NtClose (Process);
  786. ASSERT (NT_SUCCESS (Status1));
  787. BaseSetLastNTError (Status);
  788. return FALSE;
  789. }
  790. Status1 = NtClose (Process);
  791. ASSERT (NT_SUCCESS (Status1));
  792. return TRUE;
  793. }
  794. BOOL
  795. APIENTRY
  796. DebugActiveProcessStop(
  797. DWORD dwProcessId
  798. )
  799. /*++
  800. Routine Description:
  801. Arguments:
  802. dwProcessId - Supplies the process id of a process the caller
  803. wants to stop debugging.
  804. Return Value:
  805. TRUE - The operation was successful.
  806. FALSE/NULL - The operation failed. Extended error status is available
  807. using GetLastError.
  808. --*/
  809. {
  810. HANDLE Process, Thread;
  811. NTSTATUS Status;
  812. NTSTATUS Status1;
  813. DWORD ThreadId;
  814. Process = ProcessIdToHandle (dwProcessId);
  815. if (Process == NULL) {
  816. return FALSE;
  817. }
  818. //
  819. // Tell dbgss we have finished with this process.
  820. //
  821. CloseAllProcessHandles (dwProcessId);
  822. Status = DbgUiStopDebugging (Process);
  823. Status1 = NtClose (Process);
  824. ASSERT (NT_SUCCESS (Status1));
  825. if (!NT_SUCCESS(Status)) {
  826. SetLastError(ERROR_ACCESS_DENIED);
  827. return FALSE;
  828. }
  829. return TRUE;
  830. }
  831. BOOL
  832. APIENTRY
  833. DebugBreakProcess (
  834. IN HANDLE Process
  835. )
  836. /*++
  837. Routine Description:
  838. This functions creates a thread inside the target process that issues a break.
  839. Arguments:
  840. Process - Handle to process
  841. Return Value:
  842. TRUE - The operation was successful
  843. FALSE/NULL - The operation failed. Extended error status is available
  844. using GetLastError.
  845. --*/
  846. {
  847. NTSTATUS Status;
  848. Status = DbgUiIssueRemoteBreakin (Process);
  849. if (NT_SUCCESS (Status)) {
  850. return TRUE;
  851. } else {
  852. BaseSetLastNTError (Status);
  853. return FALSE;
  854. }
  855. }
  856. BOOL
  857. APIENTRY
  858. DebugSetProcessKillOnExit (
  859. IN BOOL KillOnExit
  860. )
  861. /*++
  862. Routine Description:
  863. This functions sets the action to be performed when the debugging thread dies
  864. Arguments:
  865. KillOnExit - TRUE: Kill debugged processes on exit, FALSE: Detatch on debug exit
  866. Return Value:
  867. TRUE - The operation was successful
  868. FALSE/NULL - The operation failed. Extended error status is available
  869. using GetLastError.
  870. --*/
  871. {
  872. HANDLE DebugHandle;
  873. ULONG Flags;
  874. NTSTATUS Status;
  875. DebugHandle = DbgUiGetThreadDebugObject ();
  876. if (DebugHandle == NULL) {
  877. BaseSetLastNTError (STATUS_INVALID_HANDLE);
  878. return FALSE;
  879. }
  880. if (KillOnExit) {
  881. Flags = DEBUG_KILL_ON_CLOSE;
  882. } else {
  883. Flags = 0;
  884. }
  885. Status = NtSetInformationDebugObject (DebugHandle,
  886. DebugObjectFlags,
  887. &Flags,
  888. sizeof (Flags),
  889. NULL);
  890. if (!NT_SUCCESS (Status)) {
  891. BaseSetLastNTError (Status);
  892. return FALSE;
  893. }
  894. return TRUE;
  895. }
  896. BOOL
  897. APIENTRY
  898. GetThreadSelectorEntry(
  899. HANDLE hThread,
  900. DWORD dwSelector,
  901. LPLDT_ENTRY lpSelectorEntry
  902. )
  903. /*++
  904. Routine Description:
  905. This function is used to return a descriptor table entry for the
  906. specified thread corresponding to the specified selector.
  907. This API is only functional on x86 based systems. For non x86 based
  908. systems. A value of FALSE is returned.
  909. This API is used by a debugger so that it can convert segment
  910. relative addresses to linear virtual address (since this is the only
  911. format supported by ReadProcessMemory and WriteProcessMemory.
  912. Arguments:
  913. hThread - Supplies a handle to the thread that contains the
  914. specified selector. The handle must have been created with
  915. THREAD_QUERY_INFORMATION access.
  916. dwSelector - Supplies the selector value to lookup. The selector
  917. value may be a global selector or a local selector.
  918. lpSelectorEntry - If the specified selector is contained withing the
  919. threads descriptor tables, this parameter returns the selector
  920. entry corresponding to the specified selector value. This data
  921. can be used to compute the linear base address that segment
  922. relative addresses refer to.
  923. Return Value:
  924. TRUE - The operation was successful
  925. FALSE/NULL - The operation failed. Extended error status is available
  926. using GetLastError.
  927. --*/
  928. {
  929. #if defined(i386)
  930. DESCRIPTOR_TABLE_ENTRY DescriptorEntry;
  931. NTSTATUS Status;
  932. DescriptorEntry.Selector = dwSelector;
  933. Status = NtQueryInformationThread(
  934. hThread,
  935. ThreadDescriptorTableEntry,
  936. &DescriptorEntry,
  937. sizeof(DescriptorEntry),
  938. NULL
  939. );
  940. if ( !NT_SUCCESS(Status) ) {
  941. BaseSetLastNTError(Status);
  942. return FALSE;
  943. }
  944. *lpSelectorEntry = DescriptorEntry.Descriptor;
  945. return TRUE;
  946. #else
  947. BaseSetLastNTError(STATUS_NOT_SUPPORTED);
  948. return FALSE;
  949. #endif // i386
  950. }