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> | 17 #include <winternl.h> |
18 | 18 |
19 #include "base/logging.h" | 19 #include "base/logging.h" |
| 20 #include "base/strings/stringprintf.h" |
| 21 #include "build/build_config.h" |
20 #include "util/numeric/safe_assignment.h" | 22 #include "util/numeric/safe_assignment.h" |
| 23 #include "util/win/ntstatus_logging.h" |
21 #include "util/win/process_structs.h" | 24 #include "util/win/process_structs.h" |
22 | 25 |
23 namespace crashpad { | 26 namespace crashpad { |
24 | 27 |
25 namespace { | 28 namespace { |
26 | 29 |
27 NTSTATUS NtQueryInformationProcess(HANDLE process_handle, | 30 NTSTATUS NtQueryInformationProcess(HANDLE process_handle, |
28 PROCESSINFOCLASS process_information_class, | 31 PROCESSINFOCLASS process_information_class, |
29 PVOID process_information, | 32 PVOID process_information, |
30 ULONG process_information_length, | 33 ULONG process_information_length, |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
73 PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING"; | 76 PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING"; |
74 return false; | 77 return false; |
75 } | 78 } |
76 if (bytes_read != us.Length) { | 79 if (bytes_read != us.Length) { |
77 LOG(ERROR) << "ReadProcessMemory UNICODE_STRING incorrect size"; | 80 LOG(ERROR) << "ReadProcessMemory UNICODE_STRING incorrect size"; |
78 return false; | 81 return false; |
79 } | 82 } |
80 return true; | 83 return true; |
81 } | 84 } |
82 | 85 |
83 template <class T> bool ReadStruct(HANDLE process, WinVMAddress at, T* into) { | 86 template <class T> |
| 87 bool ReadStruct(HANDLE process, WinVMAddress at, T* into) { |
84 SIZE_T bytes_read; | 88 SIZE_T bytes_read; |
85 if (!ReadProcessMemory(process, | 89 if (!ReadProcessMemory(process, |
86 reinterpret_cast<const void*>(at), | 90 reinterpret_cast<const void*>(at), |
87 into, | 91 into, |
88 sizeof(T), | 92 sizeof(T), |
89 &bytes_read)) { | 93 &bytes_read)) { |
90 // We don't have a name for the type we're reading, so include the signature | 94 // We don't have a name for the type we're reading, so include the signature |
91 // to get the type of T. | 95 // to get the type of T. |
92 PLOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__; | 96 PLOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__; |
93 return false; | 97 return false; |
94 } | 98 } |
95 if (bytes_read != sizeof(T)) { | 99 if (bytes_read != sizeof(T)) { |
96 LOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__ << " incorrect size"; | 100 LOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__ << " incorrect size"; |
97 return false; | 101 return false; |
98 } | 102 } |
99 return true; | 103 return true; |
100 } | 104 } |
101 | 105 |
102 } // namespace | 106 } // namespace |
103 | 107 |
104 template <class Traits> | 108 template <class Traits> |
| 109 bool GetProcessBasicInformation(HANDLE process, |
| 110 bool is_wow64, |
| 111 ProcessInfo* process_info, |
| 112 WinVMAddress* peb_address) { |
| 113 ULONG bytes_returned; |
| 114 process_types::PROCESS_BASIC_INFORMATION<Traits> process_basic_information; |
| 115 NTSTATUS status = |
| 116 crashpad::NtQueryInformationProcess(process, |
| 117 ProcessBasicInformation, |
| 118 &process_basic_information, |
| 119 sizeof(process_basic_information), |
| 120 &bytes_returned); |
| 121 if (!NT_SUCCESS(status)) { |
| 122 NTSTATUS_LOG(ERROR, status) << "NtQueryInformationProcess"; |
| 123 return false; |
| 124 } |
| 125 if (bytes_returned != sizeof(process_basic_information)) { |
| 126 LOG(ERROR) << "NtQueryInformationProcess incorrect size"; |
| 127 return false; |
| 128 } |
| 129 |
| 130 // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203 on |
| 131 // 32 bit being the correct size for HANDLEs for proceses, even on Windows |
| 132 // x64. API functions (e.g. OpenProcess) take only a DWORD, so there's no |
| 133 // sense in maintaining the top bits. |
| 134 process_info->process_id_ = |
| 135 static_cast<DWORD>(process_basic_information.UniqueProcessId); |
| 136 process_info->inherited_from_process_id_ = static_cast<DWORD>( |
| 137 process_basic_information.InheritedFromUniqueProcessId); |
| 138 |
| 139 // We now want to read the PEB to gather the rest of our information. The |
| 140 // PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32, |
| 141 // but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both). |
| 142 // The address of this is found by a second call to NtQueryInformationProcess. |
| 143 if (!is_wow64) { |
| 144 *peb_address = process_basic_information.PebBaseAddress; |
| 145 } else { |
| 146 ULONG_PTR wow64_peb_address; |
| 147 status = crashpad::NtQueryInformationProcess(process, |
| 148 ProcessWow64Information, |
| 149 &wow64_peb_address, |
| 150 sizeof(wow64_peb_address), |
| 151 &bytes_returned); |
| 152 if (!NT_SUCCESS(status)) { |
| 153 NTSTATUS_LOG(ERROR, status), "NtQueryInformationProcess"; |
| 154 return false; |
| 155 } |
| 156 if (bytes_returned != sizeof(wow64_peb_address)) { |
| 157 LOG(ERROR) << "NtQueryInformationProcess incorrect size"; |
| 158 return false; |
| 159 } |
| 160 *peb_address = wow64_peb_address; |
| 161 } |
| 162 |
| 163 return true; |
| 164 } |
| 165 |
| 166 template <class Traits> |
105 bool ReadProcessData(HANDLE process, | 167 bool ReadProcessData(HANDLE process, |
106 WinVMAddress peb_address_vmaddr, | 168 WinVMAddress peb_address_vmaddr, |
107 ProcessInfo* process_info) { | 169 ProcessInfo* process_info) { |
108 Traits::Pointer peb_address; | 170 Traits::Pointer peb_address; |
109 if (!AssignIfInRange(&peb_address, peb_address_vmaddr)) { | 171 if (!AssignIfInRange(&peb_address, peb_address_vmaddr)) { |
110 LOG(ERROR) << "peb_address_vmaddr " << peb_address_vmaddr | 172 LOG(ERROR) << base::StringPrintf("peb address 0x%x out of range", |
111 << " out of range"; | 173 peb_address_vmaddr); |
112 return false; | 174 return false; |
113 } | 175 } |
114 | 176 |
115 // Try to read the process environment block. | 177 // Try to read the process environment block. |
116 process_types::PEB<Traits> peb; | 178 process_types::PEB<Traits> peb; |
117 if (!ReadStruct(process, peb_address, &peb)) | 179 if (!ReadStruct(process, peb_address, &peb)) |
118 return false; | 180 return false; |
119 | 181 |
120 process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters; | 182 process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters; |
121 if (!ReadStruct(process, peb.ProcessParameters, &process_parameters)) | 183 if (!ReadStruct(process, peb.ProcessParameters, &process_parameters)) |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
225 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; | 287 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; |
226 } | 288 } |
227 | 289 |
228 #if ARCH_CPU_32_BITS | 290 #if ARCH_CPU_32_BITS |
229 if (is_64_bit_) { | 291 if (is_64_bit_) { |
230 LOG(ERROR) << "Reading x64 process from x86 process not supported"; | 292 LOG(ERROR) << "Reading x64 process from x86 process not supported"; |
231 return false; | 293 return false; |
232 } | 294 } |
233 #endif | 295 #endif |
234 | 296 |
235 ULONG bytes_returned; | 297 WinVMAddress peb_address; |
236 // We assume this process is not running on Wow64. The | 298 #if ARCH_CPU_64_BITS |
237 // PROCESS_BASIC_INFORMATION uses the OS size (that is, even Wow64 has a 64 | 299 bool result = GetProcessBasicInformation<process_types::internal::Traits64>( |
238 // bit one.) | 300 process, is_wow64_, this, &peb_address); |
239 // TODO(scottmg): Either support running as Wow64, or check/resolve this at a | |
240 // higher level. | |
241 #if ARCH_CPU_32_BITS | |
242 process_types::PROCESS_BASIC_INFORMATION<process_types::internal::Traits32> | |
243 process_basic_information; | |
244 #else | 301 #else |
245 process_types::PROCESS_BASIC_INFORMATION<process_types::internal::Traits64> | 302 bool result = GetProcessBasicInformation<process_types::internal::Traits32>( |
246 process_basic_information; | 303 process, false, this, &peb_address); |
247 #endif | 304 #endif // ARCH_CPU_64_BITS |
248 NTSTATUS status = | 305 |
249 crashpad::NtQueryInformationProcess(process, | 306 if (!result) { |
250 ProcessBasicInformation, | 307 LOG(ERROR) << "GetProcessBasicInformation failed"; |
251 &process_basic_information, | |
252 sizeof(process_basic_information), | |
253 &bytes_returned); | |
254 if (status < 0) { | |
255 LOG(ERROR) << "NtQueryInformationProcess: status=" << status; | |
256 return false; | |
257 } | |
258 if (bytes_returned != sizeof(process_basic_information)) { | |
259 LOG(ERROR) << "NtQueryInformationProcess incorrect size"; | |
260 return false; | 308 return false; |
261 } | 309 } |
262 | 310 |
263 // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203 on | 311 result = is_64_bit_ ? ReadProcessData<process_types::internal::Traits64>( |
264 // 32 bit being the correct size for HANDLEs for proceses, even on Windows | |
265 // x64. API functions (e.g. OpenProcess) take only a DWORD, so there's no | |
266 // sense in maintaining the top bits. | |
267 process_id_ = static_cast<DWORD>(process_basic_information.UniqueProcessId); | |
268 inherited_from_process_id_ = static_cast<DWORD>( | |
269 process_basic_information.InheritedFromUniqueProcessId); | |
270 | |
271 // We now want to read the PEB to gather the rest of our information. The | |
272 // PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32, | |
273 // but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both). | |
274 // The address of this is found by a second call to NtQueryInformationProcess. | |
275 WinVMAddress peb_address = process_basic_information.PebBaseAddress; | |
276 if (is_wow64_) { | |
277 ULONG_PTR wow64_peb_address; | |
278 status = | |
279 crashpad::NtQueryInformationProcess(process, | |
280 ProcessWow64Information, | |
281 &wow64_peb_address, | |
282 sizeof(wow64_peb_address), | |
283 &bytes_returned); | |
284 if (status < 0) { | |
285 LOG(ERROR) << "NtQueryInformationProcess: status=" << status; | |
286 return false; | |
287 } | |
288 if (bytes_returned != sizeof(wow64_peb_address)) { | |
289 LOG(ERROR) << "NtQueryInformationProcess incorrect size"; | |
290 return false; | |
291 } | |
292 peb_address = wow64_peb_address; | |
293 } | |
294 | |
295 // Read the PEB data using the correct word size. | |
296 bool result = is_64_bit_ ? ReadProcessData<process_types::internal::Traits64>( | |
297 process, peb_address, this) | 312 process, peb_address, this) |
298 : ReadProcessData<process_types::internal::Traits32>( | 313 : ReadProcessData<process_types::internal::Traits32>( |
299 process, peb_address, this); | 314 process, peb_address, this); |
300 if (!result) | 315 if (!result) { |
| 316 LOG(ERROR) << "ReadProcessData failed"; |
301 return false; | 317 return false; |
| 318 } |
302 | 319 |
303 INITIALIZATION_STATE_SET_VALID(initialized_); | 320 INITIALIZATION_STATE_SET_VALID(initialized_); |
304 return true; | 321 return true; |
305 } | 322 } |
306 | 323 |
307 bool ProcessInfo::Is64Bit() const { | 324 bool ProcessInfo::Is64Bit() const { |
308 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 325 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
309 return is_64_bit_; | 326 return is_64_bit_; |
310 } | 327 } |
311 | 328 |
(...skipping 18 matching lines...) Expand all Loading... |
330 return true; | 347 return true; |
331 } | 348 } |
332 | 349 |
333 bool ProcessInfo::Modules(std::vector<Module>* modules) const { | 350 bool ProcessInfo::Modules(std::vector<Module>* modules) const { |
334 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 351 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
335 *modules = modules_; | 352 *modules = modules_; |
336 return true; | 353 return true; |
337 } | 354 } |
338 | 355 |
339 } // namespace crashpad | 356 } // namespace crashpad |
OLD | NEW |