Home | History | Annotate | Download | only in win
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/win/iat_patch_function.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/win/pe_image.h"
      9 
     10 namespace base {
     11 namespace win {
     12 
     13 namespace {
     14 
     15 struct InterceptFunctionInformation {
     16   bool finished_operation;
     17   const char* imported_from_module;
     18   const char* function_name;
     19   void* new_function;
     20   void** old_function;
     21   IMAGE_THUNK_DATA** iat_thunk;
     22   DWORD return_code;
     23 };
     24 
     25 void* GetIATFunction(IMAGE_THUNK_DATA* iat_thunk) {
     26   if (NULL == iat_thunk) {
     27     NOTREACHED();
     28     return NULL;
     29   }
     30 
     31   // Works around the 64 bit portability warning:
     32   // The Function member inside IMAGE_THUNK_DATA is really a pointer
     33   // to the IAT function. IMAGE_THUNK_DATA correctly maps to IMAGE_THUNK_DATA32
     34   // or IMAGE_THUNK_DATA64 for correct pointer size.
     35   union FunctionThunk {
     36     IMAGE_THUNK_DATA thunk;
     37     void* pointer;
     38   } iat_function;
     39 
     40   iat_function.thunk = *iat_thunk;
     41   return iat_function.pointer;
     42 }
     43 
     44 bool InterceptEnumCallback(const base::win::PEImage& image, const char* module,
     45                            DWORD ordinal, const char* name, DWORD hint,
     46                            IMAGE_THUNK_DATA* iat, void* cookie) {
     47   InterceptFunctionInformation* intercept_information =
     48     reinterpret_cast<InterceptFunctionInformation*>(cookie);
     49 
     50   if (NULL == intercept_information) {
     51     NOTREACHED();
     52     return false;
     53   }
     54 
     55   DCHECK(module);
     56 
     57   if ((0 == lstrcmpiA(module, intercept_information->imported_from_module)) &&
     58      (NULL != name) &&
     59      (0 == lstrcmpiA(name, intercept_information->function_name))) {
     60     // Save the old pointer.
     61     if (NULL != intercept_information->old_function) {
     62       *(intercept_information->old_function) = GetIATFunction(iat);
     63     }
     64 
     65     if (NULL != intercept_information->iat_thunk) {
     66       *(intercept_information->iat_thunk) = iat;
     67     }
     68 
     69     // portability check
     70     COMPILE_ASSERT(sizeof(iat->u1.Function) ==
     71       sizeof(intercept_information->new_function), unknown_IAT_thunk_format);
     72 
     73     // Patch the function.
     74     intercept_information->return_code =
     75       ModifyCode(&(iat->u1.Function),
     76                  &(intercept_information->new_function),
     77                  sizeof(intercept_information->new_function));
     78 
     79     // Terminate further enumeration.
     80     intercept_information->finished_operation = true;
     81     return false;
     82   }
     83 
     84   return true;
     85 }
     86 
     87 // Helper to intercept a function in an import table of a specific
     88 // module.
     89 //
     90 // Arguments:
     91 // module_handle          Module to be intercepted
     92 // imported_from_module   Module that exports the symbol
     93 // function_name          Name of the API to be intercepted
     94 // new_function           Interceptor function
     95 // old_function           Receives the original function pointer
     96 // iat_thunk              Receives pointer to IAT_THUNK_DATA
     97 //                        for the API from the import table.
     98 //
     99 // Returns: Returns NO_ERROR on success or Windows error code
    100 //          as defined in winerror.h
    101 DWORD InterceptImportedFunction(HMODULE module_handle,
    102                                 const char* imported_from_module,
    103                                 const char* function_name, void* new_function,
    104                                 void** old_function,
    105                                 IMAGE_THUNK_DATA** iat_thunk) {
    106   if ((NULL == module_handle) || (NULL == imported_from_module) ||
    107      (NULL == function_name) || (NULL == new_function)) {
    108     NOTREACHED();
    109     return ERROR_INVALID_PARAMETER;
    110   }
    111 
    112   base::win::PEImage target_image(module_handle);
    113   if (!target_image.VerifyMagic()) {
    114     NOTREACHED();
    115     return ERROR_INVALID_PARAMETER;
    116   }
    117 
    118   InterceptFunctionInformation intercept_information = {
    119     false,
    120     imported_from_module,
    121     function_name,
    122     new_function,
    123     old_function,
    124     iat_thunk,
    125     ERROR_GEN_FAILURE};
    126 
    127   // First go through the IAT. If we don't find the import we are looking
    128   // for in IAT, search delay import table.
    129   target_image.EnumAllImports(InterceptEnumCallback, &intercept_information);
    130   if (!intercept_information.finished_operation) {
    131     target_image.EnumAllDelayImports(InterceptEnumCallback,
    132                                      &intercept_information);
    133   }
    134 
    135   return intercept_information.return_code;
    136 }
    137 
    138 // Restore intercepted IAT entry with the original function.
    139 //
    140 // Arguments:
    141 // intercept_function     Interceptor function
    142 // original_function      Receives the original function pointer
    143 //
    144 // Returns: Returns NO_ERROR on success or Windows error code
    145 //          as defined in winerror.h
    146 DWORD RestoreImportedFunction(void* intercept_function,
    147                               void* original_function,
    148                               IMAGE_THUNK_DATA* iat_thunk) {
    149   if ((NULL == intercept_function) || (NULL == original_function) ||
    150       (NULL == iat_thunk)) {
    151     NOTREACHED();
    152     return ERROR_INVALID_PARAMETER;
    153   }
    154 
    155   if (GetIATFunction(iat_thunk) != intercept_function) {
    156     // Check if someone else has intercepted on top of us.
    157     // We cannot unpatch in this case, just raise a red flag.
    158     NOTREACHED();
    159     return ERROR_INVALID_FUNCTION;
    160   }
    161 
    162   return ModifyCode(&(iat_thunk->u1.Function),
    163                     &original_function,
    164                     sizeof(original_function));
    165 }
    166 
    167 }  // namespace
    168 
    169 // Change the page protection (of code pages) to writable and copy
    170 // the data at the specified location
    171 //
    172 // Arguments:
    173 // old_code               Target location to copy
    174 // new_code               Source
    175 // length                 Number of bytes to copy
    176 //
    177 // Returns: Windows error code (winerror.h). NO_ERROR if successful
    178 DWORD ModifyCode(void* old_code, void* new_code, int length) {
    179   if ((NULL == old_code) || (NULL == new_code) || (0 == length)) {
    180     NOTREACHED();
    181     return ERROR_INVALID_PARAMETER;
    182   }
    183 
    184   // Change the page protection so that we can write.
    185   MEMORY_BASIC_INFORMATION memory_info;
    186   DWORD error = NO_ERROR;
    187   DWORD old_page_protection = 0;
    188 
    189   if (!VirtualQuery(old_code, &memory_info, sizeof(memory_info))) {
    190     error = GetLastError();
    191     return error;
    192   }
    193 
    194   DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
    195                         PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
    196                         memory_info.Protect;
    197 
    198   if (VirtualProtect(old_code,
    199                      length,
    200                      is_executable ? PAGE_EXECUTE_READWRITE :
    201                                      PAGE_READWRITE,
    202                      &old_page_protection)) {
    203 
    204     // Write the data.
    205     CopyMemory(old_code, new_code, length);
    206 
    207     // Restore the old page protection.
    208     error = ERROR_SUCCESS;
    209     VirtualProtect(old_code,
    210                   length,
    211                   old_page_protection,
    212                   &old_page_protection);
    213   } else {
    214     error = GetLastError();
    215   }
    216 
    217   return error;
    218 }
    219 
    220 IATPatchFunction::IATPatchFunction()
    221     : module_handle_(NULL),
    222       original_function_(NULL),
    223       iat_thunk_(NULL),
    224       intercept_function_(NULL) {
    225 }
    226 
    227 IATPatchFunction::~IATPatchFunction() {
    228   if (NULL != intercept_function_) {
    229     DWORD error = Unpatch();
    230     DCHECK_EQ(static_cast<DWORD>(NO_ERROR), error);
    231   }
    232 }
    233 
    234 DWORD IATPatchFunction::Patch(const wchar_t* module,
    235                               const char* imported_from_module,
    236                               const char* function_name,
    237                               void* new_function) {
    238   DCHECK_EQ(static_cast<void*>(NULL), original_function_);
    239   DCHECK_EQ(static_cast<IMAGE_THUNK_DATA*>(NULL), iat_thunk_);
    240   DCHECK_EQ(static_cast<void*>(NULL), intercept_function_);
    241 
    242   HMODULE module_handle = LoadLibraryW(module);
    243 
    244   if (module_handle == NULL) {
    245     NOTREACHED();
    246     return GetLastError();
    247   }
    248 
    249   DWORD error = InterceptImportedFunction(module_handle,
    250                                           imported_from_module,
    251                                           function_name,
    252                                           new_function,
    253                                           &original_function_,
    254                                           &iat_thunk_);
    255 
    256   if (NO_ERROR == error) {
    257     DCHECK_NE(original_function_, intercept_function_);
    258     module_handle_ = module_handle;
    259     intercept_function_ = new_function;
    260   } else {
    261     FreeLibrary(module_handle);
    262   }
    263 
    264   return error;
    265 }
    266 
    267 DWORD IATPatchFunction::Unpatch() {
    268   DWORD error = RestoreImportedFunction(intercept_function_,
    269                                         original_function_,
    270                                         iat_thunk_);
    271   DCHECK_EQ(static_cast<DWORD>(NO_ERROR), error);
    272 
    273   // Hands off the intercept if we fail to unpatch.
    274   // If IATPatchFunction::Unpatch fails during RestoreImportedFunction
    275   // it means that we cannot safely unpatch the import address table
    276   // patch. In this case its better to be hands off the intercept as
    277   // trying to unpatch again in the destructor of IATPatchFunction is
    278   // not going to be any safer
    279   if (module_handle_)
    280     FreeLibrary(module_handle_);
    281   module_handle_ = NULL;
    282   intercept_function_ = NULL;
    283   original_function_ = NULL;
    284   iat_thunk_ = NULL;
    285 
    286   return error;
    287 }
    288 
    289 void* IATPatchFunction::original_function() const {
    290   DCHECK(is_patched());
    291   return original_function_;
    292 }
    293 
    294 }  // namespace win
    295 }  // namespace base
    296