Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 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 "hook_util.h" | |
| 6 | |
| 7 #include <versionhelpers.h> // windows.h must be before | |
| 8 | |
| 9 #include "base/win/pe_image.h" | |
| 10 #include "sandbox/win/src/interception_internal.h" | |
| 11 #include "sandbox/win/src/internal_types.h" | |
| 12 #include "sandbox/win/src/sandbox_utils.h" | |
| 13 #include "sandbox/win/src/service_resolver.h" | |
| 14 | |
| 15 namespace { | |
| 16 | |
| 17 //------------------------------------------------------------------------------ | |
| 18 // Common hooking utility functions - LOCAL | |
| 19 //------------------------------------------------------------------------------ | |
| 20 | |
| 21 #if !defined(_WIN64) | |
| 22 // Whether a process is running under WOW64 (the wrapper that allows 32-bit | |
| 23 // processes to run on 64-bit versions of Windows). This will return | |
| 24 // WOW64_DISABLED for both "32-bit Chrome on 32-bit Windows" and "64-bit | |
| 25 // Chrome on 64-bit Windows". WOW64_UNKNOWN means "an error occurred", e.g. | |
| 26 // the process does not have sufficient access rights to determine this. | |
| 27 enum WOW64Status { | |
| 28 WOW64_DISABLED, | |
| 29 WOW64_ENABLED, | |
| 30 WOW64_UNKNOWN, | |
| 31 }; | |
| 32 | |
| 33 WOW64Status GetWOW64StatusForCurrentProcess() { | |
| 34 typedef BOOL(WINAPI * IsWow64ProcessFunc)(HANDLE, PBOOL); | |
| 35 IsWow64ProcessFunc is_wow64_process = reinterpret_cast<IsWow64ProcessFunc>( | |
| 36 GetProcAddress(GetModuleHandle(L"kernel32.dll"), "IsWow64Process")); | |
| 37 if (!is_wow64_process) | |
| 38 return WOW64_DISABLED; | |
| 39 BOOL is_wow64 = FALSE; | |
| 40 if (!is_wow64_process(GetCurrentProcess(), &is_wow64)) | |
| 41 return WOW64_UNKNOWN; | |
| 42 return is_wow64 ? WOW64_ENABLED : WOW64_DISABLED; | |
| 43 } | |
| 44 #endif // !defined(_WIN64) | |
| 45 | |
| 46 // Change the page protections to writable, copy the data, | |
| 47 // restore protections. Returns a winerror code. | |
| 48 DWORD PatchMem(void* target, void* new_bytes, size_t length) { | |
| 49 if (target == nullptr || new_bytes == nullptr || length == 0) | |
| 50 return ERROR_INVALID_PARAMETER; | |
| 51 | |
| 52 // Preserve executable state. | |
| 53 MEMORY_BASIC_INFORMATION memory_info = {}; | |
| 54 if (!::VirtualQuery(target, &memory_info, sizeof(memory_info))) { | |
| 55 return GetLastError(); | |
| 56 } | |
| 57 | |
| 58 DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ | | |
| 59 PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) & | |
| 60 memory_info.Protect; | |
| 61 | |
| 62 // Make target writeable. | |
| 63 DWORD old_page_protection = 0; | |
| 64 if (!::VirtualProtect(target, length, | |
| 65 is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, | |
| 66 &old_page_protection)) { | |
| 67 return GetLastError(); | |
| 68 } | |
| 69 | |
| 70 // Write the data. | |
| 71 ::memcpy(target, new_bytes, length); | |
| 72 | |
| 73 // Restore old page protection. | |
| 74 if (!::VirtualProtect(target, length, old_page_protection, | |
| 75 &old_page_protection)) { | |
| 76 // Yes, this could fail. However, memory was already patched. | |
| 77 #ifdef _DEBUG | |
| 78 assert(false); | |
| 79 #endif // _DEBUG | |
| 80 } | |
| 81 | |
| 82 return NO_ERROR; | |
| 83 } | |
| 84 | |
| 85 //------------------------------------------------------------------------------ | |
| 86 // Import Address Table hooking support - LOCAL | |
| 87 //------------------------------------------------------------------------------ | |
| 88 | |
| 89 void* GetIATFunctionPtr(IMAGE_THUNK_DATA* iat_thunk) { | |
| 90 if (iat_thunk == nullptr) | |
| 91 return nullptr; | |
| 92 | |
| 93 // Works around the 64 bit portability warning: | |
| 94 // The Function member inside IMAGE_THUNK_DATA is really a pointer | |
| 95 // to the IAT function. IMAGE_THUNK_DATA correctly maps to IMAGE_THUNK_DATA32 | |
| 96 // or IMAGE_THUNK_DATA64 for correct pointer size. | |
| 97 union FunctionThunk { | |
| 98 IMAGE_THUNK_DATA thunk; | |
| 99 void* pointer; | |
| 100 } iat_function; | |
| 101 | |
| 102 iat_function.thunk = *iat_thunk; | |
| 103 return iat_function.pointer; | |
| 104 } | |
| 105 | |
| 106 // Used to pass target function information during pe_image enumeration. | |
| 107 struct IATHookFunctionInfo { | |
| 108 bool finished_operation; | |
| 109 const char* imported_from_module; | |
| 110 const char* function_name; | |
| 111 void* new_function; | |
| 112 void** old_function; | |
| 113 IMAGE_THUNK_DATA** iat_thunk; | |
| 114 DWORD return_code; | |
| 115 }; | |
| 116 | |
| 117 // Callback function for pe_image enumeration. This function is called from | |
| 118 // within PEImage::EnumOneImportChunk(). | |
| 119 // NOTE: Returning true means continue enumerating. False means stop. | |
| 120 bool IATFindHookFuncCallback(const base::win::PEImage& image, | |
| 121 const char* module, | |
| 122 DWORD ordinal, | |
| 123 const char* import_name, | |
| 124 DWORD hint, | |
| 125 IMAGE_THUNK_DATA* iat, | |
| 126 void* cookie) { | |
| 127 IATHookFunctionInfo* hook_func_info = | |
| 128 reinterpret_cast<IATHookFunctionInfo*>(cookie); | |
| 129 if (hook_func_info == nullptr) | |
| 130 return false; | |
| 131 | |
| 132 // Check for the right module. | |
| 133 if (module == nullptr || | |
| 134 ::strnicmp(module, hook_func_info->imported_from_module, | |
| 135 ::strlen(module)) != 0) | |
| 136 return true; | |
| 137 | |
| 138 // Check for the right function. | |
| 139 if (import_name == nullptr || | |
| 140 ::strnicmp(import_name, hook_func_info->function_name, | |
| 141 ::strlen(import_name)) != 0) | |
| 142 return true; | |
| 143 | |
| 144 // At this point, the target function was found. Even if something fails now, | |
| 145 // don't do any further enumerating. | |
| 146 hook_func_info->finished_operation = true; | |
| 147 | |
| 148 // This is it. Do the hook! | |
| 149 // 1) Save the old function pointer. | |
| 150 *(hook_func_info->old_function) = GetIATFunctionPtr(iat); | |
| 151 | |
| 152 // 2) Save the IAT thunk. | |
| 153 *(hook_func_info->iat_thunk) = iat; | |
| 154 | |
| 155 // 3) Sanity check the pointer sizes (architectures). | |
| 156 if (sizeof(iat->u1.Function) != sizeof(hook_func_info->new_function)) { | |
| 157 hook_func_info->return_code = ERROR_BAD_ENVIRONMENT; | |
| 158 return false; | |
| 159 } | |
| 160 | |
| 161 // 4) Patch the function pointer. | |
| 162 hook_func_info->return_code = | |
| 163 PatchMem(&(iat->u1.Function), &(hook_func_info->new_function), | |
| 164 sizeof(hook_func_info->new_function)); | |
| 165 | |
| 166 return false; | |
| 167 } | |
| 168 | |
| 169 DWORD ApplyIATHook(HMODULE module_handle, | |
|
robertshield
2016/08/02 02:38:41
Please document this function Are the return value
penny
2016/08/02 21:07:43
Done.
| |
| 170 const char* imported_from_module, | |
| 171 const char* function_name, | |
| 172 void* new_function, | |
| 173 void** old_function, | |
| 174 IMAGE_THUNK_DATA** iat_thunk) { | |
| 175 base::win::PEImage target_image(module_handle); | |
| 176 if (!target_image.VerifyMagic()) | |
| 177 return 1; | |
|
robertshield
2016/08/02 02:38:41
Along the same lines, should this be ERROR_INVALID
penny
2016/08/02 21:07:43
Done.
| |
| 178 | |
| 179 IATHookFunctionInfo hook_info = {false, | |
| 180 imported_from_module, | |
| 181 function_name, | |
| 182 new_function, | |
| 183 old_function, | |
| 184 iat_thunk, | |
| 185 ERROR_GEN_FAILURE}; | |
| 186 | |
| 187 // First go through the IAT. If we don't find the import we are looking | |
| 188 // for in IAT, search delay import table. | |
| 189 target_image.EnumAllImports(IATFindHookFuncCallback, &hook_info); | |
| 190 if (!hook_info.finished_operation) { | |
| 191 target_image.EnumAllDelayImports(IATFindHookFuncCallback, &hook_info); | |
| 192 } | |
| 193 | |
| 194 return hook_info.return_code; | |
| 195 } | |
| 196 | |
| 197 DWORD RemoveIATHook(void* intercept_function, | |
| 198 void* original_function, | |
| 199 IMAGE_THUNK_DATA* iat_thunk) { | |
| 200 if (GetIATFunctionPtr(iat_thunk) != intercept_function) { | |
| 201 // Someone else has messed with the same target. Cannot unpatch. | |
| 202 #ifdef _DEBUG | |
| 203 assert(false); | |
| 204 #endif // _DEBUG | |
| 205 return ERROR_INVALID_FUNCTION; | |
| 206 } | |
| 207 | |
| 208 return PatchMem(&(iat_thunk->u1.Function), &original_function, | |
| 209 sizeof(original_function)); | |
| 210 } | |
| 211 | |
| 212 } // namespace | |
| 213 | |
| 214 namespace elf_hook { | |
| 215 | |
| 216 //------------------------------------------------------------------------------ | |
| 217 // System Service hooking support | |
| 218 //------------------------------------------------------------------------------ | |
| 219 | |
| 220 sandbox::ServiceResolverThunk* HookSystemService(bool relaxed) { | |
| 221 // Create a thunk via the appropriate ServiceResolver instance. | |
| 222 sandbox::ServiceResolverThunk* thunk = nullptr; | |
| 223 | |
| 224 // No hooking unsupported OS versions. | |
|
robertshield
2016/08/02 02:38:41
nit: No hooking _on_ unsupported
penny
2016/08/02 21:07:43
Well, technically we're hooking the operating syst
robertshield
2016/08/03 04:52:45
Potato, po-tah-to I guess, 'twas merely a nit :-)
| |
| 225 if (!::IsWindows7OrGreater()) | |
| 226 return thunk; | |
| 227 | |
| 228 // Pseudo-handle, no need to close. | |
| 229 HANDLE current_process = ::GetCurrentProcess(); | |
| 230 | |
| 231 #if defined(_WIN64) | |
| 232 // ServiceResolverThunk can handle all the formats in 64-bit (instead only | |
| 233 // handling one like it does in 32-bit versions). | |
| 234 thunk = new sandbox::ServiceResolverThunk(current_process, relaxed); | |
| 235 #else | |
| 236 if (GetWOW64StatusForCurrentProcess() == WOW64_ENABLED) { | |
| 237 if (::IsWindows10OrGreater()) | |
| 238 thunk = new sandbox::Wow64W10ResolverThunk(current_process, relaxed); | |
| 239 else if (::IsWindows8OrGreater()) | |
| 240 thunk = new sandbox::Wow64W8ResolverThunk(current_process, relaxed); | |
| 241 else | |
| 242 thunk = new sandbox::Wow64ResolverThunk(current_process, relaxed); | |
| 243 } else if (::IsWindows8OrGreater()) { | |
| 244 thunk = new sandbox::Win8ResolverThunk(current_process, relaxed); | |
| 245 } else { | |
| 246 thunk = new sandbox::ServiceResolverThunk(current_process, relaxed); | |
| 247 } | |
| 248 #endif | |
| 249 | |
| 250 return thunk; | |
| 251 } | |
| 252 | |
| 253 //------------------------------------------------------------------------------ | |
| 254 // Import Address Table hooking support | |
| 255 //------------------------------------------------------------------------------ | |
| 256 | |
| 257 IATHook::IATHook() | |
| 258 : intercept_function_(nullptr), | |
| 259 original_function_(nullptr), | |
| 260 iat_thunk_(nullptr) {} | |
| 261 | |
| 262 IATHook::~IATHook() { | |
| 263 if (intercept_function_ != nullptr) { | |
| 264 if (Unhook() != NO_ERROR) { | |
| 265 #ifdef _DEBUG | |
| 266 assert(false); | |
| 267 #endif // _DEBUG | |
| 268 } | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 DWORD IATHook::Hook(HMODULE module, | |
| 273 const char* imported_from_module, | |
| 274 const char* function_name, | |
| 275 void* new_function) { | |
| 276 if ((module == 0 || module == INVALID_HANDLE_VALUE) || | |
| 277 imported_from_module == nullptr || function_name == nullptr || | |
| 278 new_function == nullptr) | |
| 279 return ERROR_INVALID_PARAMETER; | |
| 280 | |
| 281 DWORD winerror = ApplyIATHook(module, imported_from_module, function_name, | |
| 282 new_function, &original_function_, &iat_thunk_); | |
| 283 if (winerror == NO_ERROR) { | |
| 284 intercept_function_ = new_function; | |
| 285 #ifdef _DEBUG | |
| 286 if (original_function_ == new_function) | |
| 287 assert(false); | |
| 288 #endif //_DEBUG | |
| 289 } | |
| 290 | |
| 291 return winerror; | |
| 292 } | |
| 293 | |
| 294 DWORD IATHook::Unhook() { | |
| 295 DWORD winerror = | |
| 296 RemoveIATHook(intercept_function_, original_function_, iat_thunk_); | |
| 297 #ifdef _DEBUG | |
| 298 if (winerror != NO_ERROR) | |
| 299 assert(false); | |
| 300 #endif //_DEBUG | |
| 301 | |
| 302 intercept_function_ = nullptr; | |
| 303 original_function_ = nullptr; | |
| 304 iat_thunk_ = nullptr; | |
| 305 | |
| 306 return winerror; | |
| 307 } | |
| 308 | |
| 309 } // namespace elf_hook | |
| OLD | NEW |