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/string_util.h"
  26    :  #include "sawbuck/common/com_utils.h"
  27    :  #include "syzygy/common/align.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: " << com::LogWe(error)
  52    :                 << ".";
  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    :                 << com::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    :                 << ": " << com::LogWe(::LsaNtStatusToWinError(status)) << ".";
  70  i :      return false;
  71    :    }
  72    :  
  73  E :    return true;
  74  E :  }
  75    :  
  76    :  bool ReadEnvironmentString(HANDLE handle,
  77    :                             size_t page_size,
  78    :                             const wchar_t* remote_env_string,
  79  E :                             std::vector<wchar_t>* environment) {
  80  E :    DCHECK_LT(0u, page_size);
  81  E :    DCHECK(common::IsPowerOfTwo(page_size));
  82  E :    DCHECK(remote_env_string != NULL);
  83  E :    DCHECK(environment != NULL);
  84    :  
  85  E :    environment->clear();
  86    :  
  87  E :    std::vector<uint8> vector(page_size);
  88  E :    uint8* buffer = &vector.at(0);
  89  E :    const wchar_t* wbuffer = reinterpret_cast<const wchar_t*>(buffer);
  90    :    const uint8* remote_cursor =
  91  E :        reinterpret_cast<const uint8*>(remote_env_string);
  92    :    const uint8* next_page = reinterpret_cast<const uint8*>(
  93    :        common::AlignUp(reinterpret_cast<size_t>(remote_cursor),
  94  E :                        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    :      if (!::ReadProcessMemory(handle, remote_cursor, buffer, bytes_to_read,
 111  E :                               &bytes_read)) {
 112  i :        DWORD error = ::GetLastError();
 113  i :        LOG(ERROR) << "Failed to read environment string: " << com::LogWe(error)
 114    :                   << ".";
 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 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* peb_base_address = reinterpret_cast<uint8*>(pbi.PebBaseAddress);
 170  E :    uint8* 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    :    if (!::ReadProcessMemory(handle, peb_base_address + kProcessParamOffset,
 176  E :                             &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    :                 << " " << com::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    :    if (!::ReadProcessMemory(handle, user_proc_params + kImagePathNameOffset,
 189  E :                             &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    :                 << ": " << com::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    :    if (!::ReadProcessMemory(handle, string_value[0].Buffer,
 200    :                             WriteInto(&temp_exe_path, num_chars_in_path + 1),
 201  E :                             string_value[0].Length, NULL)) {
 202  i :      DWORD error = ::GetLastError();
 203  i :      LOG(ERROR) << "Failed to read the exe path for PID=" << pid
 204    :                 << ": " << com::LogWe(error) << ".";
 205  i :      return false;
 206    :    }
 207  E :    *exe_path = base::FilePath(temp_exe_path);
 208    :  
 209    :    // Read the command line.
 210  E :    size_t num_chars_in_cmd_line = string_value[1].Length / sizeof(wchar_t);
 211    :    if (!::ReadProcessMemory(handle, string_value[1].Buffer,
 212    :                             WriteInto(cmd_line, num_chars_in_cmd_line + 1),
 213  E :                             string_value[1].Length, NULL)) {
 214  i :      DWORD error = ::GetLastError();
 215  i :      LOG(ERROR) << "Failed to read the command line for PID=" << pid
 216    :                 << ": " << com::LogWe(error) << ".";
 217  i :      return false;
 218    :    }
 219    :  
 220    :    // Get the environment string. Note that this a pointer into a remote process
 221    :    // so we can't directly dereference it. This is not documented directly in
 222    :    // winternl.h, but it is documented here: http://goto.google.com/win-proc-env
 223  E :    const size_t kEnvironmentStringOffset = 0x48;
 224  E :    wchar_t* remote_env_string = NULL;
 225    :    if (!::ReadProcessMemory(handle, user_proc_params + kEnvironmentStringOffset,
 226    :                             &remote_env_string, sizeof(remote_env_string),
 227  E :                             NULL)) {
 228  i :      DWORD error = ::GetLastError();
 229  i :      LOG(ERROR) << "Failed to read environment variable string for PID=" << pid
 230    :                 << ": " << com::LogWe(error) << ".";
 231  i :      return false;
 232    :    }
 233    :  
 234    :    // Finally, read the environment string.
 235    :    if (!ReadEnvironmentString(handle, page_size, remote_env_string,
 236  E :                               environment)) {
 237  i :      return false;
 238    :    }
 239    :  
 240  E :    return true;
 241  E :  }
 242    :  
 243    :  // Gets the NT headers of the running process.
 244    :  bool GetProcessNtHeaders(
 245  E :      uint32 pid, HANDLE handle, IMAGE_NT_HEADERS* nt_headers) {
 246  E :    DCHECK(nt_headers != NULL);
 247  E :    HMODULE module = 0;
 248  E :    DWORD dummy = 0;
 249    :  
 250    :    // The first module returned by the enumeration will be the executable. So
 251    :    // we only need to ask for one HMODULE.
 252  E :    if (!::EnumProcessModules(handle, &module, sizeof(module), &dummy)) {
 253  i :      DWORD error = ::GetLastError();
 254  i :      LOG(ERROR) << "Failed to get module handle for PID=" << pid
 255    :                 << ": " << com::LogWe(error) << ".";
 256  i :      return false;
 257    :    }
 258    :  
 259    :    // We now have enough information get the module info for the executable.
 260  E :    MODULEINFO info = {};
 261  E :    if (!::GetModuleInformation(handle, module, &info, sizeof(info))) {
 262  i :      DWORD error = ::GetLastError();
 263  i :      LOG(ERROR) << "Failed to get module info for PID=" << pid
 264    :                 << ": " << com::LogWe(error) << ".";
 265  i :      return false;
 266    :    }
 267    :  
 268  E :    uint8* base_addr = reinterpret_cast<uint8*>(info.lpBaseOfDll);
 269    :  
 270    :    // Get the DOS header.
 271    :    IMAGE_DOS_HEADER dos_header;
 272  E :    uint8* addr_to_read = base_addr;
 273  E :    SIZE_T bytes_to_read = sizeof(IMAGE_DOS_HEADER);
 274  E :    SIZE_T bytes_read = 0;
 275    :    if (!::ReadProcessMemory(handle, addr_to_read, &dos_header, bytes_to_read,
 276    :                             &bytes_read) ||
 277  E :        bytes_read != bytes_to_read) {
 278  i :      DWORD error = ::GetLastError();
 279  i :      LOG(ERROR) << "Failed to read DOS header for PID=" << pid
 280    :                 << " " << com::LogWe(error) << ".";
 281  i :      return false;
 282    :    }
 283    :  
 284    :    // Get the NT headers.
 285  E :    addr_to_read = base_addr + dos_header.e_lfanew;
 286  E :    bytes_to_read = sizeof(IMAGE_NT_HEADERS);
 287  E :    bytes_read = 0;
 288    :    if (!::ReadProcessMemory(handle, addr_to_read, nt_headers, bytes_to_read,
 289    :                             &bytes_read) ||
 290  E :        bytes_read != bytes_to_read) {
 291  i :      DWORD error = ::GetLastError();
 292  i :      LOG(ERROR) << "Failed to read NT headers for PID=" << pid
 293    :                 << " " << com::LogWe(error) << ".";
 294  i :      return false;
 295    :    }
 296    :  
 297  E :    return true;
 298  E :  }
 299    :  
 300    :  }  // namespace
 301    :  
 302    :  ProcessInfo::ProcessInfo()
 303    :      : process_id(0),
 304    :        exe_base_address(0),
 305    :        exe_image_size(0),
 306    :        exe_checksum(0),
 307  E :        exe_time_date_stamp(0) {
 308  E :    ::memset(&os_version_info, 0, sizeof(os_version_info));
 309  E :    ::memset(&system_info, 0, sizeof(system_info));
 310  E :    ::memset(&memory_status, 0, sizeof(memory_status));
 311  E :  }
 312    :  
 313  E :  ProcessInfo::~ProcessInfo() {
 314  E :  }
 315    :  
 316  E :  void ProcessInfo::Reset() {
 317  E :    process_handle.Close();
 318  E :    process_id = 0;
 319  E :    executable_path.clear();
 320  E :    command_line.clear();
 321  E :    environment.clear();
 322  E :    ::memset(&os_version_info, 0, sizeof(os_version_info));
 323  E :    ::memset(&system_info, 0, sizeof(system_info));
 324  E :    ::memset(&memory_status, 0, sizeof(memory_status));
 325  E :    exe_base_address = 0;
 326  E :    exe_image_size = 0;
 327  E :    exe_checksum = 0;
 328  E :    exe_time_date_stamp = 0;
 329  E :  }
 330    :  
 331  E :  bool ProcessInfo::Initialize(uint32 pid) {
 332    :    // TODO(chrisha): This whole mechanism is racy by its very nature, as it
 333    :    //     reads memory from a remote process that is running, and which may be
 334    :    //     changing the things being read. In practice this has not proved to be
 335    :    //     a problem as we are typically running under the loader lock, but this
 336    :    //     is not true when running instrumented EXEs. Long term it would be good
 337    :    //     to make this run in the instrumented process and have it shuttle the
 338    :    //     data across in the first buffer.
 339    :  
 340    :    // Open the process given by pid. We need a process handle that (1) remains
 341    :    // valid over time (2) lets us query for info about the process, and (3)
 342    :    // allows us to read the command line from the process memory.
 343    :    const DWORD kFlags =
 344  E :        PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
 345    :  
 346  E :    process_handle.Set(::OpenProcess(kFlags, FALSE, pid));
 347    :  
 348  E :    if (!process_handle.IsValid()) {
 349  i :      DWORD error = ::GetLastError();
 350  i :      LOG(ERROR) << "Failed to open PID=" << pid << " " << com::LogWe(error)
 351    :                 << ".";
 352  i :      Reset();
 353  i :      return false;
 354    :    }
 355    :  
 356  E :    process_id = pid;
 357    :  
 358  E :    ::GetSystemInfo(&system_info);
 359    :  
 360    :    // Get the executable path, command line and environment string.
 361    :    if (!GetProcessStrings(process_id, process_handle, system_info.dwPageSize,
 362  E :                           &executable_path, &command_line, &environment)) {
 363  i :      Reset();
 364  i :      return false;
 365    :    }
 366    :  
 367    :    // Get the operating system and hardware information.
 368  E :    os_version_info.dwOSVersionInfoSize = sizeof(os_version_info);
 369    :    if (!::GetVersionEx(
 370  E :        reinterpret_cast<OSVERSIONINFO*>(&os_version_info))) {
 371  i :      DWORD error = ::GetLastError();
 372  i :      LOG(ERROR) << "Failed to get OS version information: "
 373    :                 << com::LogWe(error) << ".";
 374  i :      Reset();
 375  i :      return false;
 376    :    }
 377    :  
 378  E :    memory_status.dwLength = sizeof(memory_status);
 379  E :    if (!::GlobalMemoryStatusEx(&memory_status)) {
 380  i :      DWORD error = ::GetLastError();
 381  i :      LOG(ERROR) << "Failed to get global memory status: "
 382    :                 << com::LogWe(error) << ".";
 383  i :      Reset();
 384  i :      return false;
 385    :    }
 386    :  
 387    :    // Get the headers for the running image and use these to populate various
 388    :    // fields.
 389    :    IMAGE_NT_HEADERS nt_headers;
 390  E :    if (!GetProcessNtHeaders(process_id, process_handle, &nt_headers)) {
 391  i :      Reset();
 392  i :      return false;
 393    :    }
 394  E :    exe_base_address = nt_headers.OptionalHeader.ImageBase;
 395  E :    exe_image_size = nt_headers.OptionalHeader.SizeOfImage;
 396  E :    exe_checksum = nt_headers.OptionalHeader.CheckSum;
 397  E :    exe_time_date_stamp = nt_headers.FileHeader.TimeDateStamp;
 398    :  
 399  E :    return true;
 400  E :  }
 401    :  
 402    :  }  // namespace service
 403    :  }  // namespace trace

Coverage information generated Thu Jul 04 09:34:53 2013.