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