Coverage for /Syzygy/kasko/api/internal/crash_key_registration.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
0.0%00164.C++source

Line-by-line coverage:

   1    :  // Copyright 2015 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    :  #include "syzygy/kasko/api/internal/crash_key_registration.h"
  16    :  
  17    :  #include <psapi.h>
  18    :  
  19    :  #include <string.h>
  20    :  
  21    :  #include <cstdint>
  22    :  
  23    :  #include "base/logging.h"
  24    :  #include "base/process/process_handle.h"
  25    :  #include "base/strings/string16.h"
  26    :  #include "syzygy/common/process_utils.h"
  27    :  #include "syzygy/kasko/api/crash_key.h"
  28    :  
  29    :  // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
  30  m :  extern "C" IMAGE_DOS_HEADER __ImageBase;
  31    :  
  32  m :  namespace kasko {
  33  m :  namespace api {
  34  m :  namespace internal {
  35    :  
  36  m :  namespace {
  37    :  
  38    :  // Used to store the CrashKey array address and size in the client process.
  39  m :  struct CrashKeyStorage {
  40  m :    const CrashKey* crash_keys;
  41  m :    size_t crash_key_count;
  42  m :  } g_crash_key_storage = {nullptr, 0};
  43    :  
  44    :  // Returns the image path of |module| in |process|.
  45  m :  base::string16 GetModulePath(HANDLE process, HMODULE module) {
  46  m :    base::char16 path_buffer[MAX_PATH];
  47  m :    DWORD bytes_read = ::GetModuleFileNameEx(process, module, path_buffer,
  48  m :                                             arraysize(path_buffer));
  49  m :    if (bytes_read == 0)
  50  m :      DPLOG(ERROR) << "GetModuleFileNameEx";
  51  m :    else if (bytes_read >= arraysize(path_buffer))
  52  m :      return base::string16();
  53    :  
  54  m :    return path_buffer;
  55  m :  }
  56    :  
  57    :  // Returns the image path of the current module.
  58  m :  base::string16 GetCurrentModulePath() {
  59  m :    return GetModulePath(base::GetCurrentProcessHandle(),
  60  m :                         reinterpret_cast<HMODULE>(&__ImageBase));
  61  m :  }
  62    :  
  63    :  // Returns the linker timestamp of the current module.
  64  m :  DWORD GetCurrentModuleTimestamp() {
  65  m :    uintptr_t image_dos_header_address =
  66  m :        reinterpret_cast<uintptr_t>(&__ImageBase);
  67  m :    IMAGE_DOS_HEADER* image_dos_header =
  68  m :        reinterpret_cast<IMAGE_DOS_HEADER*>(image_dos_header_address);
  69  m :    IMAGE_NT_HEADERS* image_nt_header = reinterpret_cast<IMAGE_NT_HEADERS*>(
  70  m :        image_dos_header_address + image_dos_header->e_lfanew);
  71  m :    return image_nt_header->FileHeader.TimeDateStamp;
  72  m :  }
  73    :  
  74    :  // Returns the size of |module| in |process|.
  75  m :  DWORD GetModuleSize(HANDLE process, HMODULE module) {
  76  m :    MODULEINFO module_info = {0};
  77  m :    if (!::GetModuleInformation(process, module, &module_info,
  78  m :                                sizeof(module_info))) {
  79  m :      DPLOG(ERROR) << "GetModuleInformation";
  80  m :      return 0;
  81  m :    }
  82  m :    DCHECK_NE(0u, module_info.SizeOfImage);
  83  m :    return module_info.SizeOfImage;
  84  m :  }
  85    :  
  86    :  // Returns the size of the current module.
  87  m :  DWORD GetCurrentModuleSize() {
  88  m :    return GetModuleSize(base::GetCurrentProcessHandle(),
  89  m :                         reinterpret_cast<HMODULE>(&__ImageBase));
  90  m :  }
  91    :  
  92    :  // Reads a value of type T from |address| in |process| into |value|. Returns
  93    :  // true if successful.
  94  m :  template <typename T>
  95  m :  bool ReadValueFromOtherProcess(HANDLE process, uintptr_t address, T* value) {
  96  m :    DWORD bytes_count = 0;
  97  m :    if (!::ReadProcessMemory(process, reinterpret_cast<void*>(address), value,
  98  m :                             sizeof(T), &bytes_count)) {
  99  m :      DPLOG(ERROR) << "ReadProcessMemory";
 100  m :      return false;
 101  m :    }
 102  m :    if (bytes_count != sizeof(T))
 103  m :      return false;
 104  m :    return true;
 105  m :  }
 106    :  
 107    :  // Reads the crash keys from another instance of the current module image,
 108    :  // loaded into another process.
 109  m :  bool ReadCrashKeysFromProcessModule(HANDLE process,
 110  m :                                      HMODULE module,
 111  m :                                      std::vector<CrashKey>* crash_keys) {
 112    :    // Calculate the offset of g_crash_key_storage from our base address. It will
 113    :    // be the same in the other instance.
 114  m :    ptrdiff_t storage_offset = reinterpret_cast<uintptr_t>(&g_crash_key_storage) -
 115  m :                               reinterpret_cast<uintptr_t>(&__ImageBase);
 116    :  
 117    :    // Calculate the virtual address of g_crash_key_storage in the other instance.
 118  m :    uintptr_t storage_address =
 119  m :        storage_offset + reinterpret_cast<uintptr_t>(module);
 120    :  
 121    :    // Read the CrashKeyStorage structure, which contains the address and size of
 122    :    // a CrashKey array in the other process.
 123  m :    CrashKeyStorage crash_key_storage = {0};
 124  m :    if (!ReadValueFromOtherProcess(process, storage_address, &crash_key_storage))
 125  m :      return false;
 126    :  
 127    :    // Prepare a buffer and read the CrashKey array into it.
 128  m :    crash_keys->resize(crash_key_storage.crash_key_count);
 129  m :    DWORD bytes_count = 0;
 130  m :    if (!::ReadProcessMemory(
 131  m :            process, crash_key_storage.crash_keys, crash_keys->data(),
 132  m :            sizeof(CrashKey) * crash_keys->size(), &bytes_count)) {
 133  m :      DPLOG(ERROR) << "ReadProcessMemory";
 134  m :      crash_keys->clear();
 135  m :      return false;
 136  m :    }
 137  m :    if (bytes_count != sizeof(CrashKey) * crash_keys->size()) {
 138  m :      crash_keys->clear();
 139  m :      return false;
 140  m :    }
 141    :  
 142    :    // Validate the CrashKey array that we read. If any of the names or values is
 143    :    // not properly terminated, fail the entire operation.
 144  m :    for (const auto& crash_key : *crash_keys) {
 145  m :      if (::wcsnlen(crash_key.name, CrashKey::kNameMaxLength) ==
 146  m :          CrashKey::kNameMaxLength) {
 147  m :        crash_keys->clear();
 148  m :        return false;
 149  m :      }
 150  m :      if (::wcsnlen(crash_key.value, CrashKey::kValueMaxLength) ==
 151  m :          CrashKey::kValueMaxLength) {
 152  m :        crash_keys->clear();
 153  m :        return false;
 154  m :      }
 155  m :    }
 156  m :    return true;
 157  m :  }
 158    :  
 159    :  // Retrieves the linker timestamp from a module loaded into another process.
 160  m :  DWORD GetOtherModuleTimestamp(HANDLE process, HANDLE module) {
 161    :    // Read the relative address of the NT_IMAGE_HEADERS structure from the
 162    :    // IMAGE_DOS_HEADER (which is located at the module's base address).
 163  m :    decltype(IMAGE_DOS_HEADER::e_lfanew) nt_header_offset = 0;
 164  m :    if (!ReadValueFromOtherProcess(process,
 165  m :                                   reinterpret_cast<uintptr_t>(module) +
 166  m :                                       offsetof(IMAGE_DOS_HEADER, e_lfanew),
 167  m :                                   &nt_header_offset)) {
 168  m :      return 0;
 169  m :    }
 170  m :    if (nt_header_offset == 0)
 171  m :      return 0;
 172    :  
 173    :    // Calculate the address of the timestamp, which is stored in
 174    :    // image_nt_header.FileHeader.TimeDateStamp.
 175  m :    uintptr_t image_nt_header_address =
 176  m :        reinterpret_cast<uintptr_t>(module) + nt_header_offset;
 177  m :    uintptr_t image_file_header_address =
 178  m :        image_nt_header_address + offsetof(IMAGE_NT_HEADERS, FileHeader);
 179  m :    uintptr_t time_date_stamp_address =
 180  m :        image_file_header_address + offsetof(IMAGE_FILE_HEADER, TimeDateStamp);
 181    :  
 182    :    // Read the value of the timestamp.
 183  m :    decltype(IMAGE_FILE_HEADER::TimeDateStamp) time_date_stamp = 0;
 184  m :    if (!ReadValueFromOtherProcess(process, time_date_stamp_address,
 185  m :                                   &time_date_stamp)) {
 186  m :      return 0;
 187  m :    }
 188    :  
 189  m :    return time_date_stamp;
 190  m :  }
 191    :  
 192    :  // Reads a fingerprint of the current module, and provides a method to compare
 193    :  // that fingerprint to a module in another process.
 194  m :  class CurrentModuleMatcher {
 195  m :   public:
 196  m :    CurrentModuleMatcher()
 197  m :        : path_(GetCurrentModulePath()),
 198  m :          timestamp_(GetCurrentModuleTimestamp()),
 199  m :          size_(GetCurrentModuleSize()) {}
 200    :  
 201    :    // Returns true if the specifed |module| in |process| appears to be the same
 202    :    // image as the current module.
 203  m :    bool Matches(HANDLE process, HMODULE module) {
 204    :      // Give up now if we failed to read any of our own identifying values. As a
 205    :      // result, we know we will fail later if we fail to read one of the other
 206    :      // module's values.
 207  m :      if (path_.empty() || timestamp_ == 0 || size_ == 0)
 208  m :        return false;
 209    :  
 210  m :      base::string16 other_path = GetModulePath(process, module);
 211  m :      if (other_path != path_)
 212  m :        return false;
 213    :  
 214  m :      DWORD other_size = GetModuleSize(process, module);
 215  m :      if (other_size != size_)
 216  m :        return false;
 217    :  
 218  m :      DWORD other_timestamp = GetOtherModuleTimestamp(process, module);
 219  m :      if (other_timestamp != timestamp_)
 220  m :        return false;
 221    :  
 222  m :      return true;
 223  m :    }
 224    :  
 225  m :   private:
 226  m :    base::string16 path_;
 227  m :    DWORD timestamp_;
 228  m :    DWORD size_;
 229  m :  };
 230    :  
 231  m :  }  // namespace
 232    :  
 233  m :  void RegisterCrashKeys(const CrashKey* crash_keys, size_t count) {
 234  m :    DCHECK(!g_crash_key_storage.crash_keys);
 235  m :    g_crash_key_storage.crash_keys = crash_keys;
 236  m :    DCHECK_EQ(0u, g_crash_key_storage.crash_key_count);
 237  m :    g_crash_key_storage.crash_key_count = count;
 238  m :  }
 239    :  
 240  m :  bool ReadCrashKeysFromProcess(HANDLE process,
 241  m :                                std::vector<CrashKey>* crash_keys) {
 242  m :    ::common::ModuleVector modules;
 243  m :    ::common::GetProcessModules(process, &modules);
 244    :  
 245  m :    CurrentModuleMatcher module_matcher;
 246    :  
 247  m :    for (const auto& module : modules) {
 248  m :      if (module_matcher.Matches(process, module))
 249  m :        return ReadCrashKeysFromProcessModule(process, module, crash_keys);
 250  m :    }
 251  m :    return false;
 252  m :  }
 253    :  
 254  m :  }  // namespace internal
 255  m :  }  // namespace api
 256  m :  }  // namespace kasko

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