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, |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
13 // limitations under the License. | 13 // limitations under the License. |
14 | 14 |
15 #include "util/win/process_info.h" | 15 #include "util/win/process_info.h" |
16 | 16 |
17 #include <winternl.h> | 17 #include <winternl.h> |
18 | 18 |
19 #include <algorithm> | 19 #include <algorithm> |
20 #include <limits> | 20 #include <limits> |
21 | 21 |
22 #include "base/logging.h" | 22 #include "base/logging.h" |
23 #include "base/memory/scoped_ptr.h" | |
23 #include "base/strings/stringprintf.h" | 24 #include "base/strings/stringprintf.h" |
24 #include "base/template_util.h" | 25 #include "base/template_util.h" |
25 #include "build/build_config.h" | 26 #include "build/build_config.h" |
26 #include "util/numeric/safe_assignment.h" | 27 #include "util/numeric/safe_assignment.h" |
28 #include "util/win/nt_internals.h" | |
27 #include "util/win/ntstatus_logging.h" | 29 #include "util/win/ntstatus_logging.h" |
28 #include "util/win/process_structs.h" | 30 #include "util/win/process_structs.h" |
31 #include "util/win/scoped_handle.h" | |
29 | 32 |
30 namespace crashpad { | 33 namespace crashpad { |
31 | 34 |
32 namespace { | 35 namespace { |
33 | 36 |
34 NTSTATUS NtQueryInformationProcess(HANDLE process_handle, | 37 NTSTATUS NtQueryInformationProcess(HANDLE process_handle, |
35 PROCESSINFOCLASS process_information_class, | 38 PROCESSINFOCLASS process_information_class, |
36 PVOID process_information, | 39 PVOID process_information, |
37 ULONG process_information_length, | 40 ULONG process_information_length, |
38 PULONG return_length) { | 41 PULONG return_length) { |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
120 mbi64.BaseAddress = reinterpret_cast<ULONGLONG>(mbi.BaseAddress); | 123 mbi64.BaseAddress = reinterpret_cast<ULONGLONG>(mbi.BaseAddress); |
121 mbi64.AllocationBase = reinterpret_cast<ULONGLONG>(mbi.AllocationBase); | 124 mbi64.AllocationBase = reinterpret_cast<ULONGLONG>(mbi.AllocationBase); |
122 mbi64.AllocationProtect = mbi.AllocationProtect; | 125 mbi64.AllocationProtect = mbi.AllocationProtect; |
123 mbi64.RegionSize = mbi.RegionSize; | 126 mbi64.RegionSize = mbi.RegionSize; |
124 mbi64.State = mbi.State; | 127 mbi64.State = mbi.State; |
125 mbi64.Protect = mbi.Protect; | 128 mbi64.Protect = mbi.Protect; |
126 mbi64.Type = mbi.Type; | 129 mbi64.Type = mbi.Type; |
127 return mbi64; | 130 return mbi64; |
128 } | 131 } |
129 | 132 |
133 // NtQueryObject with a retry for size mismatch as well as a minimum size to | |
134 // retrieve (and expect). | |
135 scoped_ptr<uint8_t[]> QueryObject( | |
136 HANDLE handle, | |
137 OBJECT_INFORMATION_CLASS object_information_class, | |
138 ULONG minimum_size) { | |
139 ULONG size = minimum_size; | |
140 ULONG return_length; | |
141 scoped_ptr<uint8_t[]> buffer(new uint8_t[size]); | |
142 NTSTATUS status = crashpad::NtQueryObject( | |
143 handle, object_information_class, buffer.get(), size, &return_length); | |
144 if (status == STATUS_INFO_LENGTH_MISMATCH) { | |
145 DCHECK_GT(return_length, size); | |
146 size = return_length; | |
147 buffer.reset(new uint8_t[size]); | |
148 status = crashpad::NtQueryObject( | |
149 handle, object_information_class, buffer.get(), size, &return_length); | |
150 } | |
151 | |
152 if (!NT_SUCCESS(status)) { | |
153 NTSTATUS_LOG(ERROR, status) << "NtQueryObject"; | |
154 return scoped_ptr<uint8_t[]>(); | |
155 } | |
156 | |
157 DCHECK_GE(return_length, minimum_size); | |
158 return buffer.Pass(); | |
159 } | |
160 | |
130 } // namespace | 161 } // namespace |
131 | 162 |
132 template <class Traits> | 163 template <class Traits> |
133 bool GetProcessBasicInformation(HANDLE process, | 164 bool GetProcessBasicInformation(HANDLE process, |
134 bool is_wow64, | 165 bool is_wow64, |
135 ProcessInfo* process_info, | 166 ProcessInfo* process_info, |
136 WinVMAddress* peb_address, | 167 WinVMAddress* peb_address, |
137 WinVMSize* peb_size) { | 168 WinVMSize* peb_size) { |
138 ULONG bytes_returned; | 169 ULONG bytes_returned; |
139 process_types::PROCESS_BASIC_INFORMATION<Traits> process_basic_information; | 170 process_types::PROCESS_BASIC_INFORMATION<Traits> process_basic_information; |
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
307 | 338 |
308 if (memory_basic_information.RegionSize == 0) { | 339 if (memory_basic_information.RegionSize == 0) { |
309 LOG(ERROR) << "RegionSize == 0"; | 340 LOG(ERROR) << "RegionSize == 0"; |
310 return false; | 341 return false; |
311 } | 342 } |
312 } | 343 } |
313 | 344 |
314 return true; | 345 return true; |
315 } | 346 } |
316 | 347 |
348 std::vector<ProcessInfo::Handle> ProcessInfo::BuildHandleVector( | |
349 HANDLE process) const { | |
350 ULONG buffer_size = 2 * 1024 * 1024; | |
351 scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); | |
352 | |
353 // Typically if the buffer were too small, STATUS_INFO_LENGTH_MISMATCH would | |
354 // return the correct size in the final argument, but it does not for | |
355 // SystemExtendedHandleInformation, so we loop and attempt larger sizes. | |
356 NTSTATUS status; | |
357 ULONG returned_length; | |
358 for (int tries = 0; tries < 5; ++tries) { | |
359 status = crashpad::NtQuerySystemInformation( | |
360 static_cast<SYSTEM_INFORMATION_CLASS>(SystemExtendedHandleInformation), | |
361 buffer.get(), | |
362 buffer_size, | |
363 &returned_length); | |
364 if (NT_SUCCESS(status) || status != STATUS_INFO_LENGTH_MISMATCH) | |
365 break; | |
366 | |
367 buffer_size *= 2; | |
368 buffer.reset(new uint8_t[buffer_size]); | |
369 } | |
Mark Mentovai
2015/10/16 22:04:05
This is a kind of big allocation, so reset() to em
scottmg
2015/10/16 22:25:21
Done.
| |
370 | |
371 if (!NT_SUCCESS(status)) { | |
372 NTSTATUS_LOG(ERROR, status) | |
373 << "NtQuerySystemInformation SystemExtendedHandleInformation"; | |
374 return std::vector<Handle>(); | |
375 } | |
376 | |
377 const auto& system_handle_information_ex = | |
378 *reinterpret_cast<process_types::SYSTEM_HANDLE_INFORMATION_EX*>( | |
379 buffer.get()); | |
380 | |
381 DCHECK_LE(offsetof(process_types::SYSTEM_HANDLE_INFORMATION_EX, Handles) + | |
382 system_handle_information_ex.NumberOfHandles * | |
383 sizeof(system_handle_information_ex.Handles[0]), | |
384 returned_length); | |
385 | |
386 std::vector<Handle> handles; | |
387 | |
388 for (size_t i = 0; i < system_handle_information_ex.NumberOfHandles; ++i) { | |
389 const auto& handle = system_handle_information_ex.Handles[i]; | |
390 if (handle.UniqueProcessId != process_id_) | |
391 continue; | |
392 | |
393 Handle result_handle; | |
394 result_handle.handle = | |
395 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(handle.HandleValue)); | |
396 result_handle.attributes = handle.HandleAttributes; | |
397 result_handle.granted_access = handle.GrantedAccess; | |
398 | |
399 // TODO(scottmg): Could special case for self. | |
400 HANDLE dup_handle; | |
401 if (DuplicateHandle(process, | |
402 reinterpret_cast<HANDLE>(handle.HandleValue), | |
403 GetCurrentProcess(), | |
404 &dup_handle, | |
405 0, | |
406 false, | |
407 DUPLICATE_SAME_ACCESS)) { | |
408 // Some handles cannot be duplicated, for example, handles of type | |
409 // EtwRegistration. If we fail to duplicate, then we can't gather any more | |
410 // information, but include the information that we do have already. | |
411 ScopedKernelHANDLE scoped_dup_handle(dup_handle); | |
412 | |
413 scoped_ptr<uint8_t[]> object_basic_information_buffer = | |
414 QueryObject(dup_handle, | |
415 ObjectBasicInformation, | |
416 sizeof(PUBLIC_OBJECT_BASIC_INFORMATION)); | |
417 if (object_basic_information_buffer) { | |
418 PUBLIC_OBJECT_BASIC_INFORMATION* object_basic_information = | |
419 reinterpret_cast<PUBLIC_OBJECT_BASIC_INFORMATION*>( | |
420 object_basic_information_buffer.get()); | |
421 // The Attributes and GrantedAccess sometimes differ slightly between | |
422 // the data retrieved in SYSTEM_HANDLE_INFORMATION_EX and | |
423 // PUBLIC_OBJECT_TYPE_INFORMATION. We prefer the values in | |
424 // SYSTEM_HANDLE_INFORMATION_EX because they were retrieved from the | |
425 // target process, rather than on the duplicated handle, so don't use | |
426 // them here. | |
427 | |
428 // Subtract one to account for our DuplicateHandle() and another for | |
429 // NtQueryObject() while the query was being executed. | |
430 DCHECK_GT(object_basic_information->PointerCount, 2u); | |
431 result_handle.pointer_count = | |
432 object_basic_information->PointerCount - 2; | |
433 | |
434 // Subtract one to account for our DuplicateHandle(). | |
435 DCHECK_GT(object_basic_information->HandleCount, 1u); | |
436 result_handle.handle_count = object_basic_information->HandleCount - 1; | |
437 } | |
438 | |
439 scoped_ptr<uint8_t[]> object_type_information_buffer = | |
440 QueryObject(dup_handle, | |
441 ObjectTypeInformation, | |
442 sizeof(PUBLIC_OBJECT_TYPE_INFORMATION)); | |
443 if (object_type_information_buffer) { | |
444 PUBLIC_OBJECT_TYPE_INFORMATION* object_type_information = | |
445 reinterpret_cast<PUBLIC_OBJECT_TYPE_INFORMATION*>( | |
446 object_type_information_buffer.get()); | |
447 | |
448 DCHECK_EQ(object_type_information->TypeName.Length % | |
449 sizeof(result_handle.type_name[0]), | |
450 0u); | |
451 result_handle.type_name = | |
452 std::wstring(object_type_information->TypeName.Buffer, | |
453 object_type_information->TypeName.Length / | |
454 sizeof(result_handle.type_name[0])); | |
455 } | |
456 } | |
457 | |
458 handles.push_back(result_handle); | |
459 } | |
460 return handles; | |
461 } | |
462 | |
317 ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() { | 463 ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() { |
318 } | 464 } |
319 | 465 |
320 ProcessInfo::Module::~Module() { | 466 ProcessInfo::Module::~Module() { |
321 } | 467 } |
322 | 468 |
469 ProcessInfo::Handle::Handle() | |
470 : type_name(), | |
471 handle(0), | |
472 attributes(0), | |
473 granted_access(0), | |
474 pointer_count(0), | |
475 handle_count(0) { | |
476 } | |
477 | |
478 ProcessInfo::Handle::~Handle() { | |
479 } | |
480 | |
323 ProcessInfo::ProcessInfo() | 481 ProcessInfo::ProcessInfo() |
324 : process_id_(), | 482 : process_id_(), |
325 inherited_from_process_id_(), | 483 inherited_from_process_id_(), |
484 process_(), | |
326 command_line_(), | 485 command_line_(), |
327 peb_address_(0), | 486 peb_address_(0), |
328 peb_size_(0), | 487 peb_size_(0), |
329 modules_(), | 488 modules_(), |
330 memory_info_(), | 489 memory_info_(), |
490 handles_(), | |
331 is_64_bit_(false), | 491 is_64_bit_(false), |
332 is_wow64_(false), | 492 is_wow64_(false), |
333 initialized_() { | 493 initialized_() { |
334 } | 494 } |
335 | 495 |
336 ProcessInfo::~ProcessInfo() { | 496 ProcessInfo::~ProcessInfo() { |
337 } | 497 } |
338 | 498 |
339 bool ProcessInfo::Initialize(HANDLE process) { | 499 bool ProcessInfo::Initialize(HANDLE process) { |
340 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); | 500 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
341 | 501 |
502 process_ = process; | |
503 | |
342 is_wow64_ = IsProcessWow64(process); | 504 is_wow64_ = IsProcessWow64(process); |
343 | 505 |
344 if (is_wow64_) { | 506 if (is_wow64_) { |
345 // If it's WoW64, then it's 32-on-64. | 507 // If it's WoW64, then it's 32-on-64. |
346 is_64_bit_ = false; | 508 is_64_bit_ = false; |
347 } else { | 509 } else { |
348 // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to | 510 // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to |
349 // distinguish between these two cases. | 511 // distinguish between these two cases. |
350 SYSTEM_INFO system_info; | 512 SYSTEM_INFO system_info; |
351 GetSystemInfo(&system_info); | 513 GetSystemInfo(&system_info); |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
432 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 594 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
433 return memory_info_; | 595 return memory_info_; |
434 } | 596 } |
435 | 597 |
436 std::vector<CheckedRange<WinVMAddress, WinVMSize>> | 598 std::vector<CheckedRange<WinVMAddress, WinVMSize>> |
437 ProcessInfo::GetReadableRanges( | 599 ProcessInfo::GetReadableRanges( |
438 const CheckedRange<WinVMAddress, WinVMSize>& range) const { | 600 const CheckedRange<WinVMAddress, WinVMSize>& range) const { |
439 return GetReadableRangesOfMemoryMap(range, MemoryInfo()); | 601 return GetReadableRangesOfMemoryMap(range, MemoryInfo()); |
440 } | 602 } |
441 | 603 |
604 const std::vector<ProcessInfo::Handle>& ProcessInfo::Handles() { | |
605 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
606 if (handles_.empty()) | |
607 handles_ = BuildHandleVector(process_); | |
608 return handles_; | |
609 } | |
610 | |
442 std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap( | 611 std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap( |
443 const CheckedRange<WinVMAddress, WinVMSize>& range, | 612 const CheckedRange<WinVMAddress, WinVMSize>& range, |
444 const std::vector<MEMORY_BASIC_INFORMATION64>& memory_info) { | 613 const std::vector<MEMORY_BASIC_INFORMATION64>& memory_info) { |
445 using Range = CheckedRange<WinVMAddress, WinVMSize>; | 614 using Range = CheckedRange<WinVMAddress, WinVMSize>; |
446 | 615 |
447 // Find all the ranges that overlap the target range, maintaining their order. | 616 // Find all the ranges that overlap the target range, maintaining their order. |
448 std::vector<MEMORY_BASIC_INFORMATION64> overlapping; | 617 std::vector<MEMORY_BASIC_INFORMATION64> overlapping; |
449 for (const auto& mi : memory_info) { | 618 for (const auto& mi : memory_info) { |
450 static_assert(base::is_same<decltype(mi.BaseAddress), WinVMAddress>::value, | 619 static_assert(base::is_same<decltype(mi.BaseAddress), WinVMAddress>::value, |
451 "expected range address to be WinVMAddress"); | 620 "expected range address to be WinVMAddress"); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
495 } else { | 664 } else { |
496 result.push_back(as_ranges[i]); | 665 result.push_back(as_ranges[i]); |
497 } | 666 } |
498 DCHECK(result.back().IsValid()); | 667 DCHECK(result.back().IsValid()); |
499 } | 668 } |
500 | 669 |
501 return result; | 670 return result; |
502 } | 671 } |
503 | 672 |
504 } // namespace crashpad | 673 } // namespace crashpad |
OLD | NEW |