Coverage for /Syzygy/trace/service/process_info.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
68.6%1271850.C++source

Line-by-line coverage:

   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

Coverage information generated Thu Mar 26 16:15:41 2015.