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.
5 #include "chrome/app/close_handle_hook_win.h"
12 #include "base/lazy_instance.h"
13 #include "base/win/iat_patch_function.h"
14 #include "base/win/pe_image.h"
15 #include "base/win/scoped_handle.h"
16 #include "chrome/common/chrome_version_info.h"
20 typedef BOOL (WINAPI
* CloseHandleType
) (HANDLE handle
);
21 CloseHandleType g_close_function
= NULL
;
23 // The entry point for CloseHandle interception. This function notifies the
24 // verifier about the handle that is being closed, and calls the original
26 BOOL WINAPI
CloseHandleHook(HANDLE handle
) {
27 base::win::OnHandleBeingClosed(handle
);
28 return g_close_function(handle
);
31 // Provides a simple way to temporarily change the protection of a memory page.
32 class AutoProtectMemory
{
35 : changed_(false), address_(NULL
), bytes_(0), old_protect_(0) {}
37 ~AutoProtectMemory() {
41 // Grants write access to a given memory range.
42 bool ChangeProtection(void* address
, size_t bytes
);
44 // Restores the original page protection.
45 void RevertProtection();
53 DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory
);
56 bool AutoProtectMemory::ChangeProtection(void* address
, size_t bytes
) {
60 // Change the page protection so that we can write.
61 MEMORY_BASIC_INFORMATION memory_info
;
62 if (!VirtualQuery(address
, &memory_info
, sizeof(memory_info
)))
65 DWORD is_executable
= (PAGE_EXECUTE
| PAGE_EXECUTE_READ
|
66 PAGE_EXECUTE_READWRITE
| PAGE_EXECUTE_WRITECOPY
) &
69 DWORD protect
= is_executable
? PAGE_EXECUTE_READWRITE
: PAGE_READWRITE
;
70 if (!VirtualProtect(address
, bytes
, protect
, &old_protect_
))
79 void AutoProtectMemory::RevertProtection() {
86 VirtualProtect(address_
, bytes_
, old_protect_
, &old_protect_
);
93 // Performs an EAT interception.
94 void EATPatch(HMODULE module
, const char* function_name
,
95 void* new_function
, void** old_function
) {
99 base::win::PEImage
pe(module
);
100 if (!pe
.VerifyMagic())
103 DWORD
* eat_entry
= pe
.GetExportEntry(function_name
);
107 if (!(*old_function
))
108 *old_function
= pe
.RVAToAddr(*eat_entry
);
110 AutoProtectMemory memory
;
111 if (!memory
.ChangeProtection(eat_entry
, sizeof(DWORD
)))
114 // Perform the patch.
115 #pragma warning(push)
116 #pragma warning(disable: 4311)
117 // These casts generate warnings because they are 32 bit specific.
118 *eat_entry
= reinterpret_cast<DWORD
>(new_function
) -
119 reinterpret_cast<DWORD
>(module
);
123 // Keeps track of all the hooks needed to intercept CloseHandle.
124 class CloseHandleHooks
{
126 CloseHandleHooks() {}
127 ~CloseHandleHooks() {}
129 void AddIATPatch(HMODULE module
);
134 std::vector
<base::win::IATPatchFunction
*> hooks_
;
135 DISALLOW_COPY_AND_ASSIGN(CloseHandleHooks
);
137 base::LazyInstance
<CloseHandleHooks
> g_hooks
= LAZY_INSTANCE_INITIALIZER
;
139 void CloseHandleHooks::AddIATPatch(HMODULE module
) {
143 base::win::IATPatchFunction
* patch
= new base::win::IATPatchFunction
;
145 // There is no guarantee that |module| is still loaded at this point.
146 if (patch
->PatchFromModule(module
, "kernel32.dll", "CloseHandle",
151 } __except((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
||
152 GetExceptionCode() == EXCEPTION_GUARD_PAGE
||
153 GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
) ?
154 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
159 hooks_
.push_back(patch
);
160 if (!g_close_function
) {
161 // Things are probably messed up if each intercepted function points to
162 // a different place, but we need only one function to call.
164 reinterpret_cast<CloseHandleType
>(patch
->original_function());
168 void CloseHandleHooks::AddEATPatch() {
169 // An attempt to restore the entry on the table at destruction is not safe.
170 EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle",
171 &CloseHandleHook
, reinterpret_cast<void**>(&g_close_function
));
174 void CloseHandleHooks::Unpatch() {
175 for (std::vector
<base::win::IATPatchFunction
*>::iterator it
= hooks_
.begin();
176 it
!= hooks_
.end(); ++it
) {
183 #if defined(ARCH_CPU_X86_64)
185 #elif defined(NDEBUG)
186 chrome::VersionInfo::Channel channel
= chrome::VersionInfo::GetChannel();
187 if (channel
== chrome::VersionInfo::CHANNEL_CANARY
||
188 channel
== chrome::VersionInfo::CHANNEL_DEV
) {
198 void PatchLoadedModules(CloseHandleHooks
* hooks
) {
199 const DWORD kSize
= 256;
201 scoped_ptr
<HMODULE
[]> modules(new HMODULE
[kSize
]);
202 if (!EnumProcessModules(GetCurrentProcess(), modules
.get(),
203 kSize
* sizeof(HMODULE
), &returned
)) {
206 returned
/= sizeof(HMODULE
);
207 returned
= std::min(kSize
, returned
);
209 for (DWORD current
= 0; current
< returned
; current
++) {
210 hooks
->AddIATPatch(modules
[current
]);
216 void InstallCloseHandleHooks() {
218 CloseHandleHooks
* hooks
= g_hooks
.Pointer();
220 // Performing EAT interception first is safer in the presence of other
221 // threads attempting to call CloseHandle.
222 hooks
->AddEATPatch();
223 PatchLoadedModules(hooks
);
225 base::win::DisableHandleVerifier();
229 void RemoveCloseHandleHooks() {
230 // We are partching all loaded modules without forcing them to stay in memory,
231 // removing patches is not safe.