Index: chrome_elf/hook_util/hook_util.cc |
diff --git a/chrome_elf/hook_util/hook_util.cc b/chrome_elf/hook_util/hook_util.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b95cf4eff7cdd8a198679f2813626a1546e0f65f |
--- /dev/null |
+++ b/chrome_elf/hook_util/hook_util.cc |
@@ -0,0 +1,313 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "hook_util.h" |
+ |
+#include <versionhelpers.h> // windows.h must be before |
+ |
+#include "base/win/pe_image.h" |
+#include "sandbox/win/src/interception_internal.h" |
+#include "sandbox/win/src/internal_types.h" |
+#include "sandbox/win/src/sandbox_utils.h" |
+#include "sandbox/win/src/service_resolver.h" |
+ |
+namespace { |
+ |
+//------------------------------------------------------------------------------ |
+// Common hooking utility functions - LOCAL |
+//------------------------------------------------------------------------------ |
+ |
+#if !defined(_WIN64) |
+// Whether a process is running under WOW64 (the wrapper that allows 32-bit |
+// processes to run on 64-bit versions of Windows). This will return |
+// WOW64_DISABLED for both "32-bit Chrome on 32-bit Windows" and "64-bit |
+// Chrome on 64-bit Windows". WOW64_UNKNOWN means "an error occurred", e.g. |
+// the process does not have sufficient access rights to determine this. |
+enum WOW64Status { |
+ WOW64_DISABLED, |
+ WOW64_ENABLED, |
+ WOW64_UNKNOWN, |
+}; |
+ |
+WOW64Status GetWOW64StatusForCurrentProcess() { |
+ typedef BOOL(WINAPI * IsWow64ProcessFunc)(HANDLE, PBOOL); |
+ IsWow64ProcessFunc is_wow64_process = reinterpret_cast<IsWow64ProcessFunc>( |
+ GetProcAddress(GetModuleHandle(L"kernel32.dll"), "IsWow64Process")); |
+ if (!is_wow64_process) |
+ return WOW64_DISABLED; |
+ BOOL is_wow64 = FALSE; |
+ if (!is_wow64_process(GetCurrentProcess(), &is_wow64)) |
+ return WOW64_UNKNOWN; |
+ return is_wow64 ? WOW64_ENABLED : WOW64_DISABLED; |
+} |
+#endif // !defined(_WIN64) |
+ |
+// Change the page protections to writable, copy the data, |
+// restore protections. Returns a winerror code. |
+DWORD PatchMem(void* target, void* new_bytes, size_t length) { |
+ if (target == nullptr || new_bytes == nullptr || length == 0) |
+ return ERROR_INVALID_PARAMETER; |
+ |
+ // Preserve executable state. |
+ MEMORY_BASIC_INFORMATION memory_info = {}; |
+ if (!::VirtualQuery(target, &memory_info, sizeof(memory_info))) { |
+ return GetLastError(); |
+ } |
+ |
+ DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ | |
+ PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) & |
+ memory_info.Protect; |
+ |
+ // Make target writeable. |
+ DWORD old_page_protection = 0; |
+ if (!::VirtualProtect(target, length, |
+ is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, |
+ &old_page_protection)) { |
+ return GetLastError(); |
+ } |
+ |
+ // Write the data. |
+ ::memcpy(target, new_bytes, length); |
+ |
+ // Restore old page protection. |
+ if (!::VirtualProtect(target, length, old_page_protection, |
+ &old_page_protection)) { |
+// Yes, this could fail. However, memory was already patched. |
+#ifdef _DEBUG |
+ assert(false); |
+#endif // _DEBUG |
+ } |
+ |
+ return NO_ERROR; |
+} |
+ |
+//------------------------------------------------------------------------------ |
+// Import Address Table hooking support - LOCAL |
+//------------------------------------------------------------------------------ |
+ |
+void* GetIATFunctionPtr(IMAGE_THUNK_DATA* iat_thunk) { |
+ if (iat_thunk == nullptr) |
+ return nullptr; |
+ |
+ // Works around the 64 bit portability warning: |
+ // The Function member inside IMAGE_THUNK_DATA is really a pointer |
+ // to the IAT function. IMAGE_THUNK_DATA correctly maps to IMAGE_THUNK_DATA32 |
+ // or IMAGE_THUNK_DATA64 for correct pointer size. |
+ union FunctionThunk { |
+ IMAGE_THUNK_DATA thunk; |
+ void* pointer; |
+ } iat_function; |
+ |
+ iat_function.thunk = *iat_thunk; |
+ return iat_function.pointer; |
+} |
+ |
+// Used to pass target function information during pe_image enumeration. |
+struct IATHookFunctionInfo { |
+ bool finished_operation; |
+ const char* imported_from_module; |
+ const char* function_name; |
+ void* new_function; |
+ void** old_function; |
+ IMAGE_THUNK_DATA** iat_thunk; |
+ DWORD return_code; |
+}; |
+ |
+// Callback function for pe_image enumeration. This function is called from |
+// within PEImage::EnumOneImportChunk(). |
+// NOTE: Returning true means continue enumerating. False means stop. |
+bool IATFindHookFuncCallback(const base::win::PEImage& image, |
+ const char* module, |
+ DWORD ordinal, |
+ const char* import_name, |
+ DWORD hint, |
+ IMAGE_THUNK_DATA* iat, |
+ void* cookie) { |
+ IATHookFunctionInfo* hook_func_info = |
+ reinterpret_cast<IATHookFunctionInfo*>(cookie); |
+ if (hook_func_info == nullptr) |
+ return false; |
+ |
+ // Check for the right module. |
+ if (module == nullptr || |
+ ::strnicmp(module, hook_func_info->imported_from_module, |
+ ::strlen(module)) != 0) |
+ return true; |
+ |
+ // Check for the right function. |
+ if (import_name == nullptr || |
+ ::strnicmp(import_name, hook_func_info->function_name, |
+ ::strlen(import_name)) != 0) |
+ return true; |
+ |
+ // At this point, the target function was found. Even if something fails now, |
+ // don't do any further enumerating. |
+ hook_func_info->finished_operation = true; |
+ |
+ // This is it. Do the hook! |
+ // 1) Save the old function pointer. |
+ *(hook_func_info->old_function) = GetIATFunctionPtr(iat); |
+ |
+ // 2) Save the IAT thunk. |
+ *(hook_func_info->iat_thunk) = iat; |
+ |
+ // 3) Sanity check the pointer sizes (architectures). |
+ if (sizeof(iat->u1.Function) != sizeof(hook_func_info->new_function)) { |
+ hook_func_info->return_code = ERROR_BAD_ENVIRONMENT; |
+ return false; |
+ } |
+ |
+ // 4) Patch the function pointer. |
+ hook_func_info->return_code = |
+ PatchMem(&(iat->u1.Function), &(hook_func_info->new_function), |
+ sizeof(hook_func_info->new_function)); |
+ |
+ return false; |
+} |
+ |
+// Applies an import-address-table hook. Returns a system winerror.h code. |
+// Call RemoveIATHook() with |new_function|, |old_function| and |iat_thunk| |
+// to remove the hook. |
+DWORD ApplyIATHook(HMODULE module_handle, |
+ const char* imported_from_module, |
+ const char* function_name, |
+ void* new_function, |
+ void** old_function, |
+ IMAGE_THUNK_DATA** iat_thunk) { |
+ base::win::PEImage target_image(module_handle); |
+ if (!target_image.VerifyMagic()) |
+ return ERROR_INVALID_PARAMETER; |
+ |
+ IATHookFunctionInfo hook_info = {false, |
+ imported_from_module, |
+ function_name, |
+ new_function, |
+ old_function, |
+ iat_thunk, |
+ ERROR_GEN_FAILURE}; |
+ |
+ // First go through the IAT. If we don't find the import we are looking |
+ // for in IAT, search delay import table. |
+ target_image.EnumAllImports(IATFindHookFuncCallback, &hook_info); |
+ if (!hook_info.finished_operation) { |
+ target_image.EnumAllDelayImports(IATFindHookFuncCallback, &hook_info); |
+ } |
+ |
+ return hook_info.return_code; |
+} |
+ |
+// Removes an import-address-table hook. Returns a system winerror.h code. |
+DWORD RemoveIATHook(void* intercept_function, |
+ void* original_function, |
+ IMAGE_THUNK_DATA* iat_thunk) { |
+ if (GetIATFunctionPtr(iat_thunk) != intercept_function) { |
+// Someone else has messed with the same target. Cannot unpatch. |
+#ifdef _DEBUG |
+ assert(false); |
+#endif // _DEBUG |
+ return ERROR_INVALID_FUNCTION; |
+ } |
+ |
+ return PatchMem(&(iat_thunk->u1.Function), &original_function, |
+ sizeof(original_function)); |
+} |
+ |
+} // namespace |
+ |
+namespace elf_hook { |
+ |
+//------------------------------------------------------------------------------ |
+// System Service hooking support |
+//------------------------------------------------------------------------------ |
+ |
+sandbox::ServiceResolverThunk* HookSystemService(bool relaxed) { |
+ // Create a thunk via the appropriate ServiceResolver instance. |
+ sandbox::ServiceResolverThunk* thunk = nullptr; |
+ |
+ // No hooking on unsupported OS versions. |
+ if (!::IsWindows7OrGreater()) |
+ return thunk; |
+ |
+ // Pseudo-handle, no need to close. |
+ HANDLE current_process = ::GetCurrentProcess(); |
+ |
+#if defined(_WIN64) |
+ // ServiceResolverThunk can handle all the formats in 64-bit (instead only |
+ // handling one like it does in 32-bit versions). |
+ thunk = new sandbox::ServiceResolverThunk(current_process, relaxed); |
+#else |
+ if (GetWOW64StatusForCurrentProcess() == WOW64_ENABLED) { |
+ if (::IsWindows10OrGreater()) |
+ thunk = new sandbox::Wow64W10ResolverThunk(current_process, relaxed); |
+ else if (::IsWindows8OrGreater()) |
+ thunk = new sandbox::Wow64W8ResolverThunk(current_process, relaxed); |
+ else |
+ thunk = new sandbox::Wow64ResolverThunk(current_process, relaxed); |
+ } else if (::IsWindows8OrGreater()) { |
+ thunk = new sandbox::Win8ResolverThunk(current_process, relaxed); |
+ } else { |
+ thunk = new sandbox::ServiceResolverThunk(current_process, relaxed); |
+ } |
+#endif |
+ |
+ return thunk; |
+} |
+ |
+//------------------------------------------------------------------------------ |
+// Import Address Table hooking support |
+//------------------------------------------------------------------------------ |
+ |
+IATHook::IATHook() |
+ : intercept_function_(nullptr), |
+ original_function_(nullptr), |
+ iat_thunk_(nullptr) {} |
+ |
+IATHook::~IATHook() { |
+ if (intercept_function_ != nullptr) { |
+ if (Unhook() != NO_ERROR) { |
+#ifdef _DEBUG |
+ assert(false); |
+#endif // _DEBUG |
+ } |
+ } |
+} |
+ |
+DWORD IATHook::Hook(HMODULE module, |
+ const char* imported_from_module, |
+ const char* function_name, |
+ void* new_function) { |
robertshield
2016/08/03 04:52:45
It looks like this method shouldn't be called twic
penny
2016/08/04 18:55:35
Done.
|
+ if ((module == 0 || module == INVALID_HANDLE_VALUE) || |
+ imported_from_module == nullptr || function_name == nullptr || |
+ new_function == nullptr) |
+ return ERROR_INVALID_PARAMETER; |
+ |
+ DWORD winerror = ApplyIATHook(module, imported_from_module, function_name, |
+ new_function, &original_function_, &iat_thunk_); |
+ if (winerror == NO_ERROR) { |
+ intercept_function_ = new_function; |
+#ifdef _DEBUG |
+ if (original_function_ == new_function) |
robertshield
2016/08/03 04:52:45
If this is an error condition, please consider add
penny
2016/08/04 18:55:35
I've added a check in IATFindHookFuncCallback() (w
|
+ assert(false); |
+#endif //_DEBUG |
+ } |
+ |
+ return winerror; |
+} |
+ |
+DWORD IATHook::Unhook() { |
+ DWORD winerror = |
+ RemoveIATHook(intercept_function_, original_function_, iat_thunk_); |
+#ifdef _DEBUG |
+ if (winerror != NO_ERROR) |
+ assert(false); |
+#endif //_DEBUG |
+ |
+ intercept_function_ = nullptr; |
+ original_function_ = nullptr; |
+ iat_thunk_ = nullptr; |
+ |
+ return winerror; |
+} |
+ |
+} // namespace elf_hook |