OLD | NEW |
1 // Copyright 2015 The Crashpad Authors. All rights reserved. | 1 // Copyright 2015 The Crashpad Authors. All rights reserved. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
(...skipping 10 matching lines...) Expand all Loading... |
21 #include <memory> | 21 #include <memory> |
22 | 22 |
23 #include "base/atomicops.h" | 23 #include "base/atomicops.h" |
24 #include "base/logging.h" | 24 #include "base/logging.h" |
25 #include "base/scoped_generic.h" | 25 #include "base/scoped_generic.h" |
26 #include "base/strings/string16.h" | 26 #include "base/strings/string16.h" |
27 #include "base/strings/stringprintf.h" | 27 #include "base/strings/stringprintf.h" |
28 #include "base/strings/utf_string_conversions.h" | 28 #include "base/strings/utf_string_conversions.h" |
29 #include "base/synchronization/lock.h" | 29 #include "base/synchronization/lock.h" |
30 #include "util/file/file_io.h" | 30 #include "util/file/file_io.h" |
| 31 #include "util/win/address_types.h" |
31 #include "util/win/command_line.h" | 32 #include "util/win/command_line.h" |
32 #include "util/win/critical_section_with_debug_info.h" | 33 #include "util/win/critical_section_with_debug_info.h" |
33 #include "util/win/get_function.h" | 34 #include "util/win/get_function.h" |
34 #include "util/win/handle.h" | 35 #include "util/win/handle.h" |
| 36 #include "util/win/nt_internals.h" |
| 37 #include "util/win/ntstatus_logging.h" |
| 38 #include "util/win/process_info.h" |
35 #include "util/win/registration_protocol_win.h" | 39 #include "util/win/registration_protocol_win.h" |
36 #include "util/win/scoped_handle.h" | 40 #include "util/win/scoped_handle.h" |
| 41 #include "util/win/scoped_process_suspend.h" |
37 | 42 |
38 namespace { | 43 namespace { |
39 | 44 |
40 // This handle is never closed. This is used to signal to the server that a dump | 45 // This handle is never closed. This is used to signal to the server that a dump |
41 // should be taken in the event of a crash. | 46 // should be taken in the event of a crash. |
42 HANDLE g_signal_exception = INVALID_HANDLE_VALUE; | 47 HANDLE g_signal_exception = INVALID_HANDLE_VALUE; |
43 | 48 |
44 // Where we store the exception information that the crash handler reads. | 49 // Where we store the exception information that the crash handler reads. |
45 crashpad::ExceptionInformation g_crash_exception_information; | 50 crashpad::ExceptionInformation g_crash_exception_information; |
46 | 51 |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
161 // There doesn't seem to be any documentation of this, but if there's a handle | 166 // There doesn't seem to be any documentation of this, but if there's a handle |
162 // duplicated in this list, CreateProcess() fails with | 167 // duplicated in this list, CreateProcess() fails with |
163 // ERROR_INVALID_PARAMETER. | 168 // ERROR_INVALID_PARAMETER. |
164 if (IsInheritableHandle(handle) && | 169 if (IsInheritableHandle(handle) && |
165 std::find(handle_list->begin(), handle_list->end(), handle) == | 170 std::find(handle_list->begin(), handle_list->end(), handle) == |
166 handle_list->end()) { | 171 handle_list->end()) { |
167 handle_list->push_back(handle); | 172 handle_list->push_back(handle); |
168 } | 173 } |
169 } | 174 } |
170 | 175 |
| 176 void AddUint32(std::vector<unsigned char>* data_vector, uint32_t data) { |
| 177 data_vector->push_back(static_cast<unsigned char>(data & 0xff)); |
| 178 data_vector->push_back(static_cast<unsigned char>((data & 0xff00) >> 8)); |
| 179 data_vector->push_back(static_cast<unsigned char>((data & 0xff0000) >> 16)); |
| 180 data_vector->push_back(static_cast<unsigned char>((data & 0xff000000) >> 24)); |
| 181 } |
| 182 |
| 183 void AddUint64(std::vector<unsigned char>* data_vector, uint64_t data) { |
| 184 AddUint32(data_vector, static_cast<uint32_t>(data & 0xffffffffULL)); |
| 185 AddUint32(data_vector, |
| 186 static_cast<uint32_t>((data & 0xffffffff00000000ULL) >> 32)); |
| 187 } |
| 188 |
171 } // namespace | 189 } // namespace |
172 | 190 |
173 namespace crashpad { | 191 namespace crashpad { |
174 | 192 |
175 CrashpadClient::CrashpadClient() | 193 CrashpadClient::CrashpadClient() |
176 : ipc_pipe_() { | 194 : ipc_pipe_() { |
177 } | 195 } |
178 | 196 |
179 CrashpadClient::~CrashpadClient() { | 197 CrashpadClient::~CrashpadClient() { |
180 } | 198 } |
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
455 | 473 |
456 bool set_event_result = !!SetEvent(g_signal_non_crash_dump); | 474 bool set_event_result = !!SetEvent(g_signal_non_crash_dump); |
457 PLOG_IF(ERROR, !set_event_result) << "SetEvent"; | 475 PLOG_IF(ERROR, !set_event_result) << "SetEvent"; |
458 | 476 |
459 DWORD wfso_result = WaitForSingleObject(g_non_crash_dump_done, INFINITE); | 477 DWORD wfso_result = WaitForSingleObject(g_non_crash_dump_done, INFINITE); |
460 PLOG_IF(ERROR, wfso_result != WAIT_OBJECT_0) << "WaitForSingleObject"; | 478 PLOG_IF(ERROR, wfso_result != WAIT_OBJECT_0) << "WaitForSingleObject"; |
461 } | 479 } |
462 | 480 |
463 // static | 481 // static |
464 void CrashpadClient::DumpAndCrash(EXCEPTION_POINTERS* exception_pointers) { | 482 void CrashpadClient::DumpAndCrash(EXCEPTION_POINTERS* exception_pointers) { |
| 483 if (g_signal_exception == INVALID_HANDLE_VALUE) { |
| 484 LOG(ERROR) << "haven't called UseHandler()"; |
| 485 return; |
| 486 } |
| 487 |
465 UnhandledExceptionHandler(exception_pointers); | 488 UnhandledExceptionHandler(exception_pointers); |
466 } | 489 } |
467 | 490 |
| 491 bool CrashpadClient::DumpAndCrashTargetProcess(HANDLE process, |
| 492 HANDLE blame_thread, |
| 493 DWORD exception_code) const { |
| 494 // Confirm we're on Vista or later. |
| 495 const DWORD version = GetVersion(); |
| 496 const DWORD major_version = LOBYTE(LOWORD(version)); |
| 497 if (major_version < 6) { |
| 498 LOG(ERROR) << "unavailable before Vista"; |
| 499 return false; |
| 500 } |
| 501 |
| 502 // Confirm that our bitness is the same as the process we're crashing. |
| 503 ProcessInfo process_info; |
| 504 if (!process_info.Initialize(process)) { |
| 505 LOG(ERROR) << "ProcessInfo::Initialize"; |
| 506 return false; |
| 507 } |
| 508 #if defined(ARCH_CPU_64_BITS) |
| 509 if (!process_info.Is64Bit()) { |
| 510 LOG(ERROR) << "DumpAndCrashTargetProcess currently not supported x64->x86"; |
| 511 return false; |
| 512 } |
| 513 #endif // ARCH_CPU_64_BITS |
| 514 |
| 515 ScopedProcessSuspend suspend(process); |
| 516 |
| 517 // If no thread handle was provided, or the thread has already exited, we pass |
| 518 // 0 to the handler, which indicates no fake exception record to be created. |
| 519 DWORD thread_id = 0; |
| 520 if (blame_thread) { |
| 521 // Now that we've suspended the process, if our thread hasn't exited, we |
| 522 // know we're relatively safe to pass the thread id through. |
| 523 if (WaitForSingleObject(blame_thread, 0) == WAIT_TIMEOUT) { |
| 524 static const auto get_thread_id = |
| 525 GET_FUNCTION_REQUIRED(L"kernel32.dll", ::GetThreadId); |
| 526 thread_id = get_thread_id(blame_thread); |
| 527 } |
| 528 } |
| 529 |
| 530 const size_t kInjectBufferSize = 4 * 1024; |
| 531 WinVMAddress inject_memory = |
| 532 reinterpret_cast<WinVMAddress>(VirtualAllocEx(process, |
| 533 nullptr, |
| 534 kInjectBufferSize, |
| 535 MEM_RESERVE | MEM_COMMIT, |
| 536 PAGE_READWRITE)); |
| 537 if (!inject_memory) { |
| 538 PLOG(ERROR) << "VirtualAllocEx"; |
| 539 return false; |
| 540 } |
| 541 |
| 542 // Because we're the same bitness as our target, we can rely kernel32 being |
| 543 // loaded at the same address in our process as the target, and just look up |
| 544 // its address here. |
| 545 WinVMAddress raise_exception_address = |
| 546 reinterpret_cast<WinVMAddress>(&RaiseException); |
| 547 |
| 548 WinVMAddress code_entry_point = 0; |
| 549 std::vector<unsigned char> data_to_write; |
| 550 if (process_info.Is64Bit()) { |
| 551 // Data written is first, the data for the 4th argument (lpArguments) to |
| 552 // RaiseException(). A two element array: |
| 553 // |
| 554 // DWORD64: thread_id |
| 555 // DWORD64: exception_code |
| 556 // |
| 557 // Following that, code which sets the arguments to RaiseException() and |
| 558 // then calls it: |
| 559 // |
| 560 // mov r9, <data_array_address> |
| 561 // mov r8d, 2 ; nNumberOfArguments |
| 562 // mov edx, 1 ; dwExceptionFlags = EXCEPTION_NONCONTINUABLE |
| 563 // mov ecx, 0xcca11ed ; dwExceptionCode, interpreted specially by the |
| 564 // ; handler. |
| 565 // jmp <address_of_RaiseException> |
| 566 // |
| 567 // Note that the first three arguments to RaiseException() are DWORDs even |
| 568 // on x64, so only the 4th argument (a pointer) is a full-width register. |
| 569 // |
| 570 // We also don't need to set up a stack or use call, since the only |
| 571 // registers modified are volatile ones, and we can just jmp straight to |
| 572 // RaiseException(). |
| 573 |
| 574 // The data array. |
| 575 AddUint64(&data_to_write, thread_id); |
| 576 AddUint64(&data_to_write, exception_code); |
| 577 |
| 578 // The thread entry point. |
| 579 code_entry_point = inject_memory + data_to_write.size(); |
| 580 |
| 581 // r9 = pointer to data. |
| 582 data_to_write.push_back(0x49); |
| 583 data_to_write.push_back(0xb9); |
| 584 AddUint64(&data_to_write, inject_memory); |
| 585 |
| 586 // r8d = 2 for nNumberOfArguments. |
| 587 data_to_write.push_back(0x41); |
| 588 data_to_write.push_back(0xb8); |
| 589 AddUint32(&data_to_write, 2); |
| 590 |
| 591 // edx = 1 for dwExceptionFlags. |
| 592 data_to_write.push_back(0xba); |
| 593 AddUint32(&data_to_write, 1); |
| 594 |
| 595 // ecx = kTriggeredExceptionCode for dwExceptionCode. |
| 596 data_to_write.push_back(0xb9); |
| 597 AddUint32(&data_to_write, kTriggeredExceptionCode); |
| 598 |
| 599 // jmp to RaiseException() via rax. |
| 600 data_to_write.push_back(0x48); // mov rax, imm. |
| 601 data_to_write.push_back(0xb8); |
| 602 AddUint64(&data_to_write, raise_exception_address); |
| 603 data_to_write.push_back(0xff); // jmp rax. |
| 604 data_to_write.push_back(0xe0); |
| 605 } else { |
| 606 // Data written is first, the data for the 4th argument (lpArguments) to |
| 607 // RaiseException(). A two element array: |
| 608 // |
| 609 // DWORD: thread_id |
| 610 // DWORD: exception_code |
| 611 // |
| 612 // Following that, code which pushes our arguments to RaiseException() and |
| 613 // then calls it: |
| 614 // |
| 615 // push <data_array_address> |
| 616 // push 2 ; nNumberOfArguments |
| 617 // push 1 ; dwExceptionFlags = EXCEPTION_NONCONTINUABLE |
| 618 // push 0xcca11ed ; dwExceptionCode, interpreted specially by the handler. |
| 619 // call <address_of_RaiseException> |
| 620 // ud2 ; Generate invalid opcode to make sure we still crash if we return |
| 621 // ; for some reason. |
| 622 // |
| 623 // No need to clean up the stack, as RaiseException() is __stdcall. |
| 624 |
| 625 // The data array. |
| 626 AddUint32(&data_to_write, thread_id); |
| 627 AddUint32(&data_to_write, exception_code); |
| 628 |
| 629 // The thread entry point. |
| 630 code_entry_point = inject_memory + data_to_write.size(); |
| 631 |
| 632 // Push data address. |
| 633 data_to_write.push_back(0x68); |
| 634 AddUint32(&data_to_write, static_cast<uint32_t>(inject_memory)); |
| 635 |
| 636 // Push 2 for nNumberOfArguments. |
| 637 data_to_write.push_back(0x6a); |
| 638 data_to_write.push_back(2); |
| 639 |
| 640 // Push 1 for dwExceptionCode. |
| 641 data_to_write.push_back(0x6a); |
| 642 data_to_write.push_back(1); |
| 643 |
| 644 // Push dwExceptionFlags. |
| 645 data_to_write.push_back(0x68); |
| 646 AddUint32(&data_to_write, kTriggeredExceptionCode); |
| 647 |
| 648 // Relative call to RaiseException(). |
| 649 int64_t relative_address_to_raise_exception = |
| 650 raise_exception_address - (inject_memory + data_to_write.size() + 5); |
| 651 data_to_write.push_back(0xe8); |
| 652 AddUint32(&data_to_write, |
| 653 static_cast<uint32_t>(relative_address_to_raise_exception)); |
| 654 |
| 655 // ud2. |
| 656 data_to_write.push_back(0x0f); |
| 657 data_to_write.push_back(0x0b); |
| 658 } |
| 659 |
| 660 DCHECK_LT(data_to_write.size(), kInjectBufferSize); |
| 661 |
| 662 SIZE_T bytes_written; |
| 663 if (!WriteProcessMemory(process, |
| 664 reinterpret_cast<void*>(inject_memory), |
| 665 data_to_write.data(), |
| 666 data_to_write.size(), |
| 667 &bytes_written)) { |
| 668 PLOG(ERROR) << "WriteProcessMemory"; |
| 669 return false; |
| 670 } |
| 671 |
| 672 if (bytes_written != data_to_write.size()) { |
| 673 LOG(ERROR) << "WriteProcessMemory unexpected number of bytes"; |
| 674 return false; |
| 675 } |
| 676 |
| 677 if (!FlushInstructionCache( |
| 678 process, reinterpret_cast<void*>(inject_memory), bytes_written)) { |
| 679 PLOG(ERROR) << "FlushInstructionCache"; |
| 680 return false; |
| 681 } |
| 682 |
| 683 DWORD old_protect; |
| 684 if (!VirtualProtectEx(process, |
| 685 reinterpret_cast<void*>(inject_memory), |
| 686 kInjectBufferSize, |
| 687 PAGE_EXECUTE_READ, |
| 688 &old_protect)) { |
| 689 PLOG(ERROR) << "VirtualProtectEx"; |
| 690 return false; |
| 691 } |
| 692 |
| 693 // Cause an exception in the target process by creating a thread which calls |
| 694 // RaiseException with our arguments above. Note that we cannot get away with |
| 695 // using DebugBreakProcess() (nothing happens unless a debugger is attached) |
| 696 // and we cannot get away with CreateRemoteThread() because it doesn't work if |
| 697 // the target is hung waiting for the loader lock. We use NtCreateThreadEx() |
| 698 // with the SKIP_THREAD_ATTACH flag, which skips various notifications, |
| 699 // letting this cause an exception, even when the target is stuck in the |
| 700 // loader lock. |
| 701 HANDLE injected_thread; |
| 702 const size_t kStackSize = 0x4000; // This is what DebugBreakProcess() uses. |
| 703 NTSTATUS status = NtCreateThreadEx(&injected_thread, |
| 704 STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL, |
| 705 nullptr, |
| 706 process, |
| 707 reinterpret_cast<void*>(code_entry_point), |
| 708 nullptr, |
| 709 THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH, |
| 710 0, |
| 711 kStackSize, |
| 712 0, |
| 713 nullptr); |
| 714 if (!NT_SUCCESS(status)) { |
| 715 NTSTATUS_LOG(ERROR, status) << "NtCreateThreadEx"; |
| 716 return false; |
| 717 } |
| 718 |
| 719 bool result = true; |
| 720 if (WaitForSingleObject(injected_thread, 60 * 1000) != WAIT_OBJECT_0) { |
| 721 PLOG(ERROR) << "WaitForSingleObject"; |
| 722 result = false; |
| 723 } |
| 724 |
| 725 status = NtClose(injected_thread); |
| 726 if (!NT_SUCCESS(status)) { |
| 727 NTSTATUS_LOG(ERROR, status) << "NtClose"; |
| 728 result = false; |
| 729 } |
| 730 |
| 731 return result; |
| 732 } |
| 733 |
468 } // namespace crashpad | 734 } // namespace crashpad |
OLD | NEW |