| OLD | NEW |
| (Empty) |
| 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 HMODULE module_handle = LoadLibraryW(module); | |
| 239 if (module_handle == NULL) { | |
| 240 NOTREACHED(); | |
| 241 return GetLastError(); | |
| 242 } | |
| 243 | |
| 244 DWORD error = PatchFromModule(module_handle, imported_from_module, | |
| 245 function_name, new_function); | |
| 246 if (NO_ERROR == error) { | |
| 247 module_handle_ = module_handle; | |
| 248 } else { | |
| 249 FreeLibrary(module_handle); | |
| 250 } | |
| 251 | |
| 252 return error; | |
| 253 } | |
| 254 | |
| 255 DWORD IATPatchFunction::PatchFromModule(HMODULE module, | |
| 256 const char* imported_from_module, | |
| 257 const char* function_name, | |
| 258 void* new_function) { | |
| 259 DCHECK_EQ(static_cast<void*>(NULL), original_function_); | |
| 260 DCHECK_EQ(static_cast<IMAGE_THUNK_DATA*>(NULL), iat_thunk_); | |
| 261 DCHECK_EQ(static_cast<void*>(NULL), intercept_function_); | |
| 262 DCHECK(module); | |
| 263 | |
| 264 DWORD error = InterceptImportedFunction(module, | |
| 265 imported_from_module, | |
| 266 function_name, | |
| 267 new_function, | |
| 268 &original_function_, | |
| 269 &iat_thunk_); | |
| 270 | |
| 271 if (NO_ERROR == error) { | |
| 272 DCHECK_NE(original_function_, intercept_function_); | |
| 273 intercept_function_ = new_function; | |
| 274 } | |
| 275 | |
| 276 return error; | |
| 277 } | |
| 278 | |
| 279 DWORD IATPatchFunction::Unpatch() { | |
| 280 DWORD error = RestoreImportedFunction(intercept_function_, | |
| 281 original_function_, | |
| 282 iat_thunk_); | |
| 283 DCHECK_EQ(static_cast<DWORD>(NO_ERROR), error); | |
| 284 | |
| 285 // Hands off the intercept if we fail to unpatch. | |
| 286 // If IATPatchFunction::Unpatch fails during RestoreImportedFunction | |
| 287 // it means that we cannot safely unpatch the import address table | |
| 288 // patch. In this case its better to be hands off the intercept as | |
| 289 // trying to unpatch again in the destructor of IATPatchFunction is | |
| 290 // not going to be any safer | |
| 291 if (module_handle_) | |
| 292 FreeLibrary(module_handle_); | |
| 293 module_handle_ = NULL; | |
| 294 intercept_function_ = NULL; | |
| 295 original_function_ = NULL; | |
| 296 iat_thunk_ = NULL; | |
| 297 | |
| 298 return error; | |
| 299 } | |
| 300 | |
| 301 void* IATPatchFunction::original_function() const { | |
| 302 DCHECK(is_patched()); | |
| 303 return original_function_; | |
| 304 } | |
| 305 | |
| 306 } // namespace win | |
| 307 } // namespace base | |
| OLD | NEW |