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

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
69.3%1311890.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_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

Coverage information generated Fri Jul 29 11:00:21 2016.