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

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
66.4%1502260.C++source

Line-by-line coverage:

   1    :  // Copyright 2012 Google Inc.
   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    :  
  28    :  // From advapi32.dll, but including ntsecapi.h causes conflicting declarations.
  29    :  extern "C" ULONG NTAPI LsaNtStatusToWinError(__in NTSTATUS status);
  30    :  
  31    :  namespace trace {
  32    :  namespace service {
  33    :  
  34    :  namespace {
  35    :  
  36    :  typedef NTSTATUS (NTAPI *FuncPtrNtQueryInformationProcess)(
  37    :      HANDLE ProcessHandle,
  38    :      DWORD ProcessInformationClass,
  39    :      PVOID ProcessInformation,
  40    :      DWORD ProcessInformationLength,
  41    :      PDWORD ReturnLength);
  42    :  
  43    :  // Helper function to get the basic process info for pid/handle.
  44  E :  bool GetPBI(uint32 pid, HANDLE handle, PROCESS_BASIC_INFORMATION* pbi) {
  45  E :    DCHECK(pbi != NULL);
  46    :  
  47  E :    HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
  48  E :    if (ntdll == NULL) {
  49  i :      DWORD error = ::GetLastError();
  50  i :      LOG(ERROR) << "Failed to get ntdll.dll module handle: " << com::LogWe(error)
  51    :                 << ".";
  52  i :      return false;
  53    :    }
  54    :  
  55    :    FuncPtrNtQueryInformationProcess query_func =
  56    :        reinterpret_cast<FuncPtrNtQueryInformationProcess>(
  57  E :            ::GetProcAddress(ntdll, "NtQueryInformationProcess"));
  58  E :    if (query_func == NULL) {
  59  i :      DWORD error = ::GetLastError();
  60  i :      LOG(ERROR) << "Failed to get NtQueryInformationProcess proc address: "
  61    :                 << com::LogWe(error) << ".";
  62  i :      return false;
  63    :    }
  64    :  
  65  E :    NTSTATUS status = query_func(handle, 0, pbi, sizeof(*pbi), NULL);
  66  E :    if (status != 0) {
  67  i :      LOG(ERROR) << "Failed to query process information for PID=" << pid
  68    :                 << ": " << com::LogWe(::LsaNtStatusToWinError(status)) << ".";
  69  i :      return false;
  70    :    }
  71    :  
  72  E :    return true;
  73  E :  }
  74    :  
  75    :  // Given a process and an address in its internal memory, returns the maximum
  76    :  // number of bytes owned by the process starting at that address. This is done
  77    :  // by looking up how many consecutive pages containing the given address are
  78    :  // allocated by the given process. Returns true on success (with the number of
  79    :  // bytes that can be safely read in @p size), false otherwise (@p size set to
  80    :  // zero).
  81  E :  bool GetMaximumMemorySize(HANDLE process, void* remote_address, size_t* size) {
  82  E :    DCHECK(remote_address != NULL);
  83  E :    DCHECK(size != NULL);
  84    :  
  85  E :    *size = 0;
  86    :  
  87  E :    MEMORY_BASIC_INFORMATION mem_info = {};
  88    :    if (VirtualQueryEx(process, remote_address, &mem_info,
  89  E :                       sizeof(mem_info)) == 0) {
  90  i :      DWORD error = ::GetLastError();
  91  i :      LOG(ERROR) << "VirtualQueryEx failed: " << com::LogWe(error) << ".";
  92  i :      return false;
  93    :    }
  94    :  
  95    :    // If the memory contains code or is not readable return an error.
  96  E :    if (mem_info.Protect == PAGE_NOACCESS || mem_info.Protect == PAGE_EXECUTE) {
  97  i :      LOG(ERROR) << "Address being dereferenced does not contain readable data.";
  98  i :      return false;
  99    :    }
 100    :  
 101    :    // Get the size that may be read after the provided address.
 102    :    size_t region_offset = reinterpret_cast<const uint8*>(remote_address) -
 103  E :        reinterpret_cast<const uint8*>(mem_info.BaseAddress);
 104  E :    *size = mem_info.RegionSize - region_offset;
 105    :  
 106  E :    return true;
 107  E :  }
 108    :  
 109    :  bool ReadEnvironmentString(HANDLE handle,
 110    :                             const wchar_t* remote_env_string,
 111    :                             size_t max_size,
 112  E :                             std::vector<wchar_t>* environment) {
 113  E :    DCHECK(environment != NULL);
 114    :  
 115  E :    environment->clear();
 116    :  
 117    :    const uint8* remote_read_cursor =
 118  E :        reinterpret_cast<const uint8*>(remote_env_string);
 119  E :    std::vector<wchar_t> buffer;
 120    :  
 121  E :    size_t max_elems = max_size / sizeof(buffer[0]);
 122    :  
 123    :    // We use a large buffer to minimize calls to ReadProcessMemory.
 124  E :    size_t buffer_elems = 128 * 1024;
 125  E :    if (buffer_elems < max_elems)
 126  E :      buffer_elems = max_elems;
 127  E :    buffer.resize(buffer_elems);
 128  E :    size_t elems_left = max_elems;
 129    :  
 130  E :    size_t nulls_in_a_row = 0;
 131  E :    while (elems_left > 0) {
 132    :      // Figure out how much data to read in this call.
 133  E :      size_t elems_to_read = buffer.size();
 134  E :      if (elems_to_read > elems_left)
 135  E :        elems_to_read = elems_left;
 136  E :      size_t bytes_to_read = elems_to_read * sizeof(buffer[0]);
 137    :  
 138    :      // Read the next chunk of data.
 139  E :      SIZE_T bytes_read = 0;
 140    :      if (!::ReadProcessMemory(handle, remote_read_cursor, &buffer[0],
 141  E :                               bytes_to_read, &bytes_read)) {
 142  i :        DWORD error = ::GetLastError();
 143    :  
 144    :        // It's possible for us to get a failure with ERROR_PARTIAL_COPY if we're
 145    :        // trying to read pages that are not currently mapped to memory or are
 146    :        // dirty. Since we do get the number of bytes that were successfully read
 147    :        // we can silently ignore this. We'll only bail if we're unable to
 148    :        // advance the read cursor at all.
 149  i :        if (error != ERROR_PARTIAL_COPY) {
 150  i :          LOG(ERROR) << "Unable to read environment string: " << com::LogWe(error)
 151    :                     << ".";
 152  i :          return false;
 153    :        }
 154    :      }
 155  E :      size_t elems_read = bytes_read / sizeof(buffer[0]);
 156  E :      bytes_read = elems_read * sizeof(buffer[0]);
 157    :  
 158    :      // If we got a partial read of zero bytes, we're stuck.
 159  E :      if (elems_read == 0) {
 160  i :        LOG(ERROR) << "Unable to read environment string.";
 161  i :        return false;
 162    :      }
 163    :  
 164  E :      remote_read_cursor += bytes_read;
 165    :  
 166    :      // Scan through the buffer looking for the terminating NULLs.
 167  E :      size_t i = 0;
 168  E :      for (; i < elems_to_read && nulls_in_a_row < 2; ++i) {
 169  E :        if (buffer[i] == 0)
 170  E :          ++nulls_in_a_row;
 171  E :        else
 172  E :          nulls_in_a_row = 0;
 173  E :      }
 174    :  
 175  E :      environment->insert(environment->end(), buffer.begin(), buffer.begin() + i);
 176    :  
 177  E :      if (nulls_in_a_row == 2)
 178  E :        return true;
 179  i :    }
 180    :  
 181  i :    LOG(ERROR) << "The environment appears to be malformed.";
 182    :  
 183  i :    return false;
 184  E :  }
 185    :  
 186    :  // Extract the exe path and command line for the process given by pid/handle.
 187    :  // Note that there are other ways to retrieve the exe path, but since this
 188    :  // function will already be spelunking in the same area (to get the command
 189    :  // line) we just get the exe path while we're there.
 190    :  bool GetProcessStrings(uint32 pid,
 191    :                         HANDLE handle,
 192    :                         FilePath* exe_path,
 193    :                         std::wstring* cmd_line,
 194  E :                         std::vector<wchar_t>* environment) {
 195  E :    DCHECK(exe_path != NULL);
 196  E :    DCHECK(cmd_line != NULL);
 197  E :    DCHECK(environment != NULL);
 198    :  
 199    :    // Fetch the basic process information.
 200  E :    PROCESS_BASIC_INFORMATION pbi = {};
 201  E :    if (!GetPBI(pid, handle, &pbi)) {
 202  i :      return false;
 203    :    }
 204    :  
 205    :    // TODO(rogerm): Validate that the target process has the same bitness as
 206    :    //     the querying process; otherwise, the following won't work.
 207    :  
 208    :    // Setup the variables that we'll use later.
 209  E :    uint8* peb_base_address = reinterpret_cast<uint8*>(pbi.PebBaseAddress);
 210  E :    uint8* user_proc_params = NULL;
 211  E :    UNICODE_STRING string_value[2] = {};
 212    :  
 213    :    // Get the address of the process paramters.
 214  E :    const size_t kProcessParamOffset = FIELD_OFFSET(PEB, ProcessParameters);
 215    :    if (!::ReadProcessMemory(handle, peb_base_address + kProcessParamOffset,
 216    :                             &user_proc_params, sizeof(user_proc_params),
 217  E :                             NULL)) {
 218  i :      DWORD error = ::GetLastError();
 219  i :      LOG(ERROR) << "Failed to read process parameter pointer for PID=" << pid
 220    :                 << " " << com::LogWe(error) << ".";
 221  i :      return false;
 222    :    }
 223    :  
 224    :    // Get the image path name and command line UNICODE_STRING structures.
 225    :    // string_value[0] will be the image path name, and string_value[1] will
 226    :    // be the command line.
 227    :    const size_t kImagePathNameOffset =
 228  E :        FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, ImagePathName);
 229    :    if (!::ReadProcessMemory(handle, user_proc_params + kImagePathNameOffset,
 230  E :                             &string_value[0], sizeof(string_value), NULL)) {
 231  i :      DWORD error = ::GetLastError();
 232  i :      LOG(ERROR) << "Failed to read the process parameters for PID=" << pid
 233    :                 << ": " << com::LogWe(error) << ".";
 234  i :      return false;
 235    :    }
 236    :  
 237    :    // Read the image path name.
 238  E :    std::wstring temp_exe_path;
 239  E :    size_t num_chars_in_path = string_value[0].Length / sizeof(wchar_t);
 240    :    if (!::ReadProcessMemory(handle, string_value[0].Buffer,
 241    :                             WriteInto(&temp_exe_path, num_chars_in_path + 1),
 242  E :                             string_value[0].Length, NULL)) {
 243  i :      DWORD error = ::GetLastError();
 244  i :      LOG(ERROR) << "Failed to read the exe path for PID=" << pid
 245    :                 << ": " << com::LogWe(error) << ".";
 246  i :      return false;
 247    :    }
 248  E :    *exe_path = FilePath(temp_exe_path);
 249    :  
 250    :    // Read the command line.
 251  E :    size_t num_chars_in_cmd_line = string_value[1].Length / sizeof(wchar_t);
 252    :    if (!::ReadProcessMemory(handle, string_value[1].Buffer,
 253    :                             WriteInto(cmd_line, num_chars_in_cmd_line + 1),
 254  E :                             string_value[1].Length, NULL)) {
 255  i :      DWORD error = ::GetLastError();
 256  i :      LOG(ERROR) << "Failed to read the command line for PID=" << pid
 257    :                 << ": " << com::LogWe(error) << ".";
 258  i :      return false;
 259    :    }
 260    :  
 261    :    // Get the environment string. Note that this a pointer into a remote process
 262    :    // so we can't directly dereference it. This is not documented directly in
 263    :    // winternl.h, but it is documented here: http://goto.google.com/win-proc-env
 264  E :    const size_t kEnvironmentStringOffset = 0x48;
 265  E :    wchar_t* remote_env_string = NULL;
 266    :    if (!::ReadProcessMemory(handle, user_proc_params + kEnvironmentStringOffset,
 267    :                             &remote_env_string, sizeof(remote_env_string),
 268  E :                             NULL)) {
 269  i :      DWORD error = ::GetLastError();
 270  i :      LOG(ERROR) << "Failed to read environment variable string for PID=" << pid
 271    :                 << ": " << com::LogWe(error) << ".";
 272  i :      return false;
 273    :    }
 274    :  
 275    :    // Get an upper bound on the size of the environment string. It doesn't have
 276    :    // the size encoded within it directly, and this gives us an upper bound by
 277    :    // determining how much data the remote process owns starting at the given
 278    :    // location.
 279  E :    size_t max_size = 0;
 280  E :    if (!GetMaximumMemorySize(handle, remote_env_string, &max_size))
 281  i :      return false;
 282    :  
 283    :    // Finally, read the environment string.
 284  E :    if (!ReadEnvironmentString(handle, remote_env_string, max_size, environment))
 285  i :      return false;
 286    :  
 287  E :    return true;
 288  E :  }
 289    :  
 290    :  // Gets the NT headers of the running process.
 291    :  bool GetProcessNtHeaders(
 292  E :      uint32 pid, HANDLE handle, IMAGE_NT_HEADERS* nt_headers) {
 293  E :    DCHECK(nt_headers != NULL);
 294  E :    HMODULE module = 0;
 295  E :    DWORD dummy = 0;
 296    :  
 297    :    // The first module returned by the enumeration will be the executable. So
 298    :    // we only need to ask for one HMODULE.
 299  E :    if (!::EnumProcessModules(handle, &module, sizeof(module), &dummy)) {
 300  i :      DWORD error = ::GetLastError();
 301  i :      LOG(ERROR) << "Failed to get module handle for PID=" << pid
 302    :                 << ": " << com::LogWe(error) << ".";
 303  i :      return false;
 304    :    }
 305    :  
 306    :    // We now have enough information get the module info for the executable.
 307  E :    MODULEINFO info = {};
 308  E :    if (!::GetModuleInformation(handle, module, &info, sizeof(info))) {
 309  i :      DWORD error = ::GetLastError();
 310  i :      LOG(ERROR) << "Failed to get module info for PID=" << pid
 311    :                 << ": " << com::LogWe(error) << ".";
 312  i :      return false;
 313    :    }
 314    :  
 315  E :    uint8* base_addr = reinterpret_cast<uint8*>(info.lpBaseOfDll);
 316    :  
 317    :    // Get the DOS header.
 318    :    IMAGE_DOS_HEADER dos_header;
 319  E :    uint8* addr_to_read = base_addr;
 320  E :    SIZE_T bytes_to_read = sizeof(IMAGE_DOS_HEADER);
 321  E :    SIZE_T bytes_read = 0;
 322    :    if (!::ReadProcessMemory(handle, addr_to_read, &dos_header,
 323    :                             bytes_to_read, &bytes_read) ||
 324  E :        bytes_read != bytes_to_read) {
 325  i :      DWORD error = ::GetLastError();
 326  i :      LOG(ERROR) << "Failed to read DOS header for PID=" << pid
 327    :                 << " " << com::LogWe(error) << ".";
 328  i :      return false;
 329    :    }
 330    :  
 331    :    // Get the NT headers.
 332  E :    addr_to_read = base_addr + dos_header.e_lfanew;
 333  E :    bytes_to_read = sizeof(IMAGE_NT_HEADERS);
 334  E :    bytes_read = 0;
 335    :    if (!::ReadProcessMemory(handle, addr_to_read, nt_headers,
 336    :                             bytes_to_read, &bytes_read) ||
 337  E :        bytes_read != bytes_to_read) {
 338  i :      DWORD error = ::GetLastError();
 339  i :      LOG(ERROR) << "Failed to read NT headers for PID=" << pid
 340    :                 << " " << com::LogWe(error) << ".";
 341  i :      return false;
 342    :    }
 343    :  
 344  E :    return true;
 345  E :  }
 346    :  
 347    :  // Gets the executable module information for the process given by pid/handle.
 348    :  bool GetMemoryRange(uint32 pid, HANDLE handle, uint32* base_addr,
 349  E :                      uint32* module_size) {
 350  E :    DCHECK(base_addr != NULL);
 351  E :    DCHECK(module_size != NULL);
 352    :  
 353  E :    HMODULE module = 0;
 354  E :    DWORD dummy = 0;
 355    :  
 356    :    // The first module returned by the enumeration will be the executable. So
 357    :    // we only need to ask for one HMODULE.
 358  E :    if (!::EnumProcessModules(handle, &module, sizeof(module), &dummy)) {
 359  i :      DWORD error = ::GetLastError();
 360  i :      LOG(ERROR) << "Failed to get module handle for PID=" << pid
 361    :                 << ": " << com::LogWe(error) << ".";
 362  i :      return false;
 363    :    }
 364    :  
 365    :    // We now have enough information get the module info for the executable.
 366  E :    MODULEINFO info = {};
 367  E :    if (!::GetModuleInformation(handle, module, &info, sizeof(info))) {
 368  i :      DWORD error = ::GetLastError();
 369  i :      LOG(ERROR) << "Failed to get module info for PID=" << pid
 370    :                 << ": " << com::LogWe(error) << ".";
 371  i :      return false;
 372    :    }
 373    :  
 374  E :    *base_addr = reinterpret_cast<uint32>(info.lpBaseOfDll);
 375  E :    *module_size = info.SizeOfImage;
 376    :  
 377  E :    return true;
 378  E :  }
 379    :  
 380    :  }  // namespace
 381    :  
 382    :  ProcessInfo::ProcessInfo()
 383    :      : process_id(0),
 384    :        exe_base_address(0),
 385    :        exe_image_size(0),
 386    :        exe_checksum(0),
 387  E :        exe_time_date_stamp(0) {
 388  E :    ::memset(&os_version_info, 0, sizeof(os_version_info));
 389  E :    ::memset(&system_info, 0, sizeof(system_info));
 390  E :    ::memset(&memory_status, 0, sizeof(memory_status));
 391  E :  }
 392    :  
 393  E :  ProcessInfo::~ProcessInfo() {
 394  E :  }
 395    :  
 396  E :  void ProcessInfo::Reset() {
 397  E :    process_handle.Close();
 398  E :    process_id = 0;
 399  E :    executable_path.clear();
 400  E :    command_line.clear();
 401  E :    environment.clear();
 402  E :    ::memset(&os_version_info, 0, sizeof(os_version_info));
 403  E :    ::memset(&system_info, 0, sizeof(system_info));
 404  E :    ::memset(&memory_status, 0, sizeof(memory_status));
 405  E :    exe_base_address = 0;
 406  E :    exe_image_size = 0;
 407  E :    exe_checksum = 0;
 408  E :    exe_time_date_stamp = 0;
 409  E :  }
 410    :  
 411  E :  bool ProcessInfo::Initialize(uint32 pid) {
 412    :    // Open the process given by pid. We need a process handle that (1) remains
 413    :    // valid over time (2) lets us query for info about the process, and (3)
 414    :    // allows us to read the command line from the process memory.
 415    :    const DWORD kFlags =
 416  E :        PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
 417  E :    process_handle.Set(::OpenProcess(kFlags, FALSE, pid));
 418  E :    if (!process_handle.IsValid()) {
 419  i :      DWORD error = ::GetLastError();
 420  i :      LOG(ERROR) << "Failed to open PID=" << pid << " " << com::LogWe(error)
 421    :                 << ".";
 422  i :      Reset();
 423  i :      return false;
 424    :    }
 425    :  
 426  E :    process_id = pid;
 427    :  
 428    :    // Get the executable path, command line and environment string.
 429    :    if (!GetProcessStrings(process_id, process_handle,
 430  E :                           &executable_path, &command_line, &environment)) {
 431  i :      Reset();
 432  i :      return false;
 433    :    }
 434    :  
 435    :    // Get the operating system and hardware information.
 436  E :    os_version_info.dwOSVersionInfoSize = sizeof(os_version_info);
 437    :    if (!::GetVersionEx(
 438  E :        reinterpret_cast<OSVERSIONINFO*>(&os_version_info))) {
 439  i :      DWORD error = ::GetLastError();
 440  i :      LOG(ERROR) << "Failed to get OS version information: "
 441    :                 << com::LogWe(error) << ".";
 442  i :      Reset();
 443  i :      return false;
 444    :    }
 445    :  
 446  E :    ::GetSystemInfo(&system_info);
 447    :  
 448  E :    memory_status.dwLength = sizeof(memory_status);
 449  E :    if (!::GlobalMemoryStatusEx(&memory_status)) {
 450  i :      DWORD error = ::GetLastError();
 451  i :      LOG(ERROR) << "Failed to get global memory status: "
 452    :                 << com::LogWe(error) << ".";
 453  i :      Reset();
 454  i :      return false;
 455    :    }
 456    :  
 457    :    // Get the base address and module size.
 458    :    if (!GetMemoryRange(process_id, process_handle,
 459  E :                        &exe_base_address, &exe_image_size)) {
 460  i :      Reset();
 461  i :      return false;
 462    :    }
 463    :  
 464    :    // Get the headers for the running image and use these to populate the
 465    :    // checksum and time-date stamp.
 466    :    IMAGE_NT_HEADERS nt_headers;
 467  E :    if (!GetProcessNtHeaders(process_id, process_handle, &nt_headers)) {
 468  i :      Reset();
 469  i :      return false;
 470    :    }
 471  E :    exe_checksum = nt_headers.OptionalHeader.CheckSum;
 472  E :    exe_time_date_stamp = nt_headers.FileHeader.TimeDateStamp;
 473    :  
 474  E :    return true;
 475  E :  }
 476    :  
 477    :  }  // namespace trace::service
 478    :  }  // namespace trace

Coverage information generated Thu Sep 06 11:30:46 2012.