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 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> vector(page_size);
89 E : uint8* buffer = &vector.at(0);
90 E : const wchar_t* wbuffer = reinterpret_cast<const wchar_t*>(buffer);
91 : const uint8* remote_cursor =
92 E : reinterpret_cast<const uint8*>(remote_env_string);
93 : const uint8* next_page = reinterpret_cast<const uint8*>(
94 : common::AlignUp(reinterpret_cast<size_t>(remote_cursor),
95 E : page_size));
96 :
97 E : size_t nulls_in_a_row = 0;
98 E : while (true) {
99 E : DCHECK_GE(next_page, remote_cursor);
100 E : if (remote_cursor == next_page)
101 E : next_page += page_size;
102 :
103 : // Determine the maximum amount of data to read. We read a page at a
104 : // time so as to avoid going off the end of addressable memory, something
105 : // that ReadProcessMemory really hates (it will return zero bytes read and
106 : // ERROR_PARTIAL_COPY).
107 E : size_t bytes_to_read = next_page - remote_cursor;
108 E : DCHECK_EQ(0u, bytes_to_read % sizeof(wbuffer[0]));
109 :
110 E : SIZE_T bytes_read = 0;
111 : if (!::ReadProcessMemory(handle, remote_cursor, buffer, bytes_to_read,
112 E : &bytes_read)) {
113 i : DWORD error = ::GetLastError();
114 i : LOG(ERROR) << "Failed to read environment string: "
115 : << ::common::LogWe(error) << ".";
116 i : return false;
117 : }
118 E : DCHECK_LT(0u, bytes_read);
119 E : size_t elems_read = bytes_read / sizeof(wbuffer[0]);
120 E : size_t bytes_used = elems_read * sizeof(wbuffer[0]);
121 E : remote_cursor += bytes_used;
122 :
123 : // Look for the terminating double NULL.
124 E : for (size_t i = 0; i < elems_read; ++i) {
125 E : if (wbuffer[i] == 0) {
126 E : if (++nulls_in_a_row == 2) {
127 : // We found the terminating double NULL. Append the end of the
128 : // string and we're done.
129 E : environment->insert(environment->end(), wbuffer, wbuffer + i + 1);
130 E : return true;
131 : }
132 E : } else {
133 E : nulls_in_a_row = 0;
134 : }
135 E : }
136 :
137 : // If we get here then the entire buffer we just read needs to be appended
138 : // to the environment string.
139 E : environment->insert(environment->end(), wbuffer, wbuffer + elems_read);
140 E : }
141 :
142 i : NOTREACHED();
143 i : return false;
144 E : }
145 :
146 : // Extract the exe path and command line for the process given by pid/handle.
147 : // Note that there are other ways to retrieve the exe path, but since this
148 : // function will already be spelunking in the same area (to get the command
149 : // line) we just get the exe path while we're there.
150 : bool GetProcessStrings(uint32 pid,
151 : HANDLE handle,
152 : size_t page_size,
153 : base::FilePath* exe_path,
154 : std::wstring* cmd_line,
155 E : std::vector<wchar_t>* environment) {
156 E : DCHECK(exe_path != NULL);
157 E : DCHECK(cmd_line != NULL);
158 E : DCHECK(environment != NULL);
159 :
160 : // Fetch the basic process information.
161 E : PROCESS_BASIC_INFORMATION pbi = {};
162 E : if (!GetPBI(pid, handle, &pbi)) {
163 i : return false;
164 : }
165 :
166 : // TODO(rogerm): Validate that the target process has the same bitness as
167 : // the querying process; otherwise, the following won't work.
168 :
169 : // Setup the variables that we'll use later.
170 E : uint8* peb_base_address = reinterpret_cast<uint8*>(pbi.PebBaseAddress);
171 E : uint8* user_proc_params = NULL;
172 E : UNICODE_STRING string_value[2] = {};
173 :
174 : // Get the address of the process parameters.
175 E : const size_t kProcessParamOffset = FIELD_OFFSET(PEB, ProcessParameters);
176 : if (!::ReadProcessMemory(handle, peb_base_address + kProcessParamOffset,
177 E : &user_proc_params, sizeof(user_proc_params), NULL)) {
178 i : DWORD error = ::GetLastError();
179 i : LOG(ERROR) << "Failed to read process parameter pointer for PID=" << pid
180 : << " " << ::common::LogWe(error) << ".";
181 i : return false;
182 : }
183 :
184 : // Get the image path name and command line UNICODE_STRING structures.
185 : // string_value[0] will be the image path name, and string_value[1] will
186 : // be the command line.
187 : const size_t kImagePathNameOffset =
188 E : FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, ImagePathName);
189 : if (!::ReadProcessMemory(handle, user_proc_params + kImagePathNameOffset,
190 E : &string_value[0], sizeof(string_value), NULL)) {
191 i : DWORD error = ::GetLastError();
192 i : LOG(ERROR) << "Failed to read the process parameters for PID=" << pid
193 : << ": " << ::common::LogWe(error) << ".";
194 i : return false;
195 : }
196 :
197 : // Read the image path name.
198 E : std::wstring temp_exe_path;
199 E : size_t num_chars_in_path = string_value[0].Length / sizeof(wchar_t);
200 : if (!::ReadProcessMemory(handle, string_value[0].Buffer,
201 : WriteInto(&temp_exe_path, num_chars_in_path + 1),
202 E : 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 : if (!::ReadProcessMemory(handle, string_value[1].Buffer,
213 : WriteInto(cmd_line, num_chars_in_cmd_line + 1),
214 E : 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 : if (!::ReadProcessMemory(handle, user_proc_params + kEnvironmentStringOffset,
227 : &remote_env_string, sizeof(remote_env_string),
228 E : 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 : if (!ReadEnvironmentString(handle, page_size, remote_env_string,
237 E : 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(
246 E : uint32 pid, HANDLE handle, IMAGE_NT_HEADERS* nt_headers) {
247 E : DCHECK(nt_headers != NULL);
248 E : HMODULE module = 0;
249 E : DWORD dummy = 0;
250 :
251 : // The first module returned by the enumeration will be the executable. So
252 : // we only need to ask for one HMODULE.
253 E : if (!::EnumProcessModules(handle, &module, sizeof(module), &dummy)) {
254 i : DWORD error = ::GetLastError();
255 i : LOG(ERROR) << "Failed to get module handle for PID=" << pid
256 : << ": " << ::common::LogWe(error) << ".";
257 i : return false;
258 : }
259 :
260 : // We now have enough information get the module info for the executable.
261 E : MODULEINFO info = {};
262 E : if (!::GetModuleInformation(handle, module, &info, sizeof(info))) {
263 i : DWORD error = ::GetLastError();
264 i : LOG(ERROR) << "Failed to get module info for PID=" << pid
265 : << ": " << ::common::LogWe(error) << ".";
266 i : return false;
267 : }
268 :
269 E : uint8* base_addr = reinterpret_cast<uint8*>(info.lpBaseOfDll);
270 :
271 : // Get the DOS header.
272 : IMAGE_DOS_HEADER dos_header;
273 E : uint8* addr_to_read = base_addr;
274 E : SIZE_T bytes_to_read = sizeof(IMAGE_DOS_HEADER);
275 E : SIZE_T bytes_read = 0;
276 : if (!::ReadProcessMemory(handle, addr_to_read, &dos_header, bytes_to_read,
277 : &bytes_read) ||
278 E : bytes_read != bytes_to_read) {
279 i : DWORD error = ::GetLastError();
280 i : LOG(ERROR) << "Failed to read DOS header for PID=" << pid
281 : << " " << ::common::LogWe(error) << ".";
282 i : return false;
283 : }
284 :
285 : // Get the NT headers.
286 E : addr_to_read = base_addr + dos_header.e_lfanew;
287 E : bytes_to_read = sizeof(IMAGE_NT_HEADERS);
288 E : bytes_read = 0;
289 : if (!::ReadProcessMemory(handle, addr_to_read, nt_headers, bytes_to_read,
290 : &bytes_read) ||
291 E : bytes_read != bytes_to_read) {
292 i : DWORD error = ::GetLastError();
293 i : LOG(ERROR) << "Failed to read NT headers for PID=" << pid
294 : << " " << ::common::LogWe(error) << ".";
295 i : return false;
296 : }
297 :
298 E : return true;
299 E : }
300 :
301 : } // namespace
302 :
303 : ProcessInfo::ProcessInfo()
304 : : process_id(0),
305 : exe_base_address(0),
306 : exe_image_size(0),
307 : exe_checksum(0),
308 E : exe_time_date_stamp(0) {
309 E : ::memset(&os_version_info, 0, sizeof(os_version_info));
310 E : ::memset(&system_info, 0, sizeof(system_info));
311 E : ::memset(&memory_status, 0, sizeof(memory_status));
312 E : }
313 :
314 E : ProcessInfo::~ProcessInfo() {
315 E : }
316 :
317 E : void ProcessInfo::Reset() {
318 E : process_handle.Close();
319 E : process_id = 0;
320 E : executable_path.clear();
321 E : command_line.clear();
322 E : environment.clear();
323 E : ::memset(&os_version_info, 0, sizeof(os_version_info));
324 E : ::memset(&system_info, 0, sizeof(system_info));
325 E : ::memset(&memory_status, 0, sizeof(memory_status));
326 E : exe_base_address = 0;
327 E : exe_image_size = 0;
328 E : exe_checksum = 0;
329 E : exe_time_date_stamp = 0;
330 E : }
331 :
332 E : bool ProcessInfo::Initialize(uint32 pid) {
333 : // TODO(chrisha): This whole mechanism is racy by its very nature, as it
334 : // reads memory from a remote process that is running, and which may be
335 : // changing the things being read. In practice this has not proved to be
336 : // a problem as we are typically running under the loader lock, but this
337 : // is not true when running instrumented EXEs. Long term it would be good
338 : // to make this run in the instrumented process and have it shuttle the
339 : // data across in the first buffer.
340 :
341 : // Open the process given by pid. We need a process handle that (1) remains
342 : // valid over time (2) lets us query for info about the process, and (3)
343 : // allows us to read the command line from the process memory.
344 : const DWORD kFlags =
345 E : PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
346 :
347 E : process_handle.Set(::OpenProcess(kFlags, FALSE, pid));
348 :
349 E : if (!process_handle.IsValid()) {
350 i : DWORD error = ::GetLastError();
351 i : LOG(ERROR) << "Failed to open PID=" << pid << " " << ::common::LogWe(error)
352 : << ".";
353 i : Reset();
354 i : return false;
355 : }
356 :
357 E : process_id = pid;
358 :
359 E : ::GetSystemInfo(&system_info);
360 :
361 : // Get the executable path, command line and environment string.
362 : if (!GetProcessStrings(process_id, process_handle, system_info.dwPageSize,
363 E : &executable_path, &command_line, &environment)) {
364 i : Reset();
365 i : return false;
366 : }
367 :
368 : // Get the operating system and hardware information.
369 E : os_version_info.dwOSVersionInfoSize = sizeof(os_version_info);
370 : if (!::GetVersionEx(
371 E : reinterpret_cast<OSVERSIONINFO*>(&os_version_info))) {
372 i : DWORD error = ::GetLastError();
373 i : LOG(ERROR) << "Failed to get OS version information: "
374 : << ::common::LogWe(error) << ".";
375 i : Reset();
376 i : return false;
377 : }
378 :
379 E : memory_status.dwLength = sizeof(memory_status);
380 E : if (!::GlobalMemoryStatusEx(&memory_status)) {
381 i : DWORD error = ::GetLastError();
382 i : LOG(ERROR) << "Failed to get global memory status: "
383 : << ::common::LogWe(error) << ".";
384 i : Reset();
385 i : return false;
386 : }
387 :
388 : // Get the headers for the running image and use these to populate various
389 : // fields.
390 : IMAGE_NT_HEADERS nt_headers;
391 E : if (!GetProcessNtHeaders(process_id, process_handle, &nt_headers)) {
392 i : Reset();
393 i : return false;
394 : }
395 E : exe_base_address = nt_headers.OptionalHeader.ImageBase;
396 E : exe_image_size = nt_headers.OptionalHeader.SizeOfImage;
397 E : exe_checksum = nt_headers.OptionalHeader.CheckSum;
398 E : exe_time_date_stamp = nt_headers.FileHeader.TimeDateStamp;
399 :
400 E : return true;
401 E : }
402 :
403 : } // namespace service
404 : } // namespace trace
|