| Index: util/win/process_info.cc
|
| diff --git a/util/win/process_info.cc b/util/win/process_info.cc
|
| index 66157aa482c1efccecb849014a3444851c161cdf..cc10fbc57ca11f936b9e7d88b2769dcb586fce24 100644
|
| --- a/util/win/process_info.cc
|
| +++ b/util/win/process_info.cc
|
| @@ -14,7 +14,11 @@
|
|
|
| #include "util/win/process_info.h"
|
|
|
| +#include <winternl.h>
|
| +
|
| #include "base/logging.h"
|
| +#include "util/numeric/safe_assignment.h"
|
| +#include "util/win/process_structs.h"
|
|
|
| namespace crashpad {
|
|
|
| @@ -50,8 +54,9 @@ bool IsProcessWow64(HANDLE process_handle) {
|
| return is_wow64;
|
| }
|
|
|
| +template <class T>
|
| bool ReadUnicodeString(HANDLE process,
|
| - const UNICODE_STRING& us,
|
| + const process_types::UNICODE_STRING<T>& us,
|
| std::wstring* result) {
|
| if (us.Length == 0) {
|
| result->clear();
|
| @@ -60,8 +65,11 @@ bool ReadUnicodeString(HANDLE process,
|
| DCHECK_EQ(us.Length % sizeof(wchar_t), 0u);
|
| result->resize(us.Length / sizeof(wchar_t));
|
| SIZE_T bytes_read;
|
| - if (!ReadProcessMemory(
|
| - process, us.Buffer, &result->operator[](0), us.Length, &bytes_read)) {
|
| + if (!ReadProcessMemory(process,
|
| + reinterpret_cast<const void*>(us.Buffer),
|
| + &result->operator[](0),
|
| + us.Length,
|
| + &bytes_read)) {
|
| PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING";
|
| return false;
|
| }
|
| @@ -91,35 +99,89 @@ template <class T> bool ReadStruct(HANDLE process, uintptr_t at, T* into) {
|
| return true;
|
| }
|
|
|
| -// PEB_LDR_DATA in winternl.h doesn't document the trailing
|
| -// InInitializationOrderModuleList field. See `dt ntdll!PEB_LDR_DATA`.
|
| -struct FULL_PEB_LDR_DATA : public PEB_LDR_DATA {
|
| - LIST_ENTRY InInitializationOrderModuleList;
|
| -};
|
| -
|
| -// LDR_DATA_TABLE_ENTRY doesn't include InInitializationOrderLinks, define a
|
| -// complete version here. See `dt ntdll!_LDR_DATA_TABLE_ENTRY`.
|
| -struct FULL_LDR_DATA_TABLE_ENTRY {
|
| - LIST_ENTRY InLoadOrderLinks;
|
| - LIST_ENTRY InMemoryOrderLinks;
|
| - LIST_ENTRY InInitializationOrderLinks;
|
| - PVOID DllBase;
|
| - PVOID EntryPoint;
|
| - ULONG SizeOfImage;
|
| - UNICODE_STRING FullDllName;
|
| - UNICODE_STRING BaseDllName;
|
| - ULONG Flags;
|
| - WORD LoadCount;
|
| - WORD TlsIndex;
|
| - LIST_ENTRY HashLinks;
|
| - ULONG TimeDateStamp;
|
| - _ACTIVATION_CONTEXT* EntryPointActivationContext;
|
| -};
|
| -
|
| } // namespace
|
|
|
| +template <class Traits>
|
| +bool ReadProcessData(HANDLE process,
|
| + uintptr_t peb_address_uintptr,
|
| + ProcessInfo* process_info) {
|
| + Traits::Pointer peb_address;
|
| + if (!AssignIfInRange(&peb_address, peb_address_uintptr)) {
|
| + LOG(ERROR) << "peb_address_uintptr " << peb_address_uintptr
|
| + << " out of range";
|
| + return false;
|
| + }
|
| +
|
| + // Try to read the process environment block.
|
| + process_types::PEB<Traits> peb;
|
| + if (!ReadStruct(process, peb_address, &peb))
|
| + return false;
|
| +
|
| + process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters;
|
| + if (!ReadStruct(process, peb.ProcessParameters, &process_parameters))
|
| + return false;
|
| +
|
| + if (!ReadUnicodeString(process,
|
| + process_parameters.CommandLine,
|
| + &process_info->command_line_)) {
|
| + return false;
|
| + }
|
| +
|
| + process_types::PEB_LDR_DATA<Traits> peb_ldr_data;
|
| + if (!ReadStruct(process, peb.Ldr, &peb_ldr_data))
|
| + return false;
|
| +
|
| + std::wstring module;
|
| + process_types::LDR_DATA_TABLE_ENTRY<Traits> ldr_data_table_entry;
|
| +
|
| + // Include the first module in the memory order list to get our the main
|
| + // executable's name, as it's not included in initialization order below.
|
| + if (!ReadStruct(process,
|
| + reinterpret_cast<uintptr_t>(
|
| + reinterpret_cast<const char*>(
|
| + peb_ldr_data.InMemoryOrderModuleList.Flink) -
|
| + offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
|
| + InMemoryOrderLinks)),
|
| + &ldr_data_table_entry)) {
|
| + return false;
|
| + }
|
| + if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module))
|
| + return false;
|
| + process_info->modules_.push_back(module);
|
| +
|
| + // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded
|
| + // modules. We use this method rather than EnumProcessModules to get the
|
| + // modules in initialization order rather than memory order.
|
| + Traits::Pointer last = peb_ldr_data.InInitializationOrderModuleList.Blink;
|
| + for (Traits::Pointer cur = peb_ldr_data.InInitializationOrderModuleList.Flink;
|
| + ;
|
| + cur = ldr_data_table_entry.InInitializationOrderLinks.Flink) {
|
| + // |cur| is the pointer to the LIST_ENTRY embedded in the
|
| + // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need
|
| + // to read from the target, and also offset back to the beginning of the
|
| + // structure.
|
| + if (!ReadStruct(process,
|
| + reinterpret_cast<uintptr_t>(
|
| + reinterpret_cast<const char*>(cur) -
|
| + offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
|
| + InInitializationOrderLinks)),
|
| + &ldr_data_table_entry)) {
|
| + break;
|
| + }
|
| + // TODO(scottmg): Capture TimeDateStamp, Checksum, etc. too?
|
| + if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module))
|
| + break;
|
| + process_info->modules_.push_back(module);
|
| + if (cur == last)
|
| + break;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| ProcessInfo::ProcessInfo()
|
| - : process_basic_information_(),
|
| + : process_id_(),
|
| + inherited_from_process_id_(),
|
| command_line_(),
|
| modules_(),
|
| is_64_bit_(false),
|
| @@ -147,12 +209,7 @@ bool ProcessInfo::Initialize(HANDLE process) {
|
| system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
|
| }
|
|
|
| -#if ARCH_CPU_64_BITS
|
| - if (!is_64_bit_) {
|
| - LOG(ERROR) << "Reading different bitness not yet supported";
|
| - return false;
|
| - }
|
| -#else
|
| +#if ARCH_CPU_32_BITS
|
| if (is_64_bit_) {
|
| LOG(ERROR) << "Reading x64 process from x86 process not supported";
|
| return false;
|
| @@ -160,93 +217,67 @@ bool ProcessInfo::Initialize(HANDLE process) {
|
| #endif
|
|
|
| ULONG bytes_returned;
|
| + // We assume this process is not running on Wow64. The
|
| + // PROCESS_BASIC_INFORMATION uses the OS size (that is, even Wow64 has a 64
|
| + // bit one.)
|
| + // TODO(scottmg): Either support running as Wow64, or check/resolve this at a
|
| + // higher level.
|
| +#if ARCH_CPU_32_BITS
|
| + process_types::PROCESS_BASIC_INFORMATION<process_types::internal::Traits32>
|
| + process_basic_information;
|
| +#else
|
| + process_types::PROCESS_BASIC_INFORMATION<process_types::internal::Traits64>
|
| + process_basic_information;
|
| +#endif
|
| NTSTATUS status =
|
| crashpad::NtQueryInformationProcess(process,
|
| ProcessBasicInformation,
|
| - &process_basic_information_,
|
| - sizeof(process_basic_information_),
|
| + &process_basic_information,
|
| + sizeof(process_basic_information),
|
| &bytes_returned);
|
| if (status < 0) {
|
| LOG(ERROR) << "NtQueryInformationProcess: status=" << status;
|
| return false;
|
| }
|
| - if (bytes_returned != sizeof(process_basic_information_)) {
|
| + if (bytes_returned != sizeof(process_basic_information)) {
|
| LOG(ERROR) << "NtQueryInformationProcess incorrect size";
|
| return false;
|
| }
|
| -
|
| - // Try to read the process environment block.
|
| - PEB peb;
|
| - if (!ReadStruct(process,
|
| - reinterpret_cast<uintptr_t>(
|
| - process_basic_information_.PebBaseAddress),
|
| - &peb)) {
|
| - return false;
|
| - }
|
| -
|
| - RTL_USER_PROCESS_PARAMETERS process_parameters;
|
| - if (!ReadStruct(process,
|
| - reinterpret_cast<uintptr_t>(peb.ProcessParameters),
|
| - &process_parameters)) {
|
| - return false;
|
| - }
|
| -
|
| - if (!ReadUnicodeString(
|
| - process, process_parameters.CommandLine, &command_line_)) {
|
| - return false;
|
| + process_id_ = process_basic_information.UniqueProcessId;
|
| + inherited_from_process_id_ =
|
| + process_basic_information.InheritedFromUniqueProcessId;
|
| +
|
| + // We now want to read the PEB to gather the rest of our information. The
|
| + // PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32,
|
| + // but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both).
|
| + // The address of this is found by a second call to NtQueryInformationProcess.
|
| + uintptr_t peb_address = process_basic_information.PebBaseAddress;
|
| + if (is_wow64_) {
|
| + ULONG_PTR wow64_peb_address;
|
| + status =
|
| + crashpad::NtQueryInformationProcess(process,
|
| + ProcessWow64Information,
|
| + &wow64_peb_address,
|
| + sizeof(wow64_peb_address),
|
| + &bytes_returned);
|
| + if (status < 0) {
|
| + LOG(ERROR) << "NtQueryInformationProcess: status=" << status;
|
| + return false;
|
| + }
|
| + if (bytes_returned != sizeof(wow64_peb_address)) {
|
| + LOG(ERROR) << "NtQueryInformationProcess incorrect size";
|
| + return false;
|
| + }
|
| + peb_address = wow64_peb_address;
|
| }
|
|
|
| - FULL_PEB_LDR_DATA peb_ldr_data;
|
| - if (!ReadStruct(process, reinterpret_cast<uintptr_t>(peb.Ldr), &peb_ldr_data))
|
| - return false;
|
| -
|
| - // Include the first module in the memory order list to get our own name as
|
| - // it's not included in initialization order below.
|
| - std::wstring self_module;
|
| - FULL_LDR_DATA_TABLE_ENTRY self_ldr_data_table_entry;
|
| - if (!ReadStruct(process,
|
| - reinterpret_cast<uintptr_t>(
|
| - reinterpret_cast<const char*>(
|
| - peb_ldr_data.InMemoryOrderModuleList.Flink) -
|
| - offsetof(FULL_LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks)),
|
| - &self_ldr_data_table_entry)) {
|
| - return false;
|
| - }
|
| - if (!ReadUnicodeString(
|
| - process, self_ldr_data_table_entry.FullDllName, &self_module)) {
|
| + // Read the PEB data using the correct word size.
|
| + bool result = is_64_bit_ ? ReadProcessData<process_types::internal::Traits64>(
|
| + process, peb_address, this)
|
| + : ReadProcessData<process_types::internal::Traits32>(
|
| + process, peb_address, this);
|
| + if (!result)
|
| return false;
|
| - }
|
| - modules_.push_back(self_module);
|
| -
|
| - // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded
|
| - // modules. We use this method rather than EnumProcessModules to get the
|
| - // modules in initialization order rather than memory order.
|
| - const LIST_ENTRY* last = peb_ldr_data.InInitializationOrderModuleList.Blink;
|
| - FULL_LDR_DATA_TABLE_ENTRY ldr_data_table_entry;
|
| - for (const LIST_ENTRY* cur =
|
| - peb_ldr_data.InInitializationOrderModuleList.Flink;
|
| - ;
|
| - cur = ldr_data_table_entry.InInitializationOrderLinks.Flink) {
|
| - // |cur| is the pointer to the LIST_ENTRY embedded in the
|
| - // FULL_LDR_DATA_TABLE_ENTRY, in the target process's address space. So we
|
| - // need to read from the target, and also offset back to the beginning of
|
| - // the structure.
|
| - if (!ReadStruct(
|
| - process,
|
| - reinterpret_cast<uintptr_t>(reinterpret_cast<const char*>(cur) -
|
| - offsetof(FULL_LDR_DATA_TABLE_ENTRY,
|
| - InInitializationOrderLinks)),
|
| - &ldr_data_table_entry)) {
|
| - break;
|
| - }
|
| - // TODO(scottmg): Capture TimeDateStamp, Checksum, etc. too?
|
| - std::wstring module;
|
| - if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module))
|
| - break;
|
| - modules_.push_back(module);
|
| - if (cur == last)
|
| - break;
|
| - }
|
|
|
| INITIALIZATION_STATE_SET_VALID(initialized_);
|
| return true;
|
| @@ -264,12 +295,12 @@ bool ProcessInfo::IsWow64() const {
|
|
|
| pid_t ProcessInfo::ProcessID() const {
|
| INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| - return process_basic_information_.UniqueProcessId;
|
| + return process_id_;
|
| }
|
|
|
| pid_t ProcessInfo::ParentProcessID() const {
|
| INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| - return process_basic_information_.InheritedFromUniqueProcessId;
|
| + return inherited_from_process_id_;
|
| }
|
|
|
| bool ProcessInfo::CommandLine(std::wstring* command_line) const {
|
|
|