xref: /reactos/dll/win32/kernel32/client/debugger.c (revision 9f938ea3)
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