Index: chrome/app/close_handle_hook_win.cc |
diff --git a/chrome/app/close_handle_hook_win.cc b/chrome/app/close_handle_hook_win.cc |
index b5b3d857569041789e6e91097f589a6d97255f08..a16f3ebf12bfcbb2db9cac65f5849a35145cbea7 100644 |
--- a/chrome/app/close_handle_hook_win.cc |
+++ b/chrome/app/close_handle_hook_win.cc |
@@ -5,13 +5,13 @@ |
#include "chrome/app/close_handle_hook_win.h" |
#include <Windows.h> |
+#include <psapi.h> |
#include <vector> |
-#include "base/files/file_path.h" |
#include "base/lazy_instance.h" |
-#include "base/strings/string16.h" |
#include "base/win/iat_patch_function.h" |
+#include "base/win/pe_image.h" |
#include "base/win/scoped_handle.h" |
#include "chrome/common/chrome_version_info.h" |
@@ -28,13 +28,106 @@ BOOL WINAPI CloseHandleHook(HANDLE handle) { |
return g_close_function(handle); |
} |
+// Provides a simple way to temporarily change the protection of a memory page. |
+class AutoProtectMemory { |
+ public: |
+ AutoProtectMemory() |
+ : changed_(false), address_(NULL), bytes_(0), old_protect_(0) {} |
+ |
+ ~AutoProtectMemory() { |
+ RevertProtection(); |
+ } |
+ |
+ // Grants write access to a given memory range. |
+ bool ChangeProtection(void* address, size_t bytes); |
+ |
+ // Restores the original page protection. |
+ void RevertProtection(); |
+ |
+ private: |
+ bool changed_; |
+ void* address_; |
+ size_t bytes_; |
+ DWORD old_protect_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory); |
+}; |
+ |
+bool AutoProtectMemory::ChangeProtection(void* address, size_t bytes) { |
+ DCHECK(!changed_); |
+ DCHECK(address); |
+ |
+ // Change the page protection so that we can write. |
+ MEMORY_BASIC_INFORMATION memory_info; |
+ if (!VirtualQuery(address, &memory_info, sizeof(memory_info))) |
+ return false; |
+ |
+ DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ | |
+ PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) & |
+ memory_info.Protect; |
+ |
+ DWORD protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; |
+ if (!VirtualProtect(address, bytes, protect, &old_protect_)) |
+ return false; |
+ |
+ changed_ = true; |
+ address_ = address; |
+ bytes_ = bytes; |
+ return true; |
+} |
+ |
+void AutoProtectMemory::RevertProtection() { |
+ if (!changed_) |
+ return; |
+ |
+ DCHECK(address_); |
+ DCHECK(bytes_); |
+ |
+ VirtualProtect(address_, bytes_, old_protect_, &old_protect_); |
+ changed_ = false; |
+ address_ = NULL; |
+ bytes_ = 0; |
+ old_protect_ = 0; |
+} |
+ |
+// Performs an EAT interception. |
+void EATPatch(HMODULE module, const char* function_name, |
+ void* new_function, void** old_function) { |
+ if (!module) |
+ return; |
+ |
+ base::win::PEImage pe(module); |
+ if (!pe.VerifyMagic()) |
+ return; |
+ |
+ DWORD* eat_entry = pe.GetExportEntry(function_name); |
+ if (!eat_entry) |
+ return; |
+ |
+ if (!(*old_function)) |
+ *old_function = pe.RVAToAddr(*eat_entry); |
+ |
+ AutoProtectMemory memory; |
+ if (!memory.ChangeProtection(eat_entry, sizeof(DWORD))) |
+ return; |
+ |
+ // Perform the patch. |
+#pragma warning(push) |
+#pragma warning(disable: 4311) |
+ // These casts generate warnings because they are 32 bit specific. |
+ *eat_entry = reinterpret_cast<DWORD>(new_function) - |
+ reinterpret_cast<DWORD>(module); |
+#pragma warning(pop) |
+} |
+ |
// Keeps track of all the hooks needed to intercept CloseHandle. |
class CloseHandleHooks { |
public: |
CloseHandleHooks() {} |
~CloseHandleHooks() {} |
- void AddIATPatch(const base::string16& module); |
+ void AddIATPatch(HMODULE module); |
+ void AddEATPatch(); |
void Unpatch(); |
private: |
@@ -43,12 +136,17 @@ class CloseHandleHooks { |
}; |
base::LazyInstance<CloseHandleHooks> g_hooks = LAZY_INSTANCE_INITIALIZER; |
-void CloseHandleHooks::AddIATPatch(const base::string16& module) { |
- if (module.empty()) |
+void CloseHandleHooks::AddIATPatch(HMODULE module) { |
+ if (!module) |
return; |
base::win::IATPatchFunction* patch = new base::win::IATPatchFunction; |
- patch->Patch(module.c_str(), "kernel32.dll", "CloseHandle", CloseHandleHook); |
+ if (patch->PatchFromModule(module, "kernel32.dll", "CloseHandle", |
+ CloseHandleHook)) { |
+ delete patch; |
+ return; |
+ } |
+ |
hooks_.push_back(patch); |
if (!g_close_function) { |
// Things are probably messed up if each intercepted function points to |
@@ -58,14 +156,24 @@ void CloseHandleHooks::AddIATPatch(const base::string16& module) { |
} |
} |
+void CloseHandleHooks::AddEATPatch() { |
+ // An attempt to restore the entry on the table at destruction is not safe. |
+ EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle", |
+ &CloseHandleHook, reinterpret_cast<void**>(&g_close_function)); |
+} |
+ |
void CloseHandleHooks::Unpatch() { |
for (std::vector<base::win::IATPatchFunction*>::iterator it = hooks_.begin(); |
it != hooks_.end(); ++it) { |
(*it)->Unpatch(); |
+ delete *it; |
} |
} |
bool UseHooks() { |
+#if defined(ARCH_CPU_X86_64) |
+ return false; |
+#elif defined(NDEBUG) |
chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); |
if (channel == chrome::VersionInfo::CHANNEL_CANARY || |
channel == chrome::VersionInfo::CHANNEL_DEV) { |
@@ -73,32 +181,25 @@ bool UseHooks() { |
} |
return false; |
+#else // NDEBUG |
+ return true; |
+#endif |
} |
-base::string16 GetModuleName(HMODULE module) { |
- base::string16 name; |
- if (!module) |
- return name; |
- wchar_t buffer[MAX_PATH]; |
- int rv = GetModuleFileName(module, buffer, MAX_PATH); |
- if (rv == MAX_PATH) |
- return name; |
- |
- buffer[MAX_PATH - 1] = L'\0'; |
- name.assign(buffer); |
- base::FilePath path(name); |
- return path.BaseName().AsUTF16Unsafe(); |
-} |
+void PatchLoadedModules(CloseHandleHooks* hooks) { |
+ const DWORD kSize = 256; |
+ DWORD returned; |
+ scoped_ptr<HMODULE[]> modules(new HMODULE[kSize]); |
+ if (!EnumProcessModules(GetCurrentProcess(), modules.get(), |
+ kSize * sizeof(HMODULE), &returned)) { |
+ return; |
+ } |
+ returned /= sizeof(HMODULE); |
+ returned = std::min(kSize, returned); |
-HMODULE GetChromeDLLModule() { |
- HMODULE module; |
- if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | |
- GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, |
- reinterpret_cast<wchar_t*>(&GetChromeDLLModule), |
- &module)) { |
- return NULL; |
+ for (DWORD current = 0; current < returned; current++) { |
+ hooks->AddIATPatch(modules[current]); |
} |
- return module; |
} |
} // namespace |
@@ -106,8 +207,11 @@ HMODULE GetChromeDLLModule() { |
void InstallCloseHandleHooks() { |
if (UseHooks()) { |
CloseHandleHooks* hooks = g_hooks.Pointer(); |
- hooks->AddIATPatch(L"chrome.exe"); |
- hooks->AddIATPatch(GetModuleName(GetChromeDLLModule())); |
+ |
+ // Performing EAT interception first is safer in the presence of other |
+ // threads attempting to call CloseHandle. |
+ hooks->AddEATPatch(); |
+ PatchLoadedModules(hooks); |
} else { |
base::win::DisableHandleVerifier(); |
} |