Coverage for /Syzygy/agent/common/hot_patcher.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
78.3%18230.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/agent/common/hot_patcher.h"
  16    :  
  17    :  #include <windows.h>
  18    :  
  19    :  #include "base/basictypes.h"
  20    :  #include "base/logging.h"
  21    :  #include "syzygy/common/com_utils.h"
  22    :  
  23    :  namespace agent {
  24    :  namespace common {
  25    :  
  26    :  bool HotPatcher::Patch(FunctionPointer function_entry_point,
  27  E :                         FunctionPointer new_entry_point) {
  28    :    // The hot patching starts 5 bytes before the entry point of the function.
  29  E :    uint8* hot_patch_start = reinterpret_cast<uint8*>(function_entry_point) - 5;
  30  E :    const size_t hot_patch_length = 7U;
  31    :  
  32    :    // Change the page protection so that we can write.
  33    :    MEMORY_BASIC_INFORMATION memory_info;
  34  E :    DWORD old_page_protection = 0;
  35    :  
  36  E :    if (!::VirtualQuery(hot_patch_start, &memory_info, sizeof(memory_info))) {
  37  i :      LOG(ERROR) << "Could not execute VirtualQuery(). Error code: "
  38    :                 << ::common::LogWe();
  39  i :      return false;
  40    :    }
  41    :  
  42    :    DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
  43    :                           PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
  44  E :                          memory_info.Protect;
  45    :  
  46    :    if (!::VirtualProtect(reinterpret_cast<LPVOID>(hot_patch_start),
  47    :                          hot_patch_length,
  48    :                          is_executable ? PAGE_EXECUTE_READWRITE :
  49    :                                          PAGE_READWRITE,
  50  E :                          &old_page_protection)) {
  51  i :      LOG(ERROR) << "Could not grant write privileges to page. Error code: "
  52    :                 << ::common::LogWe();
  53  i :      return false;
  54    :    }
  55    :  
  56    :    // The location where we have to write the PC-relative address of the new
  57    :    // entry point.
  58  E :    int32* new_entry_point_place = reinterpret_cast<int32*>(hot_patch_start + 1);
  59    :  
  60    :    // The instrumenter uses 0xCC bytes for block padding. Before writing we check
  61    :    // if the target bytes contain these bytes.
  62  E :    DCHECK_EQ(*hot_patch_start, 0xCC);
  63  E :    DCHECK_EQ(*new_entry_point_place, static_cast<int>(0xCCCCCCCC));
  64    :  
  65    :    // Write the JMP instruction in the padding.
  66    :    // 0xE9 [32-bit PC-relative address]
  67  E :    *hot_patch_start = 0xE9;
  68    :    *new_entry_point_place =
  69  E :        reinterpret_cast<uint8*>(new_entry_point) - hot_patch_start - 5;
  70    :  
  71    :    // This is the location where the short jump overwriting the first two bytes
  72    :    // of the function should be placed.
  73    :    volatile uint16* jump_hook_place =
  74  E :        reinterpret_cast<uint16*>(hot_patch_start + 5);
  75    :  
  76    :    // Writes on x86 architecture are atomic within a cross 4-byte boundary.
  77    :    // NOTE: This can be loosened. Any two bytes starting at an address that meets
  78    :    //     the (address % 4 != 3) condition does not cross 4-byte boundary.
  79  E :    CHECK_EQ(0, reinterpret_cast<int>(jump_hook_place) % 2);
  80    :  
  81    :    // We write the instruction JMP -5 which is represented as: 0xEB 0xF9
  82    :    // We reverse the order of the bytes because of the little endian encoding
  83    :    // to get the final value 0xF9EB.
  84  E :    *jump_hook_place = 0xF9EB;
  85    :  
  86    :    // Restore the old page protection.
  87    :    if (!::VirtualProtect(reinterpret_cast<LPVOID>(hot_patch_start),
  88    :                          hot_patch_length,
  89    :                          old_page_protection,
  90  E :                          &old_page_protection)) {
  91    :      // We do not return false if this fails as the hot patching already
  92    :      // happened.
  93  i :      LOG(ERROR) << "Could not reset old privileges to page. Error code: "
  94    :                 << ::common::LogWe();
  95    :    }
  96    :  
  97  E :    return true;
  98  E :  }
  99    :  
 100    :  }  // namespace common
 101    :  }  // namespace agent

Coverage information generated Thu Jan 14 17:40:38 2016.