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/memory/scoped_ptr.h"
14 #include "base/win/iat_patch_function.h"
15 #include "base/win/pe_image.h"
16 #include "base/win/scoped_handle.h"
17 #include "chrome/common/chrome_version_info.h"
21 typedef BOOL (WINAPI
* CloseHandleType
) (HANDLE handle
);
22 CloseHandleType g_close_function
= NULL
;
24 // The entry point for CloseHandle interception. This function notifies the
25 // verifier about the handle that is being closed, and calls the original
27 BOOL WINAPI
CloseHandleHook(HANDLE handle
) {
28 base::win::OnHandleBeingClosed(handle
);
29 return g_close_function(handle
);
32 // Provides a simple way to temporarily change the protection of a memory page.
33 class AutoProtectMemory
{
36 : changed_(false), address_(NULL
), bytes_(0), old_protect_(0) {}
38 ~AutoProtectMemory() {
42 // Grants write access to a given memory range.
43 bool ChangeProtection(void* address
, size_t bytes
);
45 // Restores the original page protection.
46 void RevertProtection();
54 DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory
);
57 bool AutoProtectMemory::ChangeProtection(void* address
, size_t bytes
) {
61 // Change the page protection so that we can write.
62 MEMORY_BASIC_INFORMATION memory_info
;
63 if (!VirtualQuery(address
, &memory_info
, sizeof(memory_info
)))
66 DWORD is_executable
= (PAGE_EXECUTE
| PAGE_EXECUTE_READ
|
67 PAGE_EXECUTE_READWRITE
| PAGE_EXECUTE_WRITECOPY
) &
70 DWORD protect
= is_executable
? PAGE_EXECUTE_READWRITE
: PAGE_READWRITE
;
71 if (!VirtualProtect(address
, bytes
, protect
, &old_protect_
))
80 void AutoProtectMemory::RevertProtection() {
87 VirtualProtect(address_
, bytes_
, old_protect_
, &old_protect_
);
94 // Performs an EAT interception.
95 void EATPatch(HMODULE module
, const char* function_name
,
96 void* new_function
, void** old_function
) {
100 base::win::PEImage
pe(module
);
101 if (!pe
.VerifyMagic())
104 DWORD
* eat_entry
= pe
.GetExportEntry(function_name
);
108 if (!(*old_function
))
109 *old_function
= pe
.RVAToAddr(*eat_entry
);
111 AutoProtectMemory memory
;
112 if (!memory
.ChangeProtection(eat_entry
, sizeof(DWORD
)))
115 // Perform the patch.
116 #pragma warning(push)
117 #pragma warning(disable: 4311)
118 // These casts generate warnings because they are 32 bit specific.
119 *eat_entry
= reinterpret_cast<DWORD
>(new_function
) -
120 reinterpret_cast<DWORD
>(module
);
124 // Keeps track of all the hooks needed to intercept CloseHandle.
125 class CloseHandleHooks
{
127 CloseHandleHooks() {}
128 ~CloseHandleHooks() {}
130 void AddIATPatch(HMODULE module
);
135 std::vector
<base::win::IATPatchFunction
*> hooks_
;
136 DISALLOW_COPY_AND_ASSIGN(CloseHandleHooks
);
138 base::LazyInstance
<CloseHandleHooks
> g_hooks
= LAZY_INSTANCE_INITIALIZER
;
140 void CloseHandleHooks::AddIATPatch(HMODULE module
) {
144 base::win::IATPatchFunction
* patch
= new base::win::IATPatchFunction
;
146 // There is no guarantee that |module| is still loaded at this point.
147 if (patch
->PatchFromModule(module
, "kernel32.dll", "CloseHandle",
152 } __except((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
||
153 GetExceptionCode() == EXCEPTION_GUARD_PAGE
||
154 GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
) ?
155 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
160 hooks_
.push_back(patch
);
161 if (!g_close_function
) {
162 // Things are probably messed up if each intercepted function points to
163 // a different place, but we need only one function to call.
165 reinterpret_cast<CloseHandleType
>(patch
->original_function());
169 void CloseHandleHooks::AddEATPatch() {
170 // An attempt to restore the entry on the table at destruction is not safe.
171 EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle",
172 &CloseHandleHook
, reinterpret_cast<void**>(&g_close_function
));
175 void CloseHandleHooks::Unpatch() {
176 for (std::vector
<base::win::IATPatchFunction
*>::iterator it
= hooks_
.begin();
177 it
!= hooks_
.end(); ++it
) {
184 #if defined(ARCH_CPU_X86_64)
186 #elif defined(NDEBUG)
187 chrome::VersionInfo::Channel channel
= chrome::VersionInfo::GetChannel();
188 if (channel
== chrome::VersionInfo::CHANNEL_CANARY
||
189 channel
== chrome::VersionInfo::CHANNEL_DEV
||
190 channel
== chrome::VersionInfo::CHANNEL_UNKNOWN
) {
200 void PatchLoadedModules(CloseHandleHooks
* hooks
) {
201 const DWORD kSize
= 256;
203 scoped_ptr
<HMODULE
[]> modules(new HMODULE
[kSize
]);
204 if (!EnumProcessModules(GetCurrentProcess(), modules
.get(),
205 kSize
* sizeof(HMODULE
), &returned
)) {
208 returned
/= sizeof(HMODULE
);
209 returned
= std::min(kSize
, returned
);
211 for (DWORD current
= 0; current
< returned
; current
++) {
212 hooks
->AddIATPatch(modules
[current
]);
218 void InstallCloseHandleHooks() {
220 CloseHandleHooks
* hooks
= g_hooks
.Pointer();
222 // Performing EAT interception first is safer in the presence of other
223 // threads attempting to call CloseHandle.
224 hooks
->AddEATPatch();
225 PatchLoadedModules(hooks
);
229 void RemoveCloseHandleHooks() {
230 // We are partching all loaded modules without forcing them to stay in memory,
231 // removing patches is not safe.