1 /*
2 * PROJECT: ReactOS Win32 Base API
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/kernel32/client/debugger.c
5 * PURPOSE: Wrappers for the NT Debug Implementation
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include <k32.h>
12
13 #include <ndk/dbgkfuncs.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 typedef struct _DBGSS_THREAD_DATA
19 {
20 struct _DBGSS_THREAD_DATA *Next;
21 HANDLE ThreadHandle;
22 HANDLE ProcessHandle;
23 DWORD ProcessId;
24 DWORD ThreadId;
25 BOOLEAN HandleMarked;
26 } DBGSS_THREAD_DATA, *PDBGSS_THREAD_DATA;
27
28 #define DbgSsSetThreadData(d) \
29 NtCurrentTeb()->DbgSsReserved[0] = d
30
31 #define DbgSsGetThreadData() \
32 ((PDBGSS_THREAD_DATA)NtCurrentTeb()->DbgSsReserved[0])
33
34 /* PRIVATE FUNCTIONS *********************************************************/
35
36 static HANDLE
K32CreateDBMonMutex(VOID)37 K32CreateDBMonMutex(VOID)
38 {
39 static SID_IDENTIFIER_AUTHORITY siaNTAuth = {SECURITY_NT_AUTHORITY};
40 static SID_IDENTIFIER_AUTHORITY siaWorldAuth = {SECURITY_WORLD_SID_AUTHORITY};
41 HANDLE hMutex;
42 NTSTATUS Status;
43
44 /* SIDs to be used in the DACL */
45 PSID psidSystem = NULL;
46 PSID psidAdministrators = NULL;
47 PSID psidEveryone = NULL;
48
49 /* Buffer for the DACL */
50 PVOID pDaclBuf = NULL;
51
52 /* Minimum size of the DACL: an ACL descriptor and three ACCESS_ALLOWED_ACE
53 * headers. We will add the size of SIDs when they are known. */
54 SIZE_T nDaclBufSize =
55 sizeof(ACL) + 3 * FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart);
56
57 /* Security descriptor and attributes of the mutex */
58 SECURITY_DESCRIPTOR sdMutexSecurity;
59 SECURITY_ATTRIBUTES saMutexAttribs = {sizeof(saMutexAttribs),
60 &sdMutexSecurity,
61 TRUE};
62
63 /* Try to open the mutex */
64 hMutex = OpenMutexW(SYNCHRONIZE | READ_CONTROL | MUTANT_QUERY_STATE,
65 TRUE,
66 L"DBWinMutex");
67 if (hMutex != NULL)
68 {
69 /* Success */
70 return hMutex;
71 }
72 /* Error other than the mutex not being found */
73 else if (GetLastError() != ERROR_FILE_NOT_FOUND)
74 {
75 /* Failure */
76 return NULL;
77 }
78
79 /* If the mutex does not exist, set up its security, then create it */
80
81 /* Allocate the NT AUTHORITY\SYSTEM SID */
82 Status = RtlAllocateAndInitializeSid(&siaNTAuth,
83 1,
84 SECURITY_LOCAL_SYSTEM_RID,
85 0, 0, 0, 0, 0, 0, 0,
86 &psidSystem);
87 if (!NT_SUCCESS(Status))
88 goto Cleanup;
89
90 /* Allocate the BUILTIN\Administrators SID */
91 Status = RtlAllocateAndInitializeSid(&siaNTAuth,
92 2,
93 SECURITY_BUILTIN_DOMAIN_RID,
94 DOMAIN_ALIAS_RID_ADMINS,
95 0, 0, 0, 0, 0, 0,
96 &psidAdministrators);
97 if (!NT_SUCCESS(Status))
98 goto Cleanup;
99
100 /* Allocate the Everyone SID */
101 Status = RtlAllocateAndInitializeSid(&siaWorldAuth,
102 1,
103 SECURITY_WORLD_RID,
104 0, 0, 0, 0, 0, 0, 0,
105 &psidEveryone);
106 if (!NT_SUCCESS(Status))
107 goto Cleanup;
108
109 /* Allocate space for the SIDs too */
110 nDaclBufSize += RtlLengthSid(psidSystem);
111 nDaclBufSize += RtlLengthSid(psidAdministrators);
112 nDaclBufSize += RtlLengthSid(psidEveryone);
113
114 /* Allocate the buffer for the DACL */
115 pDaclBuf = GlobalAlloc(GMEM_FIXED, nDaclBufSize);
116 if (pDaclBuf == NULL)
117 goto Cleanup;
118
119 /* Create the DACL */
120 Status = RtlCreateAcl(pDaclBuf, nDaclBufSize, ACL_REVISION);
121 if (!NT_SUCCESS(Status))
122 goto Cleanup;
123
124 /* Grant the minimum required access to Everyone */
125 Status = RtlAddAccessAllowedAce(pDaclBuf,
126 ACL_REVISION,
127 SYNCHRONIZE |
128 READ_CONTROL |
129 MUTANT_QUERY_STATE,
130 psidEveryone);
131 if (!NT_SUCCESS(Status))
132 goto Cleanup;
133
134 /* Grant full access to BUILTIN\Administrators */
135 Status = RtlAddAccessAllowedAce(pDaclBuf,
136 ACL_REVISION,
137 MUTANT_ALL_ACCESS,
138 psidAdministrators);
139 if (!NT_SUCCESS(Status))
140 goto Cleanup;
141
142 /* Grant full access to NT AUTHORITY\SYSTEM */
143 Status = RtlAddAccessAllowedAce(pDaclBuf,
144 ACL_REVISION,
145 MUTANT_ALL_ACCESS,
146 psidSystem);
147 if (!NT_SUCCESS(Status))
148 goto Cleanup;
149
150 /* Create the security descriptor */
151 Status = RtlCreateSecurityDescriptor(&sdMutexSecurity,
152 SECURITY_DESCRIPTOR_REVISION);
153 if (!NT_SUCCESS(Status))
154 goto Cleanup;
155
156 /* Set the descriptor's DACL to the created ACL */
157 Status = RtlSetDaclSecurityDescriptor(&sdMutexSecurity,
158 TRUE,
159 pDaclBuf,
160 FALSE);
161 if (!NT_SUCCESS(Status))
162 goto Cleanup;
163
164 /* Create the mutex */
165 hMutex = CreateMutexW(&saMutexAttribs, FALSE, L"DBWinMutex");
166
167 Cleanup:
168 /* Free the buffers */
169 if (pDaclBuf) GlobalFree(pDaclBuf);
170 if (psidEveryone) RtlFreeSid(psidEveryone);
171 if (psidAdministrators) RtlFreeSid(psidAdministrators);
172 if (psidSystem) RtlFreeSid(psidSystem);
173
174 return hMutex;
175 }
176
177 VOID
178 WINAPI
SaveThreadHandle(IN DWORD dwProcessId,IN DWORD dwThreadId,IN HANDLE hThread)179 SaveThreadHandle(IN DWORD dwProcessId,
180 IN DWORD dwThreadId,
181 IN HANDLE hThread)
182 {
183 PDBGSS_THREAD_DATA ThreadData;
184
185 /* Allocate a thread structure */
186 ThreadData = RtlAllocateHeap(RtlGetProcessHeap(),
187 0,
188 sizeof(DBGSS_THREAD_DATA));
189 if (!ThreadData) return;
190
191 /* Fill it out */
192 ThreadData->ThreadHandle = hThread;
193 ThreadData->ProcessId = dwProcessId;
194 ThreadData->ThreadId = dwThreadId;
195 ThreadData->ProcessHandle = NULL;
196 ThreadData->HandleMarked = FALSE;
197
198 /* Link it */
199 ThreadData->Next = DbgSsGetThreadData();
200 DbgSsSetThreadData(ThreadData);
201 }
202
203 VOID
204 WINAPI
SaveProcessHandle(IN DWORD dwProcessId,IN HANDLE hProcess)205 SaveProcessHandle(IN DWORD dwProcessId,
206 IN HANDLE hProcess)
207 {
208 PDBGSS_THREAD_DATA ThreadData;
209
210 /* Allocate a thread structure */
211 ThreadData = RtlAllocateHeap(RtlGetProcessHeap(),
212 0,
213 sizeof(DBGSS_THREAD_DATA));
214 if (!ThreadData) return;
215
216 /* Fill it out */
217 ThreadData->ProcessHandle = hProcess;
218 ThreadData->ProcessId = dwProcessId;
219 ThreadData->ThreadId = 0;
220 ThreadData->ThreadHandle = NULL;
221 ThreadData->HandleMarked = FALSE;
222
223 /* Link it */
224 ThreadData->Next = DbgSsGetThreadData();
225 DbgSsSetThreadData(ThreadData);
226 }
227
228 VOID
229 WINAPI
MarkThreadHandle(IN DWORD dwThreadId)230 MarkThreadHandle(IN DWORD dwThreadId)
231 {
232 PDBGSS_THREAD_DATA ThreadData;
233
234 /* Loop all thread data events */
235 for (ThreadData = DbgSsGetThreadData(); ThreadData; ThreadData = ThreadData->Next)
236 {
237 /* Check if this one matches */
238 if (ThreadData->ThreadId == dwThreadId)
239 {
240 /* Mark the structure and break out */
241 ThreadData->HandleMarked = TRUE;
242 break;
243 }
244 }
245 }
246
247 VOID
248 WINAPI
MarkProcessHandle(IN DWORD dwProcessId)249 MarkProcessHandle(IN DWORD dwProcessId)
250 {
251 PDBGSS_THREAD_DATA ThreadData;
252
253 /* Loop all thread data events */
254 for (ThreadData = DbgSsGetThreadData(); ThreadData; ThreadData = ThreadData->Next)
255 {
256 /* Check if this one matches */
257 if ((ThreadData->ProcessId == dwProcessId) && !(ThreadData->ThreadId))
258 {
259 /* Mark the structure and break out */
260 ThreadData->HandleMarked = TRUE;
261 break;
262 }
263 }
264 }
265
266 VOID
267 WINAPI
RemoveHandles(IN DWORD dwProcessId,IN DWORD dwThreadId)268 RemoveHandles(IN DWORD dwProcessId,
269 IN DWORD dwThreadId)
270 {
271 PDBGSS_THREAD_DATA *ThreadData;
272 PDBGSS_THREAD_DATA ThisData;
273
274 /* Loop all thread data events */
275 ThreadData = (PDBGSS_THREAD_DATA*)NtCurrentTeb()->DbgSsReserved;
276 ThisData = *ThreadData;
277 while(ThisData)
278 {
279 /* Check if this one matches */
280 if ((ThisData->HandleMarked) &&
281 ((ThisData->ProcessId == dwProcessId) || (ThisData->ThreadId == dwThreadId)))
282 {
283 /* Close open handles */
284 if (ThisData->ThreadHandle) CloseHandle(ThisData->ThreadHandle);
285 if (ThisData->ProcessHandle) CloseHandle(ThisData->ProcessHandle);
286
287 /* Unlink the thread data */
288 *ThreadData = ThisData->Next;
289
290 /* Free it*/
291 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData);
292 }
293 else
294 {
295 /* Move to the next one */
296 ThreadData = &ThisData->Next;
297 }
298 ThisData = *ThreadData;
299 }
300 }
301
302 VOID
303 WINAPI
CloseAllProcessHandles(IN DWORD dwProcessId)304 CloseAllProcessHandles(IN DWORD dwProcessId)
305 {
306 PDBGSS_THREAD_DATA *ThreadData;
307 PDBGSS_THREAD_DATA ThisData;
308
309 /* Loop all thread data events */
310 ThreadData = (PDBGSS_THREAD_DATA*)NtCurrentTeb()->DbgSsReserved;
311 ThisData = *ThreadData;
312 while(ThisData)
313 {
314 /* Check if this one matches */
315 if (ThisData->ProcessId == dwProcessId)
316 {
317 /* Close open handles */
318 if (ThisData->ThreadHandle) CloseHandle(ThisData->ThreadHandle);
319 if (ThisData->ProcessHandle) CloseHandle(ThisData->ProcessHandle);
320
321 /* Unlink the thread data */
322 *ThreadData = ThisData->Next;
323
324 /* Free it*/
325 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData);
326 }
327 else
328 {
329 /* Move to the next one */
330 ThreadData = &ThisData->Next;
331 }
332 ThisData = *ThreadData;
333 }
334 }
335
336 HANDLE
337 WINAPI
ProcessIdToHandle(IN DWORD dwProcessId)338 ProcessIdToHandle(IN DWORD dwProcessId)
339 {
340 NTSTATUS Status;
341 OBJECT_ATTRIBUTES ObjectAttributes;
342 HANDLE Handle;
343 CLIENT_ID ClientId;
344
345 /* If we don't have a PID, look it up */
346 if (dwProcessId == MAXDWORD) dwProcessId = (DWORD_PTR)CsrGetProcessId();
347
348 /* Open a handle to the process */
349 ClientId.UniqueThread = NULL;
350 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
351 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
352 Status = NtOpenProcess(&Handle,
353 PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
354 PROCESS_VM_WRITE | PROCESS_VM_READ |
355 PROCESS_SUSPEND_RESUME | PROCESS_QUERY_INFORMATION,
356 &ObjectAttributes,
357 &ClientId);
358 if (!NT_SUCCESS(Status))
359 {
360 /* Fail */
361 BaseSetLastNTError(Status);
362 return 0;
363 }
364
365 /* Return the handle */
366 return Handle;
367 }
368
369 /* PUBLIC FUNCTIONS **********************************************************/
370
371 /*
372 * @implemented
373 */
374 BOOL
375 WINAPI
CheckRemoteDebuggerPresent(IN HANDLE hProcess,OUT PBOOL pbDebuggerPresent)376 CheckRemoteDebuggerPresent(IN HANDLE hProcess,
377 OUT PBOOL pbDebuggerPresent)
378 {
379 HANDLE DebugPort;
380 NTSTATUS Status;
381
382 /* Make sure we have an output and process*/
383 if (!(pbDebuggerPresent) || !(hProcess))
384 {
385 /* Fail */
386 SetLastError(ERROR_INVALID_PARAMETER);
387 return FALSE;
388 }
389
390 /* Check if the process has a debug object/port */
391 Status = NtQueryInformationProcess(hProcess,
392 ProcessDebugPort,
393 &DebugPort,
394 sizeof(DebugPort),
395 NULL);
396 if (NT_SUCCESS(Status))
397 {
398 /* Return the current state */
399 *pbDebuggerPresent = DebugPort != NULL;
400 return TRUE;
401 }
402
403 /* Otherwise, fail */
404 BaseSetLastNTError(Status);
405 return FALSE;
406 }
407
408 /*
409 * @implemented
410 */
411 BOOL
412 WINAPI
ContinueDebugEvent(IN DWORD dwProcessId,IN DWORD dwThreadId,IN DWORD dwContinueStatus)413 ContinueDebugEvent(IN DWORD dwProcessId,
414 IN DWORD dwThreadId,
415 IN DWORD dwContinueStatus)
416 {
417 CLIENT_ID ClientId;
418 NTSTATUS Status;
419
420 /* Set the Client ID */
421 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
422 ClientId.UniqueThread = UlongToHandle(dwThreadId);
423
424 /* Continue debugging */
425 Status = DbgUiContinue(&ClientId, dwContinueStatus);
426 if (!NT_SUCCESS(Status))
427 {
428 /* Fail */
429 BaseSetLastNTError(Status);
430 return FALSE;
431 }
432
433 /* Remove the process/thread handles */
434 RemoveHandles(dwProcessId, dwThreadId);
435
436 /* Success */
437 return TRUE;
438 }
439
440 /*
441 * @implemented
442 */
443 BOOL
444 WINAPI
DebugActiveProcess(IN DWORD dwProcessId)445 DebugActiveProcess(IN DWORD dwProcessId)
446 {
447 NTSTATUS Status, Status1;
448 HANDLE Handle;
449
450 /* Connect to the debugger */
451 Status = DbgUiConnectToDbg();
452 if (!NT_SUCCESS(Status))
453 {
454 BaseSetLastNTError(Status);
455 return FALSE;
456 }
457
458 /* Get the process handle */
459 Handle = ProcessIdToHandle(dwProcessId);
460 if (!Handle) return FALSE;
461
462 /* Now debug the process */
463 Status = DbgUiDebugActiveProcess(Handle);
464
465 /* Close the handle since we're done */
466 Status1 = NtClose(Handle);
467 ASSERT(NT_SUCCESS(Status1));
468
469 /* Check if debugging worked */
470 if (!NT_SUCCESS(Status))
471 {
472 /* Fail */
473 BaseSetLastNTError(Status);
474 return FALSE;
475 }
476
477 /* Success */
478 return TRUE;
479 }
480
481 /*
482 * @implemented
483 */
484 BOOL
485 WINAPI
DebugActiveProcessStop(IN DWORD dwProcessId)486 DebugActiveProcessStop(IN DWORD dwProcessId)
487 {
488 NTSTATUS Status, Status1;
489 HANDLE Handle;
490
491 /* Get the process handle */
492 Handle = ProcessIdToHandle(dwProcessId);
493 if (!Handle) return FALSE;
494
495 /* Close all the process handles */
496 CloseAllProcessHandles(dwProcessId);
497
498 /* Now stop debugging the process */
499 Status = DbgUiStopDebugging(Handle);
500 Status1 = NtClose(Handle);
501 ASSERT(NT_SUCCESS(Status1));
502
503 /* Check for failure */
504 if (!NT_SUCCESS(Status))
505 {
506 /* Fail */
507 SetLastError(ERROR_ACCESS_DENIED);
508 return FALSE;
509 }
510
511 /* Success */
512 return TRUE;
513 }
514
515 /*
516 * @implemented
517 */
518 BOOL
519 WINAPI
DebugBreakProcess(IN HANDLE Process)520 DebugBreakProcess(IN HANDLE Process)
521 {
522 NTSTATUS Status;
523
524 /* Send the breakin request */
525 Status = DbgUiIssueRemoteBreakin(Process);
526 if (!NT_SUCCESS(Status))
527 {
528 /* Failure */
529 BaseSetLastNTError(Status);
530 return FALSE;
531 }
532
533 /* Success */
534 return TRUE;
535 }
536
537 /*
538 * @implemented
539 */
540 BOOL
541 WINAPI
DebugSetProcessKillOnExit(IN BOOL KillOnExit)542 DebugSetProcessKillOnExit(IN BOOL KillOnExit)
543 {
544 HANDLE Handle;
545 NTSTATUS Status;
546 ULONG State;
547
548 /* Get the debug object */
549 Handle = DbgUiGetThreadDebugObject();
550 if (!Handle)
551 {
552 /* Fail */
553 BaseSetLastNTError(STATUS_INVALID_HANDLE);
554 return FALSE;
555 }
556
557 /* Now set the kill-on-exit state */
558 State = KillOnExit != 0;
559 Status = NtSetInformationDebugObject(Handle,
560 DebugObjectKillProcessOnExitInformation,
561 &State,
562 sizeof(State),
563 NULL);
564 if (!NT_SUCCESS(Status))
565 {
566 /* Fail */
567 BaseSetLastNTError(Status);
568 return FALSE;
569 }
570
571 /* Success */
572 return TRUE;
573 }
574
575 /*
576 * @implemented
577 */
578 BOOL
579 WINAPI
IsDebuggerPresent(VOID)580 IsDebuggerPresent(VOID)
581 {
582 return (BOOL)NtCurrentPeb()->BeingDebugged;
583 }
584
585 /*
586 * @implemented
587 */
588 BOOL
589 WINAPI
WaitForDebugEvent(IN LPDEBUG_EVENT lpDebugEvent,IN DWORD dwMilliseconds)590 WaitForDebugEvent(IN LPDEBUG_EVENT lpDebugEvent,
591 IN DWORD dwMilliseconds)
592 {
593 LARGE_INTEGER WaitTime;
594 PLARGE_INTEGER Timeout;
595 DBGUI_WAIT_STATE_CHANGE WaitStateChange;
596 NTSTATUS Status;
597
598 /* Convert to NT Timeout */
599 Timeout = BaseFormatTimeOut(&WaitTime, dwMilliseconds);
600
601 /* Loop while we keep getting interrupted */
602 do
603 {
604 /* Call the native API */
605 Status = DbgUiWaitStateChange(&WaitStateChange, Timeout);
606 } while ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC));
607
608 /* Check if the wait failed */
609 if (!(NT_SUCCESS(Status)) || (Status == DBG_UNABLE_TO_PROVIDE_HANDLE))
610 {
611 /* Set the error code and quit */
612 BaseSetLastNTError(Status);
613 return FALSE;
614 }
615
616 /* Check if we timed out */
617 if (Status == STATUS_TIMEOUT)
618 {
619 /* Fail with a timeout error */
620 SetLastError(ERROR_SEM_TIMEOUT);
621 return FALSE;
622 }
623
624 /* Convert the structure */
625 Status = DbgUiConvertStateChangeStructure(&WaitStateChange, lpDebugEvent);
626 if (!NT_SUCCESS(Status))
627 {
628 /* Set the error code and quit */
629 BaseSetLastNTError(Status);
630 return FALSE;
631 }
632
633 /* Check what kind of event this was */
634 switch (lpDebugEvent->dwDebugEventCode)
635 {
636 /* New thread was created */
637 case CREATE_THREAD_DEBUG_EVENT:
638
639 /* Setup the thread data */
640 SaveThreadHandle(lpDebugEvent->dwProcessId,
641 lpDebugEvent->dwThreadId,
642 lpDebugEvent->u.CreateThread.hThread);
643 break;
644
645 /* New process was created */
646 case CREATE_PROCESS_DEBUG_EVENT:
647
648 /* Setup the process data */
649 SaveProcessHandle(lpDebugEvent->dwProcessId,
650 lpDebugEvent->u.CreateProcessInfo.hProcess);
651
652 /* Setup the thread data */
653 SaveThreadHandle(lpDebugEvent->dwProcessId,
654 lpDebugEvent->dwThreadId,
655 lpDebugEvent->u.CreateProcessInfo.hThread);
656 break;
657
658 /* Process was exited */
659 case EXIT_PROCESS_DEBUG_EVENT:
660
661 /* Mark the thread data as such and fall through */
662 MarkProcessHandle(lpDebugEvent->dwProcessId);
663
664 /* Thread was exited */
665 case EXIT_THREAD_DEBUG_EVENT:
666
667 /* Mark the thread data */
668 MarkThreadHandle(lpDebugEvent->dwThreadId);
669 break;
670
671 /* Nothing to do */
672 case EXCEPTION_DEBUG_EVENT:
673 case LOAD_DLL_DEBUG_EVENT:
674 case UNLOAD_DLL_DEBUG_EVENT:
675 case OUTPUT_DEBUG_STRING_EVENT:
676 case RIP_EVENT:
677 break;
678
679 /* Fail anything else */
680 default:
681 return FALSE;
682 }
683
684 /* Return success */
685 return TRUE;
686 }
687
688 /*
689 * @implemented
690 */
691 VOID
692 WINAPI
OutputDebugStringA(IN LPCSTR _OutputString)693 OutputDebugStringA(IN LPCSTR _OutputString)
694 {
695 _SEH2_TRY
696 {
697 ULONG_PTR a_nArgs[2];
698
699 a_nArgs[0] = (ULONG_PTR)(strlen(_OutputString) + 1);
700 a_nArgs[1] = (ULONG_PTR)_OutputString;
701
702 /* send the string to the user-mode debugger */
703 RaiseException(DBG_PRINTEXCEPTION_C, 0, 2, a_nArgs);
704 }
705 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
706 {
707 /* no user-mode debugger: try the systemwide debug message monitor, or the
708 kernel debugger as a last resort */
709
710 /* mutex used to synchronize invocations of OutputDebugString */
711 static HANDLE s_hDBMonMutex = NULL;
712 /* true if we already attempted to open/create the mutex */
713 static BOOL s_bDBMonMutexTriedOpen = FALSE;
714
715 /* local copy of the mutex handle */
716 volatile HANDLE hDBMonMutex = s_hDBMonMutex;
717 /* handle to the Section of the shared buffer */
718 volatile HANDLE hDBMonBuffer = NULL;
719
720 /* pointer to the mapped view of the shared buffer. It consist of the current
721 process id followed by the message string */
722 struct { DWORD ProcessId; CHAR Buffer[1]; } * pDBMonBuffer = NULL;
723
724 /* event: signaled by the debug message monitor when OutputDebugString can write
725 to the shared buffer */
726 volatile HANDLE hDBMonBufferReady = NULL;
727
728 /* event: to be signaled by OutputDebugString when it's done writing to the
729 shared buffer */
730 volatile HANDLE hDBMonDataReady = NULL;
731
732 /* mutex not opened, and no previous attempts to open/create it */
733 if (hDBMonMutex == NULL && !s_bDBMonMutexTriedOpen)
734 {
735 /* open/create the mutex */
736 hDBMonMutex = K32CreateDBMonMutex();
737 /* store the handle */
738 s_hDBMonMutex = hDBMonMutex;
739 }
740
741 _SEH2_TRY
742 {
743 volatile PCHAR a_cBuffer = NULL;
744
745 /* opening the mutex failed */
746 if (hDBMonMutex == NULL)
747 {
748 /* remember next time */
749 s_bDBMonMutexTriedOpen = TRUE;
750 }
751 /* opening the mutex succeeded */
752 else
753 {
754 do
755 {
756 /* synchronize with other invocations of OutputDebugString */
757 WaitForSingleObject(hDBMonMutex, INFINITE);
758
759 /* buffer of the system-wide debug message monitor */
760 hDBMonBuffer = OpenFileMappingW(SECTION_MAP_WRITE, FALSE, L"DBWIN_BUFFER");
761
762 /* couldn't open the buffer: send the string to the kernel debugger */
763 if (hDBMonBuffer == NULL) break;
764
765 /* map the buffer */
766 pDBMonBuffer = MapViewOfFile(hDBMonBuffer,
767 SECTION_MAP_READ | SECTION_MAP_WRITE,
768 0,
769 0,
770 0);
771
772 /* couldn't map the buffer: send the string to the kernel debugger */
773 if (pDBMonBuffer == NULL) break;
774
775 /* open the event signaling that the buffer can be accessed */
776 hDBMonBufferReady = OpenEventW(SYNCHRONIZE, FALSE, L"DBWIN_BUFFER_READY");
777
778 /* couldn't open the event: send the string to the kernel debugger */
779 if (hDBMonBufferReady == NULL) break;
780
781 /* open the event to be signaled when the buffer has been filled */
782 hDBMonDataReady = OpenEventW(EVENT_MODIFY_STATE, FALSE, L"DBWIN_DATA_READY");
783 }
784 while(0);
785
786 /* we couldn't connect to the system-wide debug message monitor: send the
787 string to the kernel debugger */
788 if (hDBMonDataReady == NULL) ReleaseMutex(hDBMonMutex);
789 }
790
791 _SEH2_TRY
792 {
793 /* size of the current output block */
794 volatile SIZE_T nRoundLen;
795
796 /* size of the remainder of the string */
797 volatile SIZE_T nOutputStringLen;
798
799 /* output the whole string */
800 nOutputStringLen = strlen(_OutputString);
801
802 do
803 {
804 /* we're connected to the debug monitor:
805 write the current block to the shared buffer */
806 if (hDBMonDataReady)
807 {
808 /* wait a maximum of 10 seconds for the debug monitor
809 to finish processing the shared buffer */
810 if (WaitForSingleObject(hDBMonBufferReady, 10000) != WAIT_OBJECT_0)
811 {
812 /* timeout or failure: give up */
813 break;
814 }
815
816 /* write the process id into the buffer */
817 pDBMonBuffer->ProcessId = GetCurrentProcessId();
818
819 /* write only as many bytes as they fit in the buffer */
820 if (nOutputStringLen > (PAGE_SIZE - sizeof(DWORD) - 1))
821 nRoundLen = PAGE_SIZE - sizeof(DWORD) - 1;
822 else
823 nRoundLen = nOutputStringLen;
824
825 /* copy the current block into the buffer */
826 memcpy(pDBMonBuffer->Buffer, _OutputString, nRoundLen);
827
828 /* null-terminate the current block */
829 pDBMonBuffer->Buffer[nRoundLen] = 0;
830
831 /* signal that the data contains meaningful data and can be read */
832 SetEvent(hDBMonDataReady);
833 }
834 /* else, send the current block to the kernel debugger */
835 else
836 {
837 /* output in blocks of 512 characters */
838 a_cBuffer = (CHAR*)HeapAlloc(GetProcessHeap(), 0, 512);
839
840 if (!a_cBuffer)
841 {
842 DbgPrint("OutputDebugStringA: Failed\n");
843 break;
844 }
845
846 /* write a maximum of 511 bytes */
847 if (nOutputStringLen > 510)
848 nRoundLen = 510;
849 else
850 nRoundLen = nOutputStringLen;
851
852 /* copy the current block */
853 memcpy(a_cBuffer, _OutputString, nRoundLen);
854
855 /* null-terminate the current block */
856 a_cBuffer[nRoundLen] = 0;
857
858 /* send the current block to the kernel debugger */
859 DbgPrint("%s", a_cBuffer);
860
861 if (a_cBuffer)
862 {
863 HeapFree(GetProcessHeap(), 0, a_cBuffer);
864 a_cBuffer = NULL;
865 }
866 }
867
868 /* move to the next block */
869 _OutputString += nRoundLen;
870 nOutputStringLen -= nRoundLen;
871 }
872 /* repeat until the string has been fully output */
873 while (nOutputStringLen > 0);
874 }
875 /* ignore access violations and let other exceptions fall through */
876 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_ACCESS_VIOLATION) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
877 {
878 if (a_cBuffer)
879 HeapFree(GetProcessHeap(), 0, a_cBuffer);
880
881 /* string copied verbatim from Microsoft's kernel32.dll */
882 DbgPrint("\nOutputDebugString faulted during output\n");
883 }
884 _SEH2_END;
885 }
886 _SEH2_FINALLY
887 {
888 /* close all the still open resources */
889 if (hDBMonBufferReady) CloseHandle(hDBMonBufferReady);
890 if (pDBMonBuffer) UnmapViewOfFile(pDBMonBuffer);
891 if (hDBMonBuffer) CloseHandle(hDBMonBuffer);
892 if (hDBMonDataReady) CloseHandle(hDBMonDataReady);
893
894 /* leave the critical section */
895 if (hDBMonDataReady != NULL)
896 ReleaseMutex(hDBMonMutex);
897 }
898 _SEH2_END;
899 }
900 _SEH2_END;
901 }
902
903 /*
904 * @implemented
905 */
906 VOID
907 WINAPI
OutputDebugStringW(IN LPCWSTR OutputString)908 OutputDebugStringW(IN LPCWSTR OutputString)
909 {
910 UNICODE_STRING UnicodeString;
911 ANSI_STRING AnsiString;
912 NTSTATUS Status;
913
914 /* convert the string in ANSI */
915 RtlInitUnicodeString(&UnicodeString, OutputString);
916 Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE);
917
918 /* OutputDebugStringW always prints something, even if conversion fails */
919 if (!NT_SUCCESS(Status)) AnsiString.Buffer = "";
920
921 /* Output the converted string */
922 OutputDebugStringA(AnsiString.Buffer);
923
924 /* free the converted string */
925 if (NT_SUCCESS(Status)) RtlFreeAnsiString(&AnsiString);
926 }
927
928 /* EOF */
929