OLD | NEW |
---|---|
1 // Copyright 2015 The Crashpad Authors. All rights reserved. | 1 // Copyright 2015 The Crashpad Authors. All rights reserved. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with 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 | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
13 // limitations under the License. | 13 // limitations under the License. |
14 | 14 |
15 #include "util/win/process_info.h" | 15 #include "util/win/process_info.h" |
16 | 16 |
17 #include <winternl.h> | |
18 | |
17 #include "base/logging.h" | 19 #include "base/logging.h" |
20 #include "util/numeric/safe_assignment.h" | |
21 #include "util/win/process_structs.h" | |
18 | 22 |
19 namespace crashpad { | 23 namespace crashpad { |
20 | 24 |
21 namespace { | 25 namespace { |
22 | 26 |
23 NTSTATUS NtQueryInformationProcess(HANDLE process_handle, | 27 NTSTATUS NtQueryInformationProcess(HANDLE process_handle, |
24 PROCESSINFOCLASS process_information_class, | 28 PROCESSINFOCLASS process_information_class, |
25 PVOID process_information, | 29 PVOID process_information, |
26 ULONG process_information_length, | 30 ULONG process_information_length, |
27 PULONG return_length) { | 31 PULONG return_length) { |
(...skipping 15 matching lines...) Expand all Loading... | |
43 if (!is_wow64_process) | 47 if (!is_wow64_process) |
44 return false; | 48 return false; |
45 BOOL is_wow64; | 49 BOOL is_wow64; |
46 if (!is_wow64_process(process_handle, &is_wow64)) { | 50 if (!is_wow64_process(process_handle, &is_wow64)) { |
47 PLOG(ERROR) << "IsWow64Process"; | 51 PLOG(ERROR) << "IsWow64Process"; |
48 return false; | 52 return false; |
49 } | 53 } |
50 return is_wow64; | 54 return is_wow64; |
51 } | 55 } |
52 | 56 |
57 template <class T> | |
53 bool ReadUnicodeString(HANDLE process, | 58 bool ReadUnicodeString(HANDLE process, |
54 const UNICODE_STRING& us, | 59 const process_types::UNICODE_STRING<T>& us, |
55 std::wstring* result) { | 60 std::wstring* result) { |
56 if (us.Length == 0) { | 61 if (us.Length == 0) { |
57 result->clear(); | 62 result->clear(); |
58 return true; | 63 return true; |
59 } | 64 } |
60 DCHECK_EQ(us.Length % sizeof(wchar_t), 0u); | 65 DCHECK_EQ(us.Length % sizeof(wchar_t), 0u); |
61 result->resize(us.Length / sizeof(wchar_t)); | 66 result->resize(us.Length / sizeof(wchar_t)); |
62 SIZE_T bytes_read; | 67 SIZE_T bytes_read; |
63 if (!ReadProcessMemory( | 68 if (!ReadProcessMemory(process, |
64 process, us.Buffer, &result->operator[](0), us.Length, &bytes_read)) { | 69 reinterpret_cast<const void*>(us.Buffer), |
70 &result->operator[](0), | |
71 us.Length, | |
72 &bytes_read)) { | |
65 PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING"; | 73 PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING"; |
66 return false; | 74 return false; |
67 } | 75 } |
68 if (bytes_read != us.Length) { | 76 if (bytes_read != us.Length) { |
69 LOG(ERROR) << "ReadProcessMemory UNICODE_STRING incorrect size"; | 77 LOG(ERROR) << "ReadProcessMemory UNICODE_STRING incorrect size"; |
70 return false; | 78 return false; |
71 } | 79 } |
72 return true; | 80 return true; |
73 } | 81 } |
74 | 82 |
75 template <class T> bool ReadStruct(HANDLE process, uintptr_t at, T* into) { | 83 template <class T> bool ReadStruct(HANDLE process, uintptr_t at, T* into) { |
76 SIZE_T bytes_read; | 84 SIZE_T bytes_read; |
77 if (!ReadProcessMemory(process, | 85 if (!ReadProcessMemory(process, |
78 reinterpret_cast<const void*>(at), | 86 reinterpret_cast<const void*>(at), |
79 into, | 87 into, |
80 sizeof(T), | 88 sizeof(T), |
81 &bytes_read)) { | 89 &bytes_read)) { |
82 // We don't have a name for the type we're reading, so include the signature | 90 // We don't have a name for the type we're reading, so include the signature |
83 // to get the type of T. | 91 // to get the type of T. |
84 PLOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__; | 92 PLOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__; |
85 return false; | 93 return false; |
86 } | 94 } |
87 if (bytes_read != sizeof(T)) { | 95 if (bytes_read != sizeof(T)) { |
88 LOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__ << " incorrect size"; | 96 LOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__ << " incorrect size"; |
89 return false; | 97 return false; |
90 } | 98 } |
91 return true; | 99 return true; |
92 } | 100 } |
93 | 101 |
94 // PEB_LDR_DATA in winternl.h doesn't document the trailing | |
95 // InInitializationOrderModuleList field. See `dt ntdll!PEB_LDR_DATA`. | |
96 struct FULL_PEB_LDR_DATA : public PEB_LDR_DATA { | |
97 LIST_ENTRY InInitializationOrderModuleList; | |
98 }; | |
99 | |
100 // LDR_DATA_TABLE_ENTRY doesn't include InInitializationOrderLinks, define a | |
101 // complete version here. See `dt ntdll!_LDR_DATA_TABLE_ENTRY`. | |
102 struct FULL_LDR_DATA_TABLE_ENTRY { | |
103 LIST_ENTRY InLoadOrderLinks; | |
104 LIST_ENTRY InMemoryOrderLinks; | |
105 LIST_ENTRY InInitializationOrderLinks; | |
106 PVOID DllBase; | |
107 PVOID EntryPoint; | |
108 ULONG SizeOfImage; | |
109 UNICODE_STRING FullDllName; | |
110 UNICODE_STRING BaseDllName; | |
111 ULONG Flags; | |
112 WORD LoadCount; | |
113 WORD TlsIndex; | |
114 LIST_ENTRY HashLinks; | |
115 ULONG TimeDateStamp; | |
116 _ACTIVATION_CONTEXT* EntryPointActivationContext; | |
117 }; | |
118 | |
119 } // namespace | 102 } // namespace |
120 | 103 |
104 template <class Traits> | |
105 bool ReadProcessData(HANDLE process, | |
106 uintptr_t peb_address_uintptr, | |
107 ProcessInfo* process_info) { | |
108 Traits::Pointer peb_address; | |
109 if (!AssignIfInRange(&peb_address, peb_address_uintptr)) { | |
110 LOG(ERROR) << "peb_address_uintptr " << peb_address_uintptr | |
111 << " out of range"; | |
112 return false; | |
113 } | |
114 | |
115 // Try to read the process environment block. | |
116 process_types::PEB<Traits> peb; | |
117 if (!ReadStruct(process, peb_address, &peb)) | |
118 return false; | |
119 | |
120 process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters; | |
121 if (!ReadStruct(process, peb.ProcessParameters, &process_parameters)) | |
122 return false; | |
123 | |
124 if (!ReadUnicodeString(process, | |
125 process_parameters.CommandLine, | |
126 &process_info->command_line_)) { | |
127 return false; | |
128 } | |
129 | |
130 process_types::PEB_LDR_DATA<Traits> peb_ldr_data; | |
131 if (!ReadStruct(process, peb.Ldr, &peb_ldr_data)) | |
132 return false; | |
133 | |
134 // Include the first module in the memory order list to get our own name as | |
Mark Mentovai
2015/03/09 22:44:44
It’s not actually “our” name, it’s the other proce
scottmg
2015/03/09 23:23:57
Done.
| |
135 // it's not included in initialization order below. | |
136 std::wstring self_module; | |
Mark Mentovai
2015/03/09 22:44:44
Same concern with this variable’s name. executable
scottmg
2015/03/09 23:23:57
Done.
| |
137 process_types::LDR_DATA_TABLE_ENTRY<Traits> self_ldr_data_table_entry; | |
Mark Mentovai
2015/03/09 22:44:44
And this variable too.
I guess that for both, you
scottmg
2015/03/09 23:23:57
Done.
| |
138 if (!ReadStruct(process, | |
139 reinterpret_cast<uintptr_t>( | |
140 reinterpret_cast<const char*>( | |
141 peb_ldr_data.InMemoryOrderModuleList.Flink) - | |
142 offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>, | |
143 InMemoryOrderLinks)), | |
144 &self_ldr_data_table_entry)) { | |
145 return false; | |
146 } | |
147 if (!ReadUnicodeString( | |
148 process, self_ldr_data_table_entry.FullDllName, &self_module)) { | |
149 return false; | |
150 } | |
151 process_info->modules_.push_back(self_module); | |
152 | |
153 // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded | |
154 // modules. We use this method rather than EnumProcessModules to get the | |
155 // modules in initialization order rather than memory order. | |
156 Traits::Pointer last = peb_ldr_data.InInitializationOrderModuleList.Blink; | |
157 process_types::LDR_DATA_TABLE_ENTRY<Traits> ldr_data_table_entry; | |
158 for (Traits::Pointer cur = peb_ldr_data.InInitializationOrderModuleList.Flink; | |
159 ; | |
160 cur = ldr_data_table_entry.InInitializationOrderLinks.Flink) { | |
161 // |cur| is the pointer to the LIST_ENTRY embedded in the | |
162 // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need | |
163 // to read from the target, and also offset back to the beginning of the | |
164 // structure. | |
165 if (!ReadStruct(process, | |
166 reinterpret_cast<uintptr_t>( | |
167 reinterpret_cast<const char*>(cur) - | |
168 offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>, | |
169 InInitializationOrderLinks)), | |
170 &ldr_data_table_entry)) { | |
171 break; | |
172 } | |
173 // TODO(scottmg): Capture TimeDateStamp, Checksum, etc. too? | |
174 std::wstring module; | |
175 if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module)) | |
176 break; | |
177 process_info->modules_.push_back(module); | |
178 if (cur == last) | |
179 break; | |
180 } | |
181 | |
182 return true; | |
183 } | |
184 | |
121 ProcessInfo::ProcessInfo() | 185 ProcessInfo::ProcessInfo() |
122 : process_basic_information_(), | 186 : process_id_(), |
187 inherited_from_process_id_(), | |
123 command_line_(), | 188 command_line_(), |
124 modules_(), | 189 modules_(), |
125 is_64_bit_(false), | 190 is_64_bit_(false), |
126 is_wow64_(false), | 191 is_wow64_(false), |
127 initialized_() { | 192 initialized_() { |
128 } | 193 } |
129 | 194 |
130 ProcessInfo::~ProcessInfo() { | 195 ProcessInfo::~ProcessInfo() { |
131 } | 196 } |
132 | 197 |
133 bool ProcessInfo::Initialize(HANDLE process) { | 198 bool ProcessInfo::Initialize(HANDLE process) { |
134 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); | 199 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
135 | 200 |
136 is_wow64_ = IsProcessWow64(process); | 201 is_wow64_ = IsProcessWow64(process); |
137 | 202 |
138 if (is_wow64_) { | 203 if (is_wow64_) { |
139 // If it's WoW64, then it's 32-on-64. | 204 // If it's WoW64, then it's 32-on-64. |
140 is_64_bit_ = false; | 205 is_64_bit_ = false; |
141 } else { | 206 } else { |
142 // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to | 207 // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to |
143 // distinguish between these two cases. | 208 // distinguish between these two cases. |
144 SYSTEM_INFO system_info; | 209 SYSTEM_INFO system_info; |
145 GetSystemInfo(&system_info); | 210 GetSystemInfo(&system_info); |
146 is_64_bit_ = | 211 is_64_bit_ = |
147 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; | 212 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; |
148 } | 213 } |
149 | 214 |
150 #if ARCH_CPU_64_BITS | 215 #if ARCH_CPU_32_BITS |
151 if (!is_64_bit_) { | |
152 LOG(ERROR) << "Reading different bitness not yet supported"; | |
153 return false; | |
154 } | |
155 #else | |
156 if (is_64_bit_) { | 216 if (is_64_bit_) { |
157 LOG(ERROR) << "Reading x64 process from x86 process not supported"; | 217 LOG(ERROR) << "Reading x64 process from x86 process not supported"; |
158 return false; | 218 return false; |
159 } | 219 } |
160 #endif | 220 #endif |
161 | 221 |
162 ULONG bytes_returned; | 222 ULONG bytes_returned; |
223 // We assume this process is not running on Wow64. The | |
224 // PROCESS_BASIC_INFORMATION uses the OS size (that is, even Wow64 has a 64 | |
225 // bit one.) | |
226 // TODO(scottmg): Either support running as Wow64, or check/resolve this at a | |
227 // higher level. | |
228 #if ARCH_CPU_32_BITS | |
229 process_types::PROCESS_BASIC_INFORMATION<process_types::internal::Traits32> | |
230 process_basic_information; | |
231 #else | |
232 process_types::PROCESS_BASIC_INFORMATION<process_types::internal::Traits64> | |
233 process_basic_information; | |
234 #endif | |
163 NTSTATUS status = | 235 NTSTATUS status = |
164 crashpad::NtQueryInformationProcess(process, | 236 crashpad::NtQueryInformationProcess(process, |
165 ProcessBasicInformation, | 237 ProcessBasicInformation, |
166 &process_basic_information_, | 238 &process_basic_information, |
167 sizeof(process_basic_information_), | 239 sizeof(process_basic_information), |
168 &bytes_returned); | 240 &bytes_returned); |
169 if (status < 0) { | 241 if (status < 0) { |
170 LOG(ERROR) << "NtQueryInformationProcess: status=" << status; | 242 LOG(ERROR) << "NtQueryInformationProcess: status=" << status; |
171 return false; | 243 return false; |
172 } | 244 } |
173 if (bytes_returned != sizeof(process_basic_information_)) { | 245 if (bytes_returned != sizeof(process_basic_information)) { |
174 LOG(ERROR) << "NtQueryInformationProcess incorrect size"; | 246 LOG(ERROR) << "NtQueryInformationProcess incorrect size"; |
175 return false; | 247 return false; |
176 } | 248 } |
249 process_id_ = process_basic_information.UniqueProcessId; | |
250 inherited_from_process_id_ = | |
251 process_basic_information.InheritedFromUniqueProcessId; | |
177 | 252 |
178 // Try to read the process environment block. | 253 // We now want to read the PEB to gather the rest of our information. The |
179 PEB peb; | 254 // PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32, |
180 if (!ReadStruct(process, | 255 // but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both). |
181 reinterpret_cast<uintptr_t>( | 256 // The address of this is found by a second call to NtQueryInformationProcess. |
182 process_basic_information_.PebBaseAddress), | 257 uintptr_t peb_address = process_basic_information.PebBaseAddress; |
183 &peb)) { | 258 if (is_wow64_) { |
184 return false; | 259 ULONG_PTR wow64_peb_address; |
260 status = | |
261 crashpad::NtQueryInformationProcess(process, | |
262 ProcessWow64Information, | |
263 &wow64_peb_address, | |
264 sizeof(wow64_peb_address), | |
265 &bytes_returned); | |
266 if (status < 0) { | |
267 LOG(ERROR) << "NtQueryInformationProcess: status=" << status; | |
268 return false; | |
269 } | |
270 if (bytes_returned != sizeof(wow64_peb_address)) { | |
271 LOG(ERROR) << "NtQueryInformationProcess incorrect size"; | |
272 return false; | |
273 } | |
274 peb_address = wow64_peb_address; | |
185 } | 275 } |
186 | 276 |
187 RTL_USER_PROCESS_PARAMETERS process_parameters; | 277 // Read the PEB data using the correct word size. |
188 if (!ReadStruct(process, | 278 bool result = is_64_bit_ ? ReadProcessData<process_types::internal::Traits64>( |
189 reinterpret_cast<uintptr_t>(peb.ProcessParameters), | 279 process, peb_address, this) |
190 &process_parameters)) { | 280 : ReadProcessData<process_types::internal::Traits32>( |
281 process, peb_address, this); | |
282 if (!result) | |
191 return false; | 283 return false; |
192 } | |
193 | |
194 if (!ReadUnicodeString( | |
195 process, process_parameters.CommandLine, &command_line_)) { | |
196 return false; | |
197 } | |
198 | |
199 FULL_PEB_LDR_DATA peb_ldr_data; | |
200 if (!ReadStruct(process, reinterpret_cast<uintptr_t>(peb.Ldr), &peb_ldr_data)) | |
201 return false; | |
202 | |
203 // Include the first module in the memory order list to get our own name as | |
204 // it's not included in initialization order below. | |
205 std::wstring self_module; | |
206 FULL_LDR_DATA_TABLE_ENTRY self_ldr_data_table_entry; | |
207 if (!ReadStruct(process, | |
208 reinterpret_cast<uintptr_t>( | |
209 reinterpret_cast<const char*>( | |
210 peb_ldr_data.InMemoryOrderModuleList.Flink) - | |
211 offsetof(FULL_LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks)), | |
212 &self_ldr_data_table_entry)) { | |
213 return false; | |
214 } | |
215 if (!ReadUnicodeString( | |
216 process, self_ldr_data_table_entry.FullDllName, &self_module)) { | |
217 return false; | |
218 } | |
219 modules_.push_back(self_module); | |
220 | |
221 // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded | |
222 // modules. We use this method rather than EnumProcessModules to get the | |
223 // modules in initialization order rather than memory order. | |
224 const LIST_ENTRY* last = peb_ldr_data.InInitializationOrderModuleList.Blink; | |
225 FULL_LDR_DATA_TABLE_ENTRY ldr_data_table_entry; | |
226 for (const LIST_ENTRY* cur = | |
227 peb_ldr_data.InInitializationOrderModuleList.Flink; | |
228 ; | |
229 cur = ldr_data_table_entry.InInitializationOrderLinks.Flink) { | |
230 // |cur| is the pointer to the LIST_ENTRY embedded in the | |
231 // FULL_LDR_DATA_TABLE_ENTRY, in the target process's address space. So we | |
232 // need to read from the target, and also offset back to the beginning of | |
233 // the structure. | |
234 if (!ReadStruct( | |
235 process, | |
236 reinterpret_cast<uintptr_t>(reinterpret_cast<const char*>(cur) - | |
237 offsetof(FULL_LDR_DATA_TABLE_ENTRY, | |
238 InInitializationOrderLinks)), | |
239 &ldr_data_table_entry)) { | |
240 break; | |
241 } | |
242 // TODO(scottmg): Capture TimeDateStamp, Checksum, etc. too? | |
243 std::wstring module; | |
244 if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module)) | |
245 break; | |
246 modules_.push_back(module); | |
247 if (cur == last) | |
248 break; | |
249 } | |
250 | 284 |
251 INITIALIZATION_STATE_SET_VALID(initialized_); | 285 INITIALIZATION_STATE_SET_VALID(initialized_); |
252 return true; | 286 return true; |
253 } | 287 } |
254 | 288 |
255 bool ProcessInfo::Is64Bit() const { | 289 bool ProcessInfo::Is64Bit() const { |
256 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 290 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
257 return is_64_bit_; | 291 return is_64_bit_; |
258 } | 292 } |
259 | 293 |
260 bool ProcessInfo::IsWow64() const { | 294 bool ProcessInfo::IsWow64() const { |
261 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 295 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
262 return is_wow64_; | 296 return is_wow64_; |
263 } | 297 } |
264 | 298 |
265 pid_t ProcessInfo::ProcessID() const { | 299 pid_t ProcessInfo::ProcessID() const { |
266 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 300 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
267 return process_basic_information_.UniqueProcessId; | 301 return process_id_; |
268 } | 302 } |
269 | 303 |
270 pid_t ProcessInfo::ParentProcessID() const { | 304 pid_t ProcessInfo::ParentProcessID() const { |
271 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 305 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
272 return process_basic_information_.InheritedFromUniqueProcessId; | 306 return inherited_from_process_id_; |
273 } | 307 } |
274 | 308 |
275 bool ProcessInfo::CommandLine(std::wstring* command_line) const { | 309 bool ProcessInfo::CommandLine(std::wstring* command_line) const { |
276 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 310 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
277 *command_line = command_line_; | 311 *command_line = command_line_; |
278 return true; | 312 return true; |
279 } | 313 } |
280 | 314 |
281 bool ProcessInfo::Modules(std::vector<std::wstring>* modules) const { | 315 bool ProcessInfo::Modules(std::vector<std::wstring>* modules) const { |
282 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 316 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
283 *modules = modules_; | 317 *modules = modules_; |
284 return true; | 318 return true; |
285 } | 319 } |
286 | 320 |
287 } // namespace crashpad | 321 } // namespace crashpad |
OLD | NEW |