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

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: rebase 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
« no previous file with comments | « util/win/process_info.h ('k') | util/win/process_info_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 std::wstring module;
135 process_types::LDR_DATA_TABLE_ENTRY<Traits> ldr_data_table_entry;
136
137 // Include the first module in the memory order list to get our the main
138 // executable's name, as it's not included in initialization order below.
139 if (!ReadStruct(process,
140 reinterpret_cast<uintptr_t>(
141 reinterpret_cast<const char*>(
142 peb_ldr_data.InMemoryOrderModuleList.Flink) -
143 offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
144 InMemoryOrderLinks)),
145 &ldr_data_table_entry)) {
146 return false;
147 }
148 if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module))
149 return false;
150 process_info->modules_.push_back(module);
151
152 // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded
153 // modules. We use this method rather than EnumProcessModules to get the
154 // modules in initialization order rather than memory order.
155 Traits::Pointer last = peb_ldr_data.InInitializationOrderModuleList.Blink;
156 for (Traits::Pointer cur = peb_ldr_data.InInitializationOrderModuleList.Flink;
157 ;
158 cur = ldr_data_table_entry.InInitializationOrderLinks.Flink) {
159 // |cur| is the pointer to the LIST_ENTRY embedded in the
160 // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need
161 // to read from the target, and also offset back to the beginning of the
162 // structure.
163 if (!ReadStruct(process,
164 reinterpret_cast<uintptr_t>(
165 reinterpret_cast<const char*>(cur) -
166 offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
167 InInitializationOrderLinks)),
168 &ldr_data_table_entry)) {
169 break;
170 }
171 // TODO(scottmg): Capture TimeDateStamp, Checksum, etc. too?
172 if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module))
173 break;
174 process_info->modules_.push_back(module);
175 if (cur == last)
176 break;
177 }
178
179 return true;
180 }
181
121 ProcessInfo::ProcessInfo() 182 ProcessInfo::ProcessInfo()
122 : process_basic_information_(), 183 : process_id_(),
184 inherited_from_process_id_(),
123 command_line_(), 185 command_line_(),
124 modules_(), 186 modules_(),
125 is_64_bit_(false), 187 is_64_bit_(false),
126 is_wow64_(false), 188 is_wow64_(false),
127 initialized_() { 189 initialized_() {
128 } 190 }
129 191
130 ProcessInfo::~ProcessInfo() { 192 ProcessInfo::~ProcessInfo() {
131 } 193 }
132 194
133 bool ProcessInfo::Initialize(HANDLE process) { 195 bool ProcessInfo::Initialize(HANDLE process) {
134 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); 196 INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
135 197
136 is_wow64_ = IsProcessWow64(process); 198 is_wow64_ = IsProcessWow64(process);
137 199
138 if (is_wow64_) { 200 if (is_wow64_) {
139 // If it's WoW64, then it's 32-on-64. 201 // If it's WoW64, then it's 32-on-64.
140 is_64_bit_ = false; 202 is_64_bit_ = false;
141 } else { 203 } else {
142 // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to 204 // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to
143 // distinguish between these two cases. 205 // distinguish between these two cases.
144 SYSTEM_INFO system_info; 206 SYSTEM_INFO system_info;
145 GetSystemInfo(&system_info); 207 GetSystemInfo(&system_info);
146 is_64_bit_ = 208 is_64_bit_ =
147 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; 209 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
148 } 210 }
149 211
150 #if ARCH_CPU_64_BITS 212 #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_) { 213 if (is_64_bit_) {
157 LOG(ERROR) << "Reading x64 process from x86 process not supported"; 214 LOG(ERROR) << "Reading x64 process from x86 process not supported";
158 return false; 215 return false;
159 } 216 }
160 #endif 217 #endif
161 218
162 ULONG bytes_returned; 219 ULONG bytes_returned;
220 // We assume this process is not running on Wow64. The
221 // PROCESS_BASIC_INFORMATION uses the OS size (that is, even Wow64 has a 64
222 // bit one.)
223 // TODO(scottmg): Either support running as Wow64, or check/resolve this at a
224 // higher level.
225 #if ARCH_CPU_32_BITS
226 process_types::PROCESS_BASIC_INFORMATION<process_types::internal::Traits32>
227 process_basic_information;
228 #else
229 process_types::PROCESS_BASIC_INFORMATION<process_types::internal::Traits64>
230 process_basic_information;
231 #endif
163 NTSTATUS status = 232 NTSTATUS status =
164 crashpad::NtQueryInformationProcess(process, 233 crashpad::NtQueryInformationProcess(process,
165 ProcessBasicInformation, 234 ProcessBasicInformation,
166 &process_basic_information_, 235 &process_basic_information,
167 sizeof(process_basic_information_), 236 sizeof(process_basic_information),
168 &bytes_returned); 237 &bytes_returned);
169 if (status < 0) { 238 if (status < 0) {
170 LOG(ERROR) << "NtQueryInformationProcess: status=" << status; 239 LOG(ERROR) << "NtQueryInformationProcess: status=" << status;
171 return false; 240 return false;
172 } 241 }
173 if (bytes_returned != sizeof(process_basic_information_)) { 242 if (bytes_returned != sizeof(process_basic_information)) {
174 LOG(ERROR) << "NtQueryInformationProcess incorrect size"; 243 LOG(ERROR) << "NtQueryInformationProcess incorrect size";
175 return false; 244 return false;
176 } 245 }
246 process_id_ = process_basic_information.UniqueProcessId;
247 inherited_from_process_id_ =
248 process_basic_information.InheritedFromUniqueProcessId;
177 249
178 // Try to read the process environment block. 250 // We now want to read the PEB to gather the rest of our information. The
179 PEB peb; 251 // PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32,
180 if (!ReadStruct(process, 252 // but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both).
181 reinterpret_cast<uintptr_t>( 253 // The address of this is found by a second call to NtQueryInformationProcess.
182 process_basic_information_.PebBaseAddress), 254 uintptr_t peb_address = process_basic_information.PebBaseAddress;
183 &peb)) { 255 if (is_wow64_) {
184 return false; 256 ULONG_PTR wow64_peb_address;
257 status =
258 crashpad::NtQueryInformationProcess(process,
259 ProcessWow64Information,
260 &wow64_peb_address,
261 sizeof(wow64_peb_address),
262 &bytes_returned);
263 if (status < 0) {
264 LOG(ERROR) << "NtQueryInformationProcess: status=" << status;
265 return false;
266 }
267 if (bytes_returned != sizeof(wow64_peb_address)) {
268 LOG(ERROR) << "NtQueryInformationProcess incorrect size";
269 return false;
270 }
271 peb_address = wow64_peb_address;
185 } 272 }
186 273
187 RTL_USER_PROCESS_PARAMETERS process_parameters; 274 // Read the PEB data using the correct word size.
188 if (!ReadStruct(process, 275 bool result = is_64_bit_ ? ReadProcessData<process_types::internal::Traits64>(
189 reinterpret_cast<uintptr_t>(peb.ProcessParameters), 276 process, peb_address, this)
190 &process_parameters)) { 277 : ReadProcessData<process_types::internal::Traits32>(
278 process, peb_address, this);
279 if (!result)
191 return false; 280 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 281
251 INITIALIZATION_STATE_SET_VALID(initialized_); 282 INITIALIZATION_STATE_SET_VALID(initialized_);
252 return true; 283 return true;
253 } 284 }
254 285
255 bool ProcessInfo::Is64Bit() const { 286 bool ProcessInfo::Is64Bit() const {
256 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 287 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
257 return is_64_bit_; 288 return is_64_bit_;
258 } 289 }
259 290
260 bool ProcessInfo::IsWow64() const { 291 bool ProcessInfo::IsWow64() const {
261 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 292 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
262 return is_wow64_; 293 return is_wow64_;
263 } 294 }
264 295
265 pid_t ProcessInfo::ProcessID() const { 296 pid_t ProcessInfo::ProcessID() const {
266 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 297 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
267 return process_basic_information_.UniqueProcessId; 298 return process_id_;
268 } 299 }
269 300
270 pid_t ProcessInfo::ParentProcessID() const { 301 pid_t ProcessInfo::ParentProcessID() const {
271 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 302 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
272 return process_basic_information_.InheritedFromUniqueProcessId; 303 return inherited_from_process_id_;
273 } 304 }
274 305
275 bool ProcessInfo::CommandLine(std::wstring* command_line) const { 306 bool ProcessInfo::CommandLine(std::wstring* command_line) const {
276 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 307 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
277 *command_line = command_line_; 308 *command_line = command_line_;
278 return true; 309 return true;
279 } 310 }
280 311
281 bool ProcessInfo::Modules(std::vector<std::wstring>* modules) const { 312 bool ProcessInfo::Modules(std::vector<std::wstring>* modules) const {
282 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 313 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
283 *modules = modules_; 314 *modules = modules_;
284 return true; 315 return true;
285 } 316 }
286 317
287 } // namespace crashpad 318 } // namespace crashpad
OLDNEW
« no previous file with comments | « util/win/process_info.h ('k') | util/win/process_info_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698