OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Crashpad Authors. All rights reserved. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (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 | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 | |
15 #include "util/win/process_info.h" | |
16 | |
17 namespace crashpad { | |
18 | |
19 namespace { | |
20 | |
21 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.
| |
22 std::wstring str; | |
23 if (us.Length > 0) { | |
24 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.
| |
25 SIZE_T bytes_read; | |
26 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
| |
27 process, us.Buffer, &str[0], us.Length, &bytes_read) || | |
28 bytes_read != us.Length) { | |
29 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.
| |
30 return std::wstring(); | |
31 } | |
32 } | |
33 return str; | |
34 } | |
35 | |
36 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
| |
37 SIZE_T bytes_read; | |
38 if (!ReadProcessMemory(process, at, into, sizeof(T), &bytes_read) || | |
39 bytes_read != sizeof(T)) { | |
40 // We don't have a name for the type we're reading, so include the signature | |
41 // to get the type of T. | |
42 PLOG(ERROR) << "ReadProcessMemory: " << __FUNCSIG__; | |
43 return false; | |
44 } | |
45 return true; | |
46 } | |
47 | |
48 // This is similar to PEB_LDR_DATA in winternl.h, but includes the | |
49 // InInitializationOrderModuleList field. | |
50 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.
| |
51 ULONG Length; | |
52 BOOLEAN Initialized; | |
53 PVOID Reserved; | |
54 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 (
| |
55 LIST_ENTRY InMemoryOrderModuleList; | |
56 LIST_ENTRY InInitializationOrderModuleList; | |
57 }; | |
58 | |
59 } // namespace | |
60 | |
61 ProcessInfo::ProcessInfo() | |
62 : process_basic_information_(), | |
63 command_line_(), | |
64 is64bit_(false), | |
65 iswow64_(FALSE) { | |
66 } | |
67 | |
68 ProcessInfo::~ProcessInfo() { | |
69 } | |
70 | |
71 bool ProcessInfo::Initialize(HANDLE process) { | |
72 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); | |
73 | |
74 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.
| |
75 reinterpret_cast<decltype(NtQueryInformationProcess)*>(GetProcAddress( | |
76 LoadLibrary(L"ntdll.dll"), "NtQueryInformationProcess")); | |
77 if (!nt_query_information_process) { | |
78 PLOG(ERROR) << "GetProcAddress NtQueryInformationProcess failed"; | |
79 return false; | |
80 } | |
81 | |
82 ULONG bytes_returned; | |
83 NTSTATUS status = | |
84 nt_query_information_process(process, | |
85 ProcessBasicInformation, | |
86 &process_basic_information_, | |
87 sizeof(process_basic_information_), | |
88 &bytes_returned); | |
89 if (status < 0 || bytes_returned != sizeof(process_basic_information_)) { | |
90 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.
| |
91 return false; | |
92 } | |
93 | |
94 // Try to read the process environment block. | |
95 PEB peb; | |
96 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
| |
97 return false; | |
98 | |
99 RTL_USER_PROCESS_PARAMETERS process_parameters; | |
100 if (!ReadStruct(process, peb.ProcessParameters, &process_parameters)) | |
101 return false; | |
102 | |
103 command_line_ = | |
104 ReadUnicodeString(process, process_parameters.CommandLine); | |
105 if (command_line_.empty()) | |
106 return false; | |
107 | |
108 FULL_PEB_LDR_DATA peb_ldr_data; | |
109 if (!ReadStruct(process, peb.Ldr, &peb_ldr_data)) | |
110 return false; | |
111 | |
112 // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded | |
113 // modules. We awkwardly use the Reserved fields of LDR_DATA_TABLE_ENTRY to | |
114 // get the modules in InitializationOrder rather than MemoryOrder. | |
115 LIST_ENTRY* cur = peb_ldr_data.InInitializationOrderModuleList.Flink; | |
116 LIST_ENTRY* last = peb_ldr_data.InInitializationOrderModuleList.Blink; | |
117 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
| |
118 // |cur| is the pointer to the LIST_ENTRY embedded in the | |
119 // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need | |
120 // to read from the target, and also offset back to the beginning of the | |
121 // structure. | |
122 LDR_DATA_TABLE_ENTRY ldr_data_table_entry; | |
123 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. :)
| |
124 reinterpret_cast<char*>(cur) - | |
125 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.
| |
126 &ldr_data_table_entry)) | |
127 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.
| |
128 // TODO(scottmg): Capture TimeDateStamp, Checksum, etc. too? | |
129 std::wstring module = | |
130 ReadUnicodeString(process, ldr_data_table_entry.FullDllName); | |
131 if (module.empty()) | |
132 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
| |
133 modules_.push_back(module); | |
134 if (cur == last) | |
135 break; | |
136 cur = reinterpret_cast<LIST_ENTRY*>(&ldr_data_table_entry.Reserved2)->Flink; | |
137 } | |
138 | |
139 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.
| |
140 reinterpret_cast<decltype(IsWow64Process)*>( | |
141 GetProcAddress(LoadLibrary(L"kernel32.dll"), "IsWow64Process")); | |
142 if (!is_wow64_process) { | |
143 // This means kernel32 doesn't implement this function, so there's no such | |
144 // thing as WoW64 on this OS. | |
145 iswow64_ = FALSE; | |
146 } else if (!is_wow64_process(process, &iswow64_)) { | |
147 PLOG(ERROR) << "IsWow64Process"; | |
148 return false; | |
149 } | |
150 | |
151 if (iswow64_) { | |
152 // If it's WoW64, then it's 32-on-64. | |
153 is64bit_ = false; | |
154 } else { | |
155 // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to | |
156 // distinguish between these two cases. | |
157 SYSTEM_INFO system_info; | |
158 GetSystemInfo(&system_info); | |
159 is64bit_ = | |
160 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; | |
161 } | |
162 | |
163 INITIALIZATION_STATE_SET_VALID(initialized_); | |
164 return true; | |
165 } | |
166 | |
167 bool ProcessInfo::Is64Bit() const { | |
168 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
169 return is64bit_; | |
170 } | |
171 | |
172 bool ProcessInfo::IsWow64() const { | |
173 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
174 return iswow64_; | |
175 } | |
176 | |
177 pid_t ProcessInfo::ProcessID() const { | |
178 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
179 return process_basic_information_.UniqueProcessId; | |
180 } | |
181 | |
182 pid_t ProcessInfo::ParentProcessID() const { | |
183 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
184 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.
| |
185 } | |
186 | |
187 bool ProcessInfo::CommandLine(std::wstring* command_line) const { | |
188 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
189 *command_line = command_line_; | |
190 return true; | |
191 } | |
192 | |
193 bool ProcessInfo::Modules(std::vector<std::wstring>* modules) const { | |
194 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
195 *modules = modules_; | |
196 return true; | |
197 } | |
198 | |
199 } // namespace crashpad | |
OLD | NEW |