Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(619)

Side by Side Diff: util/win/process_info.cc

Issue 981393003: win: Support reading process info cross-bitness (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@64-port-test-2
Patch Set: Review feedback Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698