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