Chromium Code Reviews| 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..78c00db7d7465172481a2138827ba15d903efbf1 |
| --- /dev/null |
| +++ b/util/win/process_info.cc |
| @@ -0,0 +1,199 @@ |
| +// 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" |
| + |
| +namespace crashpad { |
| + |
| +namespace { |
| + |
| +std::wstring ReadUnicodeString(HANDLE process, const UNICODE_STRING& us) { |
|
Mark Mentovai
2015/03/05 17:32:22
In returning, this should have a way to distinguis
scottmg
2015/03/05 20:31:06
Done.
|
| + std::wstring str; |
| + if (us.Length > 0) { |
| + str.resize(us.Length / sizeof(wchar_t)); |
|
Mark Mentovai
2015/03/05 17:32:22
DCHECK_EQ(us.Length % sizeof(wchar_t), 0)
scottmg
2015/03/05 20:31:06
Done.
|
| + SIZE_T bytes_read; |
| + if (!ReadProcessMemory( |
|
Mark Mentovai
2015/03/05 17:32:22
If I’m reading this correctly, for cross-bitted op
scottmg
2015/03/05 20:31:06
I haven't tested it yet, but yes, I believe that's
|
| + process, us.Buffer, &str[0], us.Length, &bytes_read) || |
| + bytes_read != us.Length) { |
| + PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING"; |
|
Mark Mentovai
2015/03/05 17:32:22
#include "base/logging.h". Also, PLOG is incorrect
scottmg
2015/03/05 20:31:06
Done.
|
| + return std::wstring(); |
| + } |
| + } |
| + return str; |
| +} |
| + |
| +template <class T> bool ReadStruct(HANDLE process, void* at, T* into) { |
|
Mark Mentovai
2015/03/05 17:32:22
const void* at, but
Using pointer types to refer
scottmg
2015/03/05 20:31:06
Switched to uintptr_t. ReadProcessMemory just uses
|
| + SIZE_T bytes_read; |
| + if (!ReadProcessMemory(process, at, into, sizeof(T), &bytes_read) || |
| + bytes_read != sizeof(T)) { |
| + // 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; |
| + } |
| + return true; |
| +} |
| + |
| +// This is similar to PEB_LDR_DATA in winternl.h, but includes the |
| +// InInitializationOrderModuleList field. |
| +struct FULL_PEB_LDR_DATA { |
|
Mark Mentovai
2015/03/05 17:32:22
Since you don’t need Length or Initialized, you ca
scottmg
2015/03/05 20:31:06
Done.
|
| + ULONG Length; |
| + BOOLEAN Initialized; |
| + PVOID Reserved; |
| + LIST_ENTRY InLoadOrderModuleList; |
|
Mark Mentovai
2015/03/05 17:32:22
What’s the difference between load order and initi
scottmg
2015/03/05 20:31:07
Load and Memory order seem to always be the same (
|
| + LIST_ENTRY InMemoryOrderModuleList; |
| + LIST_ENTRY InInitializationOrderModuleList; |
| +}; |
| + |
| +} // namespace |
| + |
| +ProcessInfo::ProcessInfo() |
| + : process_basic_information_(), |
| + command_line_(), |
| + is64bit_(false), |
| + iswow64_(FALSE) { |
| +} |
| + |
| +ProcessInfo::~ProcessInfo() { |
| +} |
| + |
| +bool ProcessInfo::Initialize(HANDLE process) { |
| + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
| + |
| + decltype(NtQueryInformationProcess)* nt_query_information_process = |
|
Mark Mentovai
2015/03/05 17:32:22
I like to wrap these kinds of things in their own
scottmg
2015/03/05 20:31:06
Done.
|
| + reinterpret_cast<decltype(NtQueryInformationProcess)*>(GetProcAddress( |
| + LoadLibrary(L"ntdll.dll"), "NtQueryInformationProcess")); |
| + if (!nt_query_information_process) { |
| + PLOG(ERROR) << "GetProcAddress NtQueryInformationProcess failed"; |
| + return false; |
| + } |
| + |
| + ULONG bytes_returned; |
| + NTSTATUS status = |
| + nt_query_information_process(process, |
| + ProcessBasicInformation, |
| + &process_basic_information_, |
| + sizeof(process_basic_information_), |
| + &bytes_returned); |
| + if (status < 0 || bytes_returned != sizeof(process_basic_information_)) { |
| + LOG(ERROR) << "NtQueryInformationProcess"; |
|
Mark Mentovai
2015/03/05 17:32:22
If status < 0, the value of status might be a good
scottmg
2015/03/05 20:31:07
Done.
|
| + return false; |
| + } |
| + |
| + // Try to read the process environment block. |
| + PEB peb; |
| + if (!ReadStruct(process, process_basic_information_.PebBaseAddress, &peb)) |
|
Mark Mentovai
2015/03/05 17:32:22
This looks like the first point where cross-bitted
scottmg
2015/03/05 20:31:06
Right, good point.
For now I've moved the 32/64 c
|
| + return false; |
| + |
| + RTL_USER_PROCESS_PARAMETERS process_parameters; |
| + if (!ReadStruct(process, peb.ProcessParameters, &process_parameters)) |
| + return false; |
| + |
| + command_line_ = |
| + ReadUnicodeString(process, process_parameters.CommandLine); |
| + if (command_line_.empty()) |
| + return false; |
| + |
| + FULL_PEB_LDR_DATA peb_ldr_data; |
| + if (!ReadStruct(process, peb.Ldr, &peb_ldr_data)) |
| + return false; |
| + |
| + // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded |
| + // modules. We awkwardly use the Reserved fields of LDR_DATA_TABLE_ENTRY to |
| + // get the modules in InitializationOrder rather than MemoryOrder. |
| + LIST_ENTRY* cur = peb_ldr_data.InInitializationOrderModuleList.Flink; |
| + LIST_ENTRY* last = peb_ldr_data.InInitializationOrderModuleList.Blink; |
| + for (;;) { |
|
Mark Mentovai
2015/03/05 17:32:21
You could hoist some stuff into the for construct
scottmg
2015/03/05 20:31:06
Some hoisted. Unfortunately, it's a "back" pointer
|
| + // |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. |
| + LDR_DATA_TABLE_ENTRY ldr_data_table_entry; |
| + if (!ReadStruct(process, |
|
Mark Mentovai
2015/03/05 17:32:22
Your CL description had me geared up for a truly h
scottmg
2015/03/05 20:31:07
All in the eye of the beholder, I suppose. :)
|
| + reinterpret_cast<char*>(cur) - |
| + offsetof(LDR_DATA_TABLE_ENTRY, Reserved2), |
|
Mark Mentovai
2015/03/05 17:32:22
I see.
Presumably, LDR_DATA_TABLE_ENTRY really st
scottmg
2015/03/05 20:31:07
That's right.
|
| + &ldr_data_table_entry)) |
| + return false; |
|
Mark Mentovai
2015/03/05 17:32:22
{} around this, its controlling condition is so lo
scottmg
2015/03/05 20:31:06
Done.
|
| + // TODO(scottmg): Capture TimeDateStamp, Checksum, etc. too? |
| + std::wstring module = |
| + ReadUnicodeString(process, ldr_data_table_entry.FullDllName); |
| + if (module.empty()) |
| + return false; |
|
Mark Mentovai
2015/03/05 17:32:22
A bad module should probably invalidate that modul
scottmg
2015/03/05 20:31:06
Done. I was concerned that a bad PEB meant the wor
|
| + modules_.push_back(module); |
| + if (cur == last) |
| + break; |
| + cur = reinterpret_cast<LIST_ENTRY*>(&ldr_data_table_entry.Reserved2)->Flink; |
| + } |
| + |
| + decltype(IsWow64Process)* is_wow64_process = |
|
Mark Mentovai
2015/03/05 17:32:22
If you break this one into its own function, you c
scottmg
2015/03/05 20:31:07
Done.
|
| + reinterpret_cast<decltype(IsWow64Process)*>( |
| + GetProcAddress(LoadLibrary(L"kernel32.dll"), "IsWow64Process")); |
| + if (!is_wow64_process) { |
| + // This means kernel32 doesn't implement this function, so there's no such |
| + // thing as WoW64 on this OS. |
| + iswow64_ = FALSE; |
| + } else if (!is_wow64_process(process, &iswow64_)) { |
| + PLOG(ERROR) << "IsWow64Process"; |
| + return false; |
| + } |
| + |
| + if (iswow64_) { |
| + // If it's WoW64, then it's 32-on-64. |
| + is64bit_ = 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); |
| + is64bit_ = |
| + system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; |
| + } |
| + |
| + INITIALIZATION_STATE_SET_VALID(initialized_); |
| + return true; |
| +} |
| + |
| +bool ProcessInfo::Is64Bit() const { |
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| + return is64bit_; |
| +} |
| + |
| +bool ProcessInfo::IsWow64() const { |
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| + return iswow64_; |
| +} |
| + |
| +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 reinterpret_cast<ULONG_PTR>(process_basic_information_.Reserved3); |
|
Mark Mentovai
2015/03/05 17:32:22
Can process_basic_information_ be a union type tha
scottmg
2015/03/05 20:31:06
Added FULL_... for this type too.
|
| +} |
| + |
| +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 |