OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Crashpad Authors. All rights reserved. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (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 | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 | |
15 #include "util/win/process_info.h" | |
16 | |
17 #include "base/logging.h" | |
18 | |
19 namespace crashpad { | |
20 | |
21 namespace { | |
22 | |
23 NTSTATUS NtQueryInformationProcess(HANDLE process_handle, | |
24 PROCESSINFOCLASS process_information_class, | |
25 PVOID process_information, | |
26 ULONG process_information_length, | |
27 PULONG return_length) { | |
28 static decltype(::NtQueryInformationProcess)* nt_query_information_process = | |
29 reinterpret_cast<decltype(::NtQueryInformationProcess)*>(GetProcAddress( | |
30 LoadLibrary(L"ntdll.dll"), "NtQueryInformationProcess")); | |
31 DCHECK(nt_query_information_process); | |
32 return nt_query_information_process(process_handle, | |
33 process_information_class, | |
34 process_information, | |
35 process_information_length, | |
36 return_length); | |
37 } | |
38 | |
39 bool IsProcessWow64(HANDLE process_handle) { | |
40 static decltype(IsWow64Process)* is_wow64_process = | |
41 reinterpret_cast<decltype(IsWow64Process)*>( | |
42 GetProcAddress(LoadLibrary(L"kernel32.dll"), "IsWow64Process")); | |
43 if (!is_wow64_process) | |
44 return false; | |
45 BOOL is_wow64; | |
46 if (!is_wow64_process(process_handle, &is_wow64)) { | |
47 PLOG(ERROR) << "IsWow64Process"; | |
48 return false; | |
49 } | |
50 return is_wow64; | |
51 } | |
52 | |
53 bool ReadUnicodeString(HANDLE process, | |
54 const UNICODE_STRING& us, | |
55 std::wstring* result) { | |
56 if (us.Length == 0) { | |
57 *result = std::wstring(); | |
Mark Mentovai
2015/03/05 21:53:36
result->clear()
scottmg
2015/03/06 00:42:52
Done.
| |
58 return true; | |
59 } | |
60 DCHECK_EQ(us.Length % sizeof(wchar_t), 0u); | |
61 result->resize(us.Length / sizeof(wchar_t)); | |
62 SIZE_T bytes_read; | |
63 if (!ReadProcessMemory( | |
64 process, us.Buffer, &result->operator[](0), us.Length, &bytes_read)) { | |
65 PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING"; | |
66 return false; | |
67 } | |
68 if (bytes_read != us.Length) { | |
69 LOG(ERROR) << "ReadProcessMemory UNICODE_STRING incorrect size"; | |
70 return false; | |
71 } | |
72 return true; | |
73 } | |
74 | |
75 template <class T> bool ReadStruct(HANDLE process, uintptr_t at, T* into) { | |
76 SIZE_T bytes_read; | |
77 if (!ReadProcessMemory(process, | |
78 reinterpret_cast<const void*>(at), | |
79 into, | |
80 sizeof(T), | |
81 &bytes_read)) { | |
82 // We don't have a name for the type we're reading, so include the signature | |
83 // to get the type of T. | |
84 PLOG(ERROR) << "ReadProcessMemory: " << __FUNCSIG__; | |
Mark Mentovai
2015/03/05 21:53:36
Tiny nit: colons on these messages but not on the
scottmg
2015/03/06 00:42:52
Done.
| |
85 return false; | |
86 } | |
87 if (bytes_read != sizeof(T)) { | |
88 LOG(ERROR) << "ReadProcessMemory: " << __FUNCSIG__ << " incorrect size"; | |
89 return false; | |
90 } | |
91 return true; | |
92 } | |
93 | |
94 // PEB_LDR_DATA in winternl.h doesn't document the trailing | |
95 // InInitializationOrderModuleList field. Add that here. | |
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. | |
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 union { | |
115 LIST_ENTRY HashLinks; | |
116 struct { | |
117 PVOID SectionPointer; | |
118 ULONG CheckSum; | |
119 }; | |
120 }; | |
121 union { | |
122 ULONG TimeDateStamp; | |
123 PVOID LoadedImports; | |
124 }; | |
125 _ACTIVATION_CONTEXT* EntryPointActivationContext; | |
126 PVOID PatchInformation; | |
127 LIST_ENTRY ForwarderLinks; | |
128 LIST_ENTRY ServiceTagLinks; | |
129 LIST_ENTRY StaticLinks; | |
130 }; | |
131 | |
132 } // namespace | |
133 | |
134 ProcessInfo::ProcessInfo() | |
135 : process_basic_information_(), | |
136 command_line_(), | |
Mark Mentovai
2015/03/05 21:53:36
Let’s be explicit about everything: modules_ and i
scottmg
2015/03/06 00:42:52
Done.
| |
137 is_64_bit_(false), | |
138 is_wow64_(false) { | |
139 } | |
140 | |
141 ProcessInfo::~ProcessInfo() { | |
142 } | |
143 | |
144 bool ProcessInfo::Initialize(HANDLE process) { | |
145 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); | |
146 | |
147 is_wow64_ = IsProcessWow64(process); | |
148 | |
149 if (is_wow64_) { | |
150 // If it's WoW64, then it's 32-on-64. | |
151 is_64_bit_ = false; | |
152 } else { | |
153 // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to | |
154 // distinguish between these two cases. | |
155 SYSTEM_INFO system_info; | |
156 GetSystemInfo(&system_info); | |
157 is_64_bit_ = | |
158 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; | |
159 } | |
160 | |
161 #if ARCH_CPU_64_BITS | |
162 if (!is_64_bit_) { | |
163 LOG(ERROR) << "Reading different bitness not yet supported"; | |
164 return false; | |
165 } | |
166 #else | |
167 if (is_64_bit_) { | |
168 LOG(ERROR) << "Reading x64 process from x86 process not supported"; | |
Mark Mentovai
2015/03/05 21:53:36
The “escape WoW64 and call the native function” is
scottmg
2015/03/06 00:42:52
In this case, the Wow64 ntdll contains the necessa
| |
169 return false; | |
170 } | |
171 #endif | |
172 | |
173 ULONG bytes_returned; | |
174 NTSTATUS status = | |
175 crashpad::NtQueryInformationProcess(process, | |
176 ProcessBasicInformation, | |
177 &process_basic_information_, | |
178 sizeof(process_basic_information_), | |
179 &bytes_returned); | |
180 if (status < 0) { | |
181 LOG(ERROR) << "NtQueryInformationProcess: status=" << status; | |
182 return false; | |
183 } | |
184 if (bytes_returned != sizeof(process_basic_information_)) { | |
185 LOG(ERROR) << "NtQueryInformationProcess incorrect size"; | |
186 return false; | |
187 } | |
188 | |
189 // Try to read the process environment block. | |
190 PEB peb; | |
191 if (!ReadStruct(process, | |
192 reinterpret_cast<uintptr_t>( | |
193 process_basic_information_.PebBaseAddress), | |
194 &peb)) | |
195 return false; | |
Mark Mentovai
2015/03/05 21:53:36
{} on these raggedy ifs. The next one too.
scottmg
2015/03/06 00:42:52
Oops, done.
| |
196 | |
197 RTL_USER_PROCESS_PARAMETERS process_parameters; | |
198 if (!ReadStruct(process, | |
199 reinterpret_cast<uintptr_t>(peb.ProcessParameters), | |
200 &process_parameters)) | |
201 return false; | |
202 | |
203 if (!ReadUnicodeString( | |
204 process, process_parameters.CommandLine, &command_line_)) { | |
205 return false; | |
206 } | |
207 | |
208 FULL_PEB_LDR_DATA peb_ldr_data; | |
209 if (!ReadStruct(process, reinterpret_cast<uintptr_t>(peb.Ldr), &peb_ldr_data)) | |
210 return false; | |
211 | |
212 // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded | |
213 // modules. We use this method rather than EnumProcessModules to get the | |
214 // modules in initialization order rather than memory order. | |
215 LIST_ENTRY* last = peb_ldr_data.InInitializationOrderModuleList.Blink; | |
Mark Mentovai
2015/03/05 21:53:36
Can last and cur can be pointers to const data? If
scottmg
2015/03/06 00:42:52
Done.
| |
216 FULL_LDR_DATA_TABLE_ENTRY ldr_data_table_entry; | |
Mark Mentovai
2015/03/05 21:53:36
Pull this into the loop body.
scottmg
2015/03/06 00:42:52
Can't, it's used in the loop-expression.
| |
217 for (LIST_ENTRY* cur = peb_ldr_data.InInitializationOrderModuleList.Flink;; | |
218 cur = ldr_data_table_entry.InInitializationOrderLinks.Flink) { | |
219 // |cur| is the pointer to the LIST_ENTRY embedded in the | |
220 // FULL_LDR_DATA_TABLE_ENTRY, in the target process's address space. So we | |
221 // need to read from the target, and also offset back to the beginning of | |
222 // the structure. | |
223 if (!ReadStruct( | |
224 process, | |
225 reinterpret_cast<uintptr_t>(reinterpret_cast<char*>(cur) - | |
226 offsetof(FULL_LDR_DATA_TABLE_ENTRY, | |
227 InInitializationOrderLinks)), | |
228 &ldr_data_table_entry)) { | |
229 break; | |
230 } | |
231 // TODO(scottmg): Capture TimeDateStamp, Checksum, etc. too? | |
232 std::wstring module; | |
233 if (!ReadUnicodeString(process, ldr_data_table_entry.FullDllName, &module)) | |
234 break; | |
235 modules_.push_back(module); | |
236 if (cur == last) | |
Mark Mentovai
2015/03/05 21:53:36
Oh right, I see, yeah, this has to be in the loop.
| |
237 break; | |
238 } | |
239 | |
240 INITIALIZATION_STATE_SET_VALID(initialized_); | |
241 return true; | |
242 } | |
243 | |
244 bool ProcessInfo::Is64Bit() const { | |
245 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
246 return is_64_bit_; | |
247 } | |
248 | |
249 bool ProcessInfo::IsWow64() const { | |
250 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
251 return is_wow64_; | |
252 } | |
253 | |
254 pid_t ProcessInfo::ProcessID() const { | |
255 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
256 return process_basic_information_.UniqueProcessId; | |
257 } | |
258 | |
259 pid_t ProcessInfo::ParentProcessID() const { | |
260 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
261 return process_basic_information_.InheritedFromUniqueProcessId; | |
262 } | |
263 | |
264 bool ProcessInfo::CommandLine(std::wstring* command_line) const { | |
265 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
266 *command_line = command_line_; | |
267 return true; | |
268 } | |
269 | |
270 bool ProcessInfo::Modules(std::vector<std::wstring>* modules) const { | |
271 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
272 *modules = modules_; | |
273 return true; | |
274 } | |
275 | |
276 } // namespace crashpad | |
OLD | NEW |