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

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: . 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/win/process_structs.h"
18 21
19 namespace crashpad { 22 namespace crashpad {
20 23
21 namespace { 24 namespace {
22 25
23 NTSTATUS NtQueryInformationProcess(HANDLE process_handle, 26 NTSTATUS NtQueryInformationProcess(HANDLE process_handle,
24 PROCESSINFOCLASS process_information_class, 27 PROCESSINFOCLASS process_information_class,
25 PVOID process_information, 28 PVOID process_information,
26 ULONG process_information_length, 29 ULONG process_information_length,
27 PULONG return_length) { 30 PULONG return_length) {
(...skipping 15 matching lines...) Expand all
43 if (!is_wow64_process) 46 if (!is_wow64_process)
44 return false; 47 return false;
45 BOOL is_wow64; 48 BOOL is_wow64;
46 if (!is_wow64_process(process_handle, &is_wow64)) { 49 if (!is_wow64_process(process_handle, &is_wow64)) {
47 PLOG(ERROR) << "IsWow64Process"; 50 PLOG(ERROR) << "IsWow64Process";
48 return false; 51 return false;
49 } 52 }
50 return is_wow64; 53 return is_wow64;
51 } 54 }
52 55
56 template <class T>
53 bool ReadUnicodeString(HANDLE process, 57 bool ReadUnicodeString(HANDLE process,
54 const UNICODE_STRING& us, 58 const UNICODE_STRING_T<T>& us,
55 std::wstring* result) { 59 std::wstring* result) {
56 if (us.Length == 0) { 60 if (us.Length == 0) {
57 result->clear(); 61 result->clear();
58 return true; 62 return true;
59 } 63 }
60 DCHECK_EQ(us.Length % sizeof(wchar_t), 0u); 64 DCHECK_EQ(us.Length % sizeof(wchar_t), 0u);
61 result->resize(us.Length / sizeof(wchar_t)); 65 result->resize(us.Length / sizeof(wchar_t));
62 SIZE_T bytes_read; 66 SIZE_T bytes_read;
63 if (!ReadProcessMemory( 67 if (!ReadProcessMemory(process,
64 process, us.Buffer, &result->operator[](0), us.Length, &bytes_read)) { 68 reinterpret_cast<const void*>(us.Buffer),
69 &result->operator[](0),
70 us.Length,
71 &bytes_read)) {
65 PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING"; 72 PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING";
66 return false; 73 return false;
67 } 74 }
68 if (bytes_read != us.Length) { 75 if (bytes_read != us.Length) {
69 LOG(ERROR) << "ReadProcessMemory UNICODE_STRING incorrect size"; 76 LOG(ERROR) << "ReadProcessMemory UNICODE_STRING incorrect size";
70 return false; 77 return false;
71 } 78 }
72 return true; 79 return true;
73 } 80 }
74 81
75 template <class T> bool ReadStruct(HANDLE process, uintptr_t at, T* into) { 82 template <class T> bool ReadStruct(HANDLE process, uintptr_t at, T* into) {
76 SIZE_T bytes_read; 83 SIZE_T bytes_read;
77 if (!ReadProcessMemory(process, 84 if (!ReadProcessMemory(process,
78 reinterpret_cast<const void*>(at), 85 reinterpret_cast<const void*>(at),
79 into, 86 into,
80 sizeof(T), 87 sizeof(T),
81 &bytes_read)) { 88 &bytes_read)) {
82 // We don't have a name for the type we're reading, so include the signature 89 // We don't have a name for the type we're reading, so include the signature
83 // to get the type of T. 90 // to get the type of T.
84 PLOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__; 91 PLOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__;
85 return false; 92 return false;
86 } 93 }
87 if (bytes_read != sizeof(T)) { 94 if (bytes_read != sizeof(T)) {
88 LOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__ << " incorrect size"; 95 LOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__ << " incorrect size";
89 return false; 96 return false;
90 } 97 }
91 return true; 98 return true;
92 } 99 }
93 100
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 101 } // namespace
120 102
103 template <class T>
104 bool ReadProcessData(HANDLE process,
105 uintptr_t peb_address,
Mark Mentovai 2015/03/09 16:11:46 peb_address is in the remote process’ address spac
scottmg 2015/03/09 21:16:36 Right, that's why. Done.
106 ProcessInfo* process_info) {
107 // Try to read the process environment block.
108 PEB_T<T> peb;
109 if (!ReadStruct(process, peb_address, &peb))
110 return false;
111
112 RTL_USER_PROCESS_PARAMETERS_T<T> process_parameters;
113 if (!ReadStruct(process, peb.ProcessParameters, &process_parameters))
114 return false;
115
116 if (!ReadUnicodeString(process,
117 process_parameters.CommandLine,
118 &process_info->command_line_)) {
119 return false;
120 }
121
122 PEB_LDR_DATA_T<T> peb_ldr_data;
123 if (!ReadStruct(process, peb.Ldr, &peb_ldr_data))
124 return false;
125
126 // Include the first module in the memory order list to get our own name as
127 // it's not included in initialization order below.
128 std::wstring self_module;
129 LDR_DATA_TABLE_ENTRY_T<T> self_ldr_data_table_entry;
130 if (!ReadStruct(process,
131 reinterpret_cast<uintptr_t>(
132 reinterpret_cast<const char*>(
133 peb_ldr_data.InMemoryOrderModuleList.Flink) -
134 offsetof(LDR_DATA_TABLE_ENTRY_T<T>, InMemoryOrderLinks)),
135 &self_ldr_data_table_entry)) {
136 return false;
137 }
138 if (!ReadUnicodeString(
139 process, self_ldr_data_table_entry.FullDllName, &self_module)) {
140 return false;
141 }
142 process_info->modules_.push_back(self_module);
143
144 // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded
145 // modules. We use this method rather than EnumProcessModules to get the
146 // modules in initialization order rather than memory order.
147 T last = peb_ldr_data.InInitializationOrderModuleList.Blink;
148 LDR_DATA_TABLE_ENTRY_T<T> ldr_data_table_entry;
149 for (T cur = peb_ldr_data.InInitializationOrderModuleList.Flink;;
150 cur = ldr_data_table_entry.InInitializationOrderLinks.Flink) {
151 // |cur| is the pointer to the LIST_ENTRY embedded in the
152 // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need
153 // to read from the target, and also offset back to the beginning of the
154 // structure.
155 if (!ReadStruct(
156 process,
157 reinterpret_cast<uintptr_t>(reinterpret_cast<const char*>(cur) -
158 offsetof(LDR_DATA_TABLE_ENTRY_T<T>,
159 InInitializationOrderLinks)),
160 &ldr_data_table_entry)) {
161 break;
162 }
163 // TODO(scottmg): Capture TimeDateStamp, Checksum, etc. too?
164 std::wstring module;
165 if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module))
166 break;
167 process_info->modules_.push_back(module);
168 if (cur == last)
169 break;
170 }
171
172 return true;
173 }
174
121 ProcessInfo::ProcessInfo() 175 ProcessInfo::ProcessInfo()
122 : process_basic_information_(), 176 : process_id_(),
177 inherited_from_process_id_(),
123 command_line_(), 178 command_line_(),
124 modules_(), 179 modules_(),
125 is_64_bit_(false), 180 is_64_bit_(false),
126 is_wow64_(false), 181 is_wow64_(false),
127 initialized_() { 182 initialized_() {
128 } 183 }
129 184
130 ProcessInfo::~ProcessInfo() { 185 ProcessInfo::~ProcessInfo() {
131 } 186 }
132 187
133 bool ProcessInfo::Initialize(HANDLE process) { 188 bool ProcessInfo::Initialize(HANDLE process) {
134 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); 189 INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
135 190
136 is_wow64_ = IsProcessWow64(process); 191 is_wow64_ = IsProcessWow64(process);
137 192
138 if (is_wow64_) { 193 if (is_wow64_) {
139 // If it's WoW64, then it's 32-on-64. 194 // If it's WoW64, then it's 32-on-64.
140 is_64_bit_ = false; 195 is_64_bit_ = false;
141 } else { 196 } else {
142 // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to 197 // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to
143 // distinguish between these two cases. 198 // distinguish between these two cases.
144 SYSTEM_INFO system_info; 199 SYSTEM_INFO system_info;
145 GetSystemInfo(&system_info); 200 GetSystemInfo(&system_info);
146 is_64_bit_ = 201 is_64_bit_ =
147 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; 202 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
148 } 203 }
149 204
150 #if ARCH_CPU_64_BITS 205 #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_) { 206 if (is_64_bit_) {
157 LOG(ERROR) << "Reading x64 process from x86 process not supported"; 207 LOG(ERROR) << "Reading x64 process from x86 process not supported";
158 return false; 208 return false;
159 } 209 }
160 #endif 210 #endif
161 211
162 ULONG bytes_returned; 212 ULONG bytes_returned;
213 // We assume this process is not running on Wow64. The
Mark Mentovai 2015/03/09 16:11:46 I don’t think that this assumption can really hold
scottmg 2015/03/09 21:16:36 OK. I would prefer not to support running as Wow64
214 // PROCESS_BASIC_INFORMATION uses the OS size (that is, even Wow64 has a 64
215 // bit one.)
216 #if ARCH_CPU_32_BITS
217 PROCESS_BASIC_INFORMATION_T<DWORD> process_basic_information;
218 #else
219 PROCESS_BASIC_INFORMATION_T<DWORD64> process_basic_information;
220 #endif
163 NTSTATUS status = 221 NTSTATUS status =
164 crashpad::NtQueryInformationProcess(process, 222 crashpad::NtQueryInformationProcess(process,
165 ProcessBasicInformation, 223 ProcessBasicInformation,
166 &process_basic_information_, 224 &process_basic_information,
167 sizeof(process_basic_information_), 225 sizeof(process_basic_information),
168 &bytes_returned); 226 &bytes_returned);
169 if (status < 0) { 227 if (status < 0) {
170 LOG(ERROR) << "NtQueryInformationProcess: status=" << status; 228 LOG(ERROR) << "NtQueryInformationProcess: status=" << status;
171 return false; 229 return false;
172 } 230 }
173 if (bytes_returned != sizeof(process_basic_information_)) { 231 if (bytes_returned != sizeof(process_basic_information)) {
174 LOG(ERROR) << "NtQueryInformationProcess incorrect size"; 232 LOG(ERROR) << "NtQueryInformationProcess incorrect size";
175 return false; 233 return false;
176 } 234 }
235 process_id_ = process_basic_information.UniqueProcessId;
236 inherited_from_process_id_ =
237 process_basic_information.InheritedFromUniqueProcessId;
177 238
178 // Try to read the process environment block. 239 // We now want to read the PEB to gather the rest of our information. The
179 PEB peb; 240 // PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32,
180 if (!ReadStruct(process, 241 // but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both).
181 reinterpret_cast<uintptr_t>( 242 // The address of this is found by a second call to NtQueryInformationProcess.
182 process_basic_information_.PebBaseAddress), 243 uintptr_t peb_address = process_basic_information.PebBaseAddress;
183 &peb)) { 244 if (is_wow64_) {
184 return false; 245 ULONG_PTR wow64_peb_address;
246 status =
247 crashpad::NtQueryInformationProcess(process,
248 ProcessWow64Information,
249 &wow64_peb_address,
250 sizeof(wow64_peb_address),
251 &bytes_returned);
252 if (status < 0) {
253 LOG(ERROR) << "NtQueryInformationProcess: status=" << status;
254 return false;
255 }
256 if (bytes_returned != sizeof(wow64_peb_address)) {
257 LOG(ERROR) << "NtQueryInformationProcess incorrect size";
258 return false;
259 }
260 peb_address = wow64_peb_address;
185 } 261 }
186 262
187 RTL_USER_PROCESS_PARAMETERS process_parameters; 263 // Read the PEB data using the correct word size.
188 if (!ReadStruct(process, 264 bool result = is_64_bit_
189 reinterpret_cast<uintptr_t>(peb.ProcessParameters), 265 ? ReadProcessData<DWORD64>(process, peb_address, this)
190 &process_parameters)) { 266 : ReadProcessData<DWORD>(process, peb_address, this);
267 if (!result)
191 return false; 268 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 269
251 INITIALIZATION_STATE_SET_VALID(initialized_); 270 INITIALIZATION_STATE_SET_VALID(initialized_);
252 return true; 271 return true;
253 } 272 }
254 273
255 bool ProcessInfo::Is64Bit() const { 274 bool ProcessInfo::Is64Bit() const {
256 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 275 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
257 return is_64_bit_; 276 return is_64_bit_;
258 } 277 }
259 278
260 bool ProcessInfo::IsWow64() const { 279 bool ProcessInfo::IsWow64() const {
261 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 280 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
262 return is_wow64_; 281 return is_wow64_;
263 } 282 }
264 283
265 pid_t ProcessInfo::ProcessID() const { 284 pid_t ProcessInfo::ProcessID() const {
266 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 285 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
267 return process_basic_information_.UniqueProcessId; 286 return process_id_;
268 } 287 }
269 288
270 pid_t ProcessInfo::ParentProcessID() const { 289 pid_t ProcessInfo::ParentProcessID() const {
271 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 290 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
272 return process_basic_information_.InheritedFromUniqueProcessId; 291 return inherited_from_process_id_;
273 } 292 }
274 293
275 bool ProcessInfo::CommandLine(std::wstring* command_line) const { 294 bool ProcessInfo::CommandLine(std::wstring* command_line) const {
276 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 295 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
277 *command_line = command_line_; 296 *command_line = command_line_;
278 return true; 297 return true;
279 } 298 }
280 299
281 bool ProcessInfo::Modules(std::vector<std::wstring>* modules) const { 300 bool ProcessInfo::Modules(std::vector<std::wstring>* modules) const {
282 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 301 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
283 *modules = modules_; 302 *modules = modules_;
284 return true; 303 return true;
285 } 304 }
286 305
287 } // namespace crashpad 306 } // namespace crashpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698