1 : // Copyright 2012 Google Inc. 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 : // This file declares the trace::service::ProcessInfo class which
16 : // retrieves and encapsulates the process related information captured
17 : // within a trace file.
18 :
19 : #include "syzygy/trace/service/process_info.h"
20 :
21 : #include <psapi.h>
22 : #include <winternl.h>
23 :
24 : #include "base/logging.h"
25 : #include "base/strings/string_util.h"
26 : #include "syzygy/common/align.h"
27 : #include "syzygy/common/com_utils.h"
28 :
29 : // From advapi32.dll, but including ntsecapi.h causes conflicting declarations.
30 : extern "C" ULONG NTAPI LsaNtStatusToWinError(__in NTSTATUS status);
31 :
32 : namespace trace {
33 : namespace service {
34 :
35 : namespace {
36 :
37 : typedef NTSTATUS (NTAPI *FuncPtrNtQueryInformationProcess)(
38 : HANDLE ProcessHandle,
39 : DWORD ProcessInformationClass,
40 : PVOID ProcessInformation,
41 : DWORD ProcessInformationLength,
42 : PDWORD ReturnLength);
43 :
44 : // Helper function to get the basic process info for pid/handle.
45 E : bool GetPBI(uint32_t pid, HANDLE handle, PROCESS_BASIC_INFORMATION* pbi) {
46 E : DCHECK(pbi != NULL);
47 :
48 E : HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
49 E : if (ntdll == NULL) {
50 i : DWORD error = ::GetLastError();
51 i : LOG(ERROR) << "Failed to get ntdll.dll module handle: "
52 : << ::common::LogWe(error) << ".";
53 i : return false;
54 : }
55 :
56 : FuncPtrNtQueryInformationProcess query_func =
57 : reinterpret_cast<FuncPtrNtQueryInformationProcess>(
58 E : ::GetProcAddress(ntdll, "NtQueryInformationProcess"));
59 E : if (query_func == NULL) {
60 i : DWORD error = ::GetLastError();
61 i : LOG(ERROR) << "Failed to get NtQueryInformationProcess proc address: "
62 : << ::common::LogWe(error) << ".";
63 i : return false;
64 : }
65 :
66 E : NTSTATUS status = query_func(handle, 0, pbi, sizeof(*pbi), NULL);
67 E : if (status != 0) {
68 i : LOG(ERROR) << "Failed to query process information for PID=" << pid
69 : << ": " << ::common::LogWe(::LsaNtStatusToWinError(status))
70 : << ".";
71 i : return false;
72 : }
73 :
74 E : return true;
75 E : }
76 :
77 : bool ReadEnvironmentString(HANDLE handle,
78 : size_t page_size,
79 : const wchar_t* remote_env_string,
80 E : std::vector<wchar_t>* environment) {
81 E : DCHECK_LT(0u, page_size);
82 E : DCHECK(common::IsPowerOfTwo(page_size));
83 E : DCHECK(remote_env_string != NULL);
84 E : DCHECK(environment != NULL);
85 :
86 E : environment->clear();
87 :
88 E : std::vector<uint8_t> vector(page_size);
89 E : uint8_t* buffer = &vector.at(0);
90 E : const wchar_t* wbuffer = reinterpret_cast<const wchar_t*>(buffer);
91 : const uint8_t* remote_cursor =
92 E : reinterpret_cast<const uint8_t*>(remote_env_string);
93 : const uint8_t* next_page = reinterpret_cast<const uint8_t*>(
94 E : common::AlignUp(reinterpret_cast<size_t>(remote_cursor), page_size));
95 :
96 E : size_t nulls_in_a_row = 0;
97 E : while (true) {
98 E : DCHECK_GE(next_page, remote_cursor);
99 E : if (remote_cursor == next_page)
100 E : next_page += page_size;
101 :
102 : // Determine the maximum amount of data to read. We read a page at a
103 : // time so as to avoid going off the end of addressable memory, something
104 : // that ReadProcessMemory really hates (it will return zero bytes read and
105 : // ERROR_PARTIAL_COPY).
106 E : size_t bytes_to_read = next_page - remote_cursor;
107 E : DCHECK_EQ(0u, bytes_to_read % sizeof(wbuffer[0]));
108 :
109 E : SIZE_T bytes_read = 0;
110 E : if (!::ReadProcessMemory(handle, remote_cursor, buffer, bytes_to_read,
111 : &bytes_read)) {
112 i : DWORD error = ::GetLastError();
113 i : LOG(ERROR) << "Failed to read environment string: "
114 : << ::common::LogWe(error) << ".";
115 i : return false;
116 : }
117 E : DCHECK_LT(0u, bytes_read);
118 E : size_t elems_read = bytes_read / sizeof(wbuffer[0]);
119 E : size_t bytes_used = elems_read * sizeof(wbuffer[0]);
120 E : remote_cursor += bytes_used;
121 :
122 : // Look for the terminating double NULL.
123 E : for (size_t i = 0; i < elems_read; ++i) {
124 E : if (wbuffer[i] == 0) {
125 E : if (++nulls_in_a_row == 2) {
126 : // We found the terminating double NULL. Append the end of the
127 : // string and we're done.
128 E : environment->insert(environment->end(), wbuffer, wbuffer + i + 1);
129 E : return true;
130 : }
131 E : } else {
132 E : nulls_in_a_row = 0;
133 : }
134 E : }
135 :
136 : // If we get here then the entire buffer we just read needs to be appended
137 : // to the environment string.
138 E : environment->insert(environment->end(), wbuffer, wbuffer + elems_read);
139 E : }
140 :
141 i : NOTREACHED();
142 i : return false;
143 E : }
144 :
145 : // Extract the exe path and command line for the process given by pid/handle.
146 : // Note that there are other ways to retrieve the exe path, but since this
147 : // function will already be spelunking in the same area (to get the command
148 : // line) we just get the exe path while we're there.
149 : bool GetProcessStrings(uint32_t pid,
150 : HANDLE handle,
151 : size_t page_size,
152 : base::FilePath* exe_path,
153 : std::wstring* cmd_line,
154 E : std::vector<wchar_t>* environment) {
155 E : DCHECK(exe_path != NULL);
156 E : DCHECK(cmd_line != NULL);
157 E : DCHECK(environment != NULL);
158 :
159 : // Fetch the basic process information.
160 E : PROCESS_BASIC_INFORMATION pbi = {};
161 E : if (!GetPBI(pid, handle, &pbi)) {
162 i : return false;
163 : }
164 :
165 : // TODO(rogerm): Validate that the target process has the same bitness as
166 : // the querying process; otherwise, the following won't work.
167 :
168 : // Setup the variables that we'll use later.
169 E : uint8_t* peb_base_address = reinterpret_cast<uint8_t*>(pbi.PebBaseAddress);
170 E : uint8_t* user_proc_params = NULL;
171 E : UNICODE_STRING string_value[2] = {};
172 :
173 : // Get the address of the process parameters.
174 E : const size_t kProcessParamOffset = FIELD_OFFSET(PEB, ProcessParameters);
175 E : if (!::ReadProcessMemory(handle, peb_base_address + kProcessParamOffset,
176 : &user_proc_params, sizeof(user_proc_params), NULL)) {
177 i : DWORD error = ::GetLastError();
178 i : LOG(ERROR) << "Failed to read process parameter pointer for PID=" << pid
179 : << " " << ::common::LogWe(error) << ".";
180 i : return false;
181 : }
182 :
183 : // Get the image path name and command line UNICODE_STRING structures.
184 : // string_value[0] will be the image path name, and string_value[1] will
185 : // be the command line.
186 : const size_t kImagePathNameOffset =
187 E : FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, ImagePathName);
188 E : if (!::ReadProcessMemory(handle, user_proc_params + kImagePathNameOffset,
189 : &string_value[0], sizeof(string_value), NULL)) {
190 i : DWORD error = ::GetLastError();
191 i : LOG(ERROR) << "Failed to read the process parameters for PID=" << pid
192 : << ": " << ::common::LogWe(error) << ".";
193 i : return false;
194 : }
195 :
196 : // Read the image path name.
197 E : std::wstring temp_exe_path;
198 E : size_t num_chars_in_path = string_value[0].Length / sizeof(wchar_t);
199 E : if (!::ReadProcessMemory(
200 : handle, string_value[0].Buffer,
201 : base::WriteInto(&temp_exe_path, num_chars_in_path + 1),
202 : string_value[0].Length, NULL)) {
203 i : DWORD error = ::GetLastError();
204 i : LOG(ERROR) << "Failed to read the exe path for PID=" << pid
205 : << ": " << ::common::LogWe(error) << ".";
206 i : return false;
207 : }
208 E : *exe_path = base::FilePath(temp_exe_path);
209 :
210 : // Read the command line.
211 E : size_t num_chars_in_cmd_line = string_value[1].Length / sizeof(wchar_t);
212 E : if (!::ReadProcessMemory(handle, string_value[1].Buffer,
213 : base::WriteInto(cmd_line, num_chars_in_cmd_line + 1),
214 : string_value[1].Length, NULL)) {
215 i : DWORD error = ::GetLastError();
216 i : LOG(ERROR) << "Failed to read the command line for PID=" << pid
217 : << ": " << ::common::LogWe(error) << ".";
218 i : return false;
219 : }
220 :
221 : // Get the environment string. Note that this a pointer into a remote process
222 : // so we can't directly dereference it. This is not documented directly in
223 : // winternl.h, but it is documented here: http://goto.google.com/win-proc-env
224 E : const size_t kEnvironmentStringOffset = 0x48;
225 E : wchar_t* remote_env_string = NULL;
226 E : if (!::ReadProcessMemory(handle, user_proc_params + kEnvironmentStringOffset,
227 : &remote_env_string, sizeof(remote_env_string),
228 : NULL)) {
229 i : DWORD error = ::GetLastError();
230 i : LOG(ERROR) << "Failed to read environment variable string for PID=" << pid
231 : << ": " << ::common::LogWe(error) << ".";
232 i : return false;
233 : }
234 :
235 : // Finally, read the environment string.
236 E : if (!ReadEnvironmentString(handle, page_size, remote_env_string,
237 : environment)) {
238 i : return false;
239 : }
240 :
241 E : return true;
242 E : }
243 :
244 : // Gets the NT headers of the running process.
245 : bool GetProcessNtHeaders(uint32_t pid,
246 : HANDLE handle,
247 E : IMAGE_NT_HEADERS* nt_headers) {
248 E : DCHECK(nt_headers != NULL);
249 E : HMODULE module = 0;
250 E : DWORD dummy = 0;
251 :
252 : // The first module returned by the enumeration will be the executable. So
253 : // we only need to ask for one HMODULE.
254 E : if (!::EnumProcessModules(handle, &module, sizeof(module), &dummy)) {
255 i : DWORD error = ::GetLastError();
256 i : LOG(ERROR) << "Failed to get module handle for PID=" << pid
257 : << ": " << ::common::LogWe(error) << ".";
258 i : return false;
259 : }
260 :
261 : // We now have enough information get the module info for the executable.
262 E : MODULEINFO info = {};
263 E : if (!::GetModuleInformation(handle, module, &info, sizeof(info))) {
264 i : DWORD error = ::GetLastError();
265 i : LOG(ERROR) << "Failed to get module info for PID=" << pid
266 : << ": " << ::common::LogWe(error) << ".";
267 i : return false;
268 : }
269 :
270 E : uint8_t* base_addr = reinterpret_cast<uint8_t*>(info.lpBaseOfDll);
271 :
272 : // Get the DOS header.
273 : IMAGE_DOS_HEADER dos_header;
274 E : uint8_t* addr_to_read = base_addr;
275 E : SIZE_T bytes_to_read = sizeof(IMAGE_DOS_HEADER);
276 E : SIZE_T bytes_read = 0;
277 : if (!::ReadProcessMemory(handle, addr_to_read, &dos_header, bytes_to_read,
278 E : &bytes_read) ||
279 : bytes_read != bytes_to_read) {
280 i : DWORD error = ::GetLastError();
281 i : LOG(ERROR) << "Failed to read DOS header for PID=" << pid
282 : << " " << ::common::LogWe(error) << ".";
283 i : return false;
284 : }
285 :
286 : // Get the NT headers.
287 E : addr_to_read = base_addr + dos_header.e_lfanew;
288 E : bytes_to_read = sizeof(IMAGE_NT_HEADERS);
289 E : bytes_read = 0;
290 : if (!::ReadProcessMemory(handle, addr_to_read, nt_headers, bytes_to_read,
291 E : &bytes_read) ||
292 : bytes_read != bytes_to_read) {
293 i : DWORD error = ::GetLastError();
294 i : LOG(ERROR) << "Failed to read NT headers for PID=" << pid
295 : << " " << ::common::LogWe(error) << ".";
296 i : return false;
297 : }
298 :
299 E : return true;
300 E : }
301 :
302 : } // namespace
303 :
304 : ProcessInfo::ProcessInfo()
305 E : : process_id(0),
306 E : exe_base_address(0),
307 E : exe_image_size(0),
308 E : exe_checksum(0),
309 E : exe_time_date_stamp(0) {
310 E : ::memset(&os_version_info, 0, sizeof(os_version_info));
311 E : ::memset(&system_info, 0, sizeof(system_info));
312 E : ::memset(&memory_status, 0, sizeof(memory_status));
313 E : }
314 :
315 E : ProcessInfo::~ProcessInfo() {
316 E : }
317 :
318 E : void ProcessInfo::Reset() {
319 E : process_handle.Close();
320 E : process_id = 0;
321 E : executable_path.clear();
322 E : command_line.clear();
323 E : environment.clear();
324 E : ::memset(&os_version_info, 0, sizeof(os_version_info));
325 E : ::memset(&system_info, 0, sizeof(system_info));
326 E : ::memset(&memory_status, 0, sizeof(memory_status));
327 E : exe_base_address = 0;
328 E : exe_image_size = 0;
329 E : exe_checksum = 0;
330 E : exe_time_date_stamp = 0;
331 E : }
332 :
333 E : bool ProcessInfo::Initialize(uint32_t pid) {
334 : // TODO(chrisha): This whole mechanism is racy by its very nature, as it
335 : // reads memory from a remote process that is running, and which may be
336 : // changing the things being read. In practice this has not proved to be
337 : // a problem as we are typically running under the loader lock, but this
338 : // is not true when running instrumented EXEs. Long term it would be good
339 : // to make this run in the instrumented process and have it shuttle the
340 : // data across in the first buffer.
341 :
342 : // Open the process given by pid. We need a process handle that (1) remains
343 : // valid over time (2) lets us query for info about the process, and (3)
344 : // allows us to read the command line from the process memory.
345 : const DWORD kFlags =
346 E : PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
347 :
348 E : process_handle.Set(::OpenProcess(kFlags, FALSE, pid));
349 :
350 E : if (!process_handle.IsValid()) {
351 i : DWORD error = ::GetLastError();
352 i : LOG(ERROR) << "Failed to open PID=" << pid << " " << ::common::LogWe(error)
353 : << ".";
354 i : Reset();
355 i : return false;
356 : }
357 :
358 E : process_id = pid;
359 :
360 E : ::GetSystemInfo(&system_info);
361 :
362 : // Get the executable path, command line and environment string.
363 E : if (!GetProcessStrings(process_id, process_handle.Get(),
364 : system_info.dwPageSize, &executable_path,
365 : &command_line, &environment)) {
366 i : Reset();
367 i : return false;
368 : }
369 :
370 : // Get the operating system and hardware information.
371 E : os_version_info.dwOSVersionInfoSize = sizeof(os_version_info);
372 E : if (!::GetVersionEx(
373 : reinterpret_cast<OSVERSIONINFO*>(&os_version_info))) {
374 i : DWORD error = ::GetLastError();
375 i : LOG(ERROR) << "Failed to get OS version information: "
376 : << ::common::LogWe(error) << ".";
377 i : Reset();
378 i : return false;
379 : }
380 :
381 E : memory_status.dwLength = sizeof(memory_status);
382 E : if (!::GlobalMemoryStatusEx(&memory_status)) {
383 i : DWORD error = ::GetLastError();
384 i : LOG(ERROR) << "Failed to get global memory status: "
385 : << ::common::LogWe(error) << ".";
386 i : Reset();
387 i : return false;
388 : }
389 :
390 : // Get the headers for the running image and use these to populate various
391 : // fields.
392 : IMAGE_NT_HEADERS nt_headers;
393 E : if (!GetProcessNtHeaders(process_id, process_handle.Get(), &nt_headers)) {
394 i : Reset();
395 i : return false;
396 : }
397 E : exe_base_address = nt_headers.OptionalHeader.ImageBase;
398 E : exe_image_size = nt_headers.OptionalHeader.SizeOfImage;
399 E : exe_checksum = nt_headers.OptionalHeader.CheckSum;
400 E : exe_time_date_stamp = nt_headers.FileHeader.TimeDateStamp;
401 :
402 E : return true;
403 E : }
404 :
405 : } // namespace service
406 : } // namespace trace
|