Chromium Code Reviews| Index: util/win/process_info.cc |
| diff --git a/util/win/process_info.cc b/util/win/process_info.cc |
| index 66157aa482c1efccecb849014a3444851c161cdf..46ef67a4b31b81469c0176f6170c6df69b9b9714 100644 |
| --- a/util/win/process_info.cc |
| +++ b/util/win/process_info.cc |
| @@ -14,7 +14,10 @@ |
| #include "util/win/process_info.h" |
| +#include <winternl.h> |
| + |
| #include "base/logging.h" |
| +#include "util/win/process_structs.h" |
| namespace crashpad { |
| @@ -50,8 +53,9 @@ bool IsProcessWow64(HANDLE process_handle) { |
| return is_wow64; |
| } |
| +template <class T> |
| bool ReadUnicodeString(HANDLE process, |
| - const UNICODE_STRING& us, |
| + const UNICODE_STRING_T<T>& us, |
| std::wstring* result) { |
| if (us.Length == 0) { |
| result->clear(); |
| @@ -60,8 +64,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 +98,83 @@ 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 T> |
| +bool ReadProcessData(HANDLE process, |
| + uintptr_t peb_address, |
|
Mark Mentovai
2015/03/09 16:11:46
peb_address is in the remote process’ address spac
scottmg
2015/03/09 21:16:36
Right, that's why. Done.
|
| + ProcessInfo* process_info) { |
| + // Try to read the process environment block. |
| + PEB_T<T> peb; |
| + if (!ReadStruct(process, peb_address, &peb)) |
| + return false; |
| + |
| + RTL_USER_PROCESS_PARAMETERS_T<T> process_parameters; |
| + if (!ReadStruct(process, peb.ProcessParameters, &process_parameters)) |
| + return false; |
| + |
| + if (!ReadUnicodeString(process, |
| + process_parameters.CommandLine, |
| + &process_info->command_line_)) { |
| + return false; |
| + } |
| + |
| + PEB_LDR_DATA_T<T> peb_ldr_data; |
| + if (!ReadStruct(process, 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; |
| + LDR_DATA_TABLE_ENTRY_T<T> self_ldr_data_table_entry; |
| + if (!ReadStruct(process, |
| + reinterpret_cast<uintptr_t>( |
| + reinterpret_cast<const char*>( |
| + peb_ldr_data.InMemoryOrderModuleList.Flink) - |
| + offsetof(LDR_DATA_TABLE_ENTRY_T<T>, InMemoryOrderLinks)), |
| + &self_ldr_data_table_entry)) { |
| + return false; |
| + } |
| + if (!ReadUnicodeString( |
| + process, self_ldr_data_table_entry.FullDllName, &self_module)) { |
| + return false; |
| + } |
| + process_info->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. |
| + T last = peb_ldr_data.InInitializationOrderModuleList.Blink; |
| + LDR_DATA_TABLE_ENTRY_T<T> ldr_data_table_entry; |
| + for (T 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(LDR_DATA_TABLE_ENTRY_T<T>, |
| + 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; |
| + 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 +202,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 +210,62 @@ bool ProcessInfo::Initialize(HANDLE process) { |
| #endif |
| ULONG bytes_returned; |
| + // We assume this process is not running on Wow64. The |
|
Mark Mentovai
2015/03/09 16:11:46
I don’t think that this assumption can really hold
scottmg
2015/03/09 21:16:36
OK. I would prefer not to support running as Wow64
|
| + // PROCESS_BASIC_INFORMATION uses the OS size (that is, even Wow64 has a 64 |
| + // bit one.) |
| +#if ARCH_CPU_32_BITS |
| + PROCESS_BASIC_INFORMATION_T<DWORD> process_basic_information; |
| +#else |
| + PROCESS_BASIC_INFORMATION_T<DWORD64> 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)) { |
| + // Read the PEB data using the correct word size. |
| + bool result = is_64_bit_ |
| + ? ReadProcessData<DWORD64>(process, peb_address, this) |
| + : ReadProcessData<DWORD>(process, peb_address, this); |
| + if (!result) |
| return false; |
| - } |
| - if (!ReadUnicodeString( |
| - process, self_ldr_data_table_entry.FullDllName, &self_module)) { |
| - 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 +283,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 { |