| Index: util/win/process_info.cc
|
| diff --git a/util/win/process_info.cc b/util/win/process_info.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..66157aa482c1efccecb849014a3444851c161cdf
|
| --- /dev/null
|
| +++ b/util/win/process_info.cc
|
| @@ -0,0 +1,287 @@
|
| +// Copyright 2015 The Crashpad Authors. All rights reserved.
|
| +//
|
| +// Licensed under the Apache License, Version 2.0 (the "License");
|
| +// you may not use this file except in compliance with the License.
|
| +// You may obtain a copy of the License at
|
| +//
|
| +// http://www.apache.org/licenses/LICENSE-2.0
|
| +//
|
| +// Unless required by applicable law or agreed to in writing, software
|
| +// distributed under the License is distributed on an "AS IS" BASIS,
|
| +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +// See the License for the specific language governing permissions and
|
| +// limitations under the License.
|
| +
|
| +#include "util/win/process_info.h"
|
| +
|
| +#include "base/logging.h"
|
| +
|
| +namespace crashpad {
|
| +
|
| +namespace {
|
| +
|
| +NTSTATUS NtQueryInformationProcess(HANDLE process_handle,
|
| + PROCESSINFOCLASS process_information_class,
|
| + PVOID process_information,
|
| + ULONG process_information_length,
|
| + PULONG return_length) {
|
| + static decltype(::NtQueryInformationProcess)* nt_query_information_process =
|
| + reinterpret_cast<decltype(::NtQueryInformationProcess)*>(GetProcAddress(
|
| + LoadLibrary(L"ntdll.dll"), "NtQueryInformationProcess"));
|
| + DCHECK(nt_query_information_process);
|
| + return nt_query_information_process(process_handle,
|
| + process_information_class,
|
| + process_information,
|
| + process_information_length,
|
| + return_length);
|
| +}
|
| +
|
| +bool IsProcessWow64(HANDLE process_handle) {
|
| + static decltype(IsWow64Process)* is_wow64_process =
|
| + reinterpret_cast<decltype(IsWow64Process)*>(
|
| + GetProcAddress(LoadLibrary(L"kernel32.dll"), "IsWow64Process"));
|
| + if (!is_wow64_process)
|
| + return false;
|
| + BOOL is_wow64;
|
| + if (!is_wow64_process(process_handle, &is_wow64)) {
|
| + PLOG(ERROR) << "IsWow64Process";
|
| + return false;
|
| + }
|
| + return is_wow64;
|
| +}
|
| +
|
| +bool ReadUnicodeString(HANDLE process,
|
| + const UNICODE_STRING& us,
|
| + std::wstring* result) {
|
| + if (us.Length == 0) {
|
| + result->clear();
|
| + return true;
|
| + }
|
| + 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)) {
|
| + PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING";
|
| + return false;
|
| + }
|
| + if (bytes_read != us.Length) {
|
| + LOG(ERROR) << "ReadProcessMemory UNICODE_STRING incorrect size";
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +template <class T> bool ReadStruct(HANDLE process, uintptr_t at, T* into) {
|
| + SIZE_T bytes_read;
|
| + if (!ReadProcessMemory(process,
|
| + reinterpret_cast<const void*>(at),
|
| + into,
|
| + sizeof(T),
|
| + &bytes_read)) {
|
| + // We don't have a name for the type we're reading, so include the signature
|
| + // to get the type of T.
|
| + PLOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__;
|
| + return false;
|
| + }
|
| + if (bytes_read != sizeof(T)) {
|
| + LOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__ << " incorrect size";
|
| + return false;
|
| + }
|
| + 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
|
| +
|
| +ProcessInfo::ProcessInfo()
|
| + : process_basic_information_(),
|
| + command_line_(),
|
| + modules_(),
|
| + is_64_bit_(false),
|
| + is_wow64_(false),
|
| + initialized_() {
|
| +}
|
| +
|
| +ProcessInfo::~ProcessInfo() {
|
| +}
|
| +
|
| +bool ProcessInfo::Initialize(HANDLE process) {
|
| + INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
| +
|
| + is_wow64_ = IsProcessWow64(process);
|
| +
|
| + if (is_wow64_) {
|
| + // If it's WoW64, then it's 32-on-64.
|
| + is_64_bit_ = false;
|
| + } else {
|
| + // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to
|
| + // distinguish between these two cases.
|
| + SYSTEM_INFO system_info;
|
| + GetSystemInfo(&system_info);
|
| + is_64_bit_ =
|
| + 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 (is_64_bit_) {
|
| + LOG(ERROR) << "Reading x64 process from x86 process not supported";
|
| + return false;
|
| + }
|
| +#endif
|
| +
|
| + ULONG bytes_returned;
|
| + NTSTATUS status =
|
| + crashpad::NtQueryInformationProcess(process,
|
| + ProcessBasicInformation,
|
| + &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_)) {
|
| + 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;
|
| + }
|
| +
|
| + 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)) {
|
| + 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;
|
| +}
|
| +
|
| +bool ProcessInfo::Is64Bit() const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + return is_64_bit_;
|
| +}
|
| +
|
| +bool ProcessInfo::IsWow64() const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + return is_wow64_;
|
| +}
|
| +
|
| +pid_t ProcessInfo::ProcessID() const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + return process_basic_information_.UniqueProcessId;
|
| +}
|
| +
|
| +pid_t ProcessInfo::ParentProcessID() const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + return process_basic_information_.InheritedFromUniqueProcessId;
|
| +}
|
| +
|
| +bool ProcessInfo::CommandLine(std::wstring* command_line) const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + *command_line = command_line_;
|
| + return true;
|
| +}
|
| +
|
| +bool ProcessInfo::Modules(std::vector<std::wstring>* modules) const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + *modules = modules_;
|
| + return true;
|
| +}
|
| +
|
| +} // namespace crashpad
|
|
|