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/channel_info.h"
18 #include "components/version_info/version_info.h"
22 typedef BOOL (WINAPI
* CloseHandleType
) (HANDLE handle
);
23 CloseHandleType g_close_function
= NULL
;
25 // The entry point for CloseHandle interception. This function notifies the
26 // verifier about the handle that is being closed, and calls the original
28 BOOL WINAPI
CloseHandleHook(HANDLE handle
) {
29 base::win::OnHandleBeingClosed(handle
);
30 return g_close_function(handle
);
33 // Provides a simple way to temporarily change the protection of a memory page.
34 class AutoProtectMemory
{
37 : changed_(false), address_(NULL
), bytes_(0), old_protect_(0) {}
39 ~AutoProtectMemory() {
43 // Grants write access to a given memory range.
44 bool ChangeProtection(void* address
, size_t bytes
);
46 // Restores the original page protection.
47 void RevertProtection();
55 DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory
);
58 bool AutoProtectMemory::ChangeProtection(void* address
, size_t bytes
) {
62 // Change the page protection so that we can write.
63 MEMORY_BASIC_INFORMATION memory_info
;
64 if (!VirtualQuery(address
, &memory_info
, sizeof(memory_info
)))
67 DWORD is_executable
= (PAGE_EXECUTE
| PAGE_EXECUTE_READ
|
68 PAGE_EXECUTE_READWRITE
| PAGE_EXECUTE_WRITECOPY
) &
71 DWORD protect
= is_executable
? PAGE_EXECUTE_READWRITE
: PAGE_READWRITE
;
72 if (!VirtualProtect(address
, bytes
, protect
, &old_protect_
))
81 void AutoProtectMemory::RevertProtection() {
88 VirtualProtect(address_
, bytes_
, old_protect_
, &old_protect_
);
95 // Performs an EAT interception.
96 void EATPatch(HMODULE module
, const char* function_name
,
97 void* new_function
, void** old_function
) {
101 base::win::PEImage
pe(module
);
102 if (!pe
.VerifyMagic())
105 DWORD
* eat_entry
= pe
.GetExportEntry(function_name
);
109 if (!(*old_function
))
110 *old_function
= pe
.RVAToAddr(*eat_entry
);
112 AutoProtectMemory memory
;
113 if (!memory
.ChangeProtection(eat_entry
, sizeof(DWORD
)))
116 // Perform the patch.
117 #pragma warning(push)
118 #pragma warning(disable: 4311)
119 // These casts generate warnings because they are 32 bit specific.
120 *eat_entry
= reinterpret_cast<DWORD
>(new_function
) -
121 reinterpret_cast<DWORD
>(module
);
125 // Keeps track of all the hooks needed to intercept CloseHandle.
126 class CloseHandleHooks
{
128 CloseHandleHooks() {}
129 ~CloseHandleHooks() {}
131 void AddIATPatch(HMODULE module
);
136 std::vector
<base::win::IATPatchFunction
*> hooks_
;
137 DISALLOW_COPY_AND_ASSIGN(CloseHandleHooks
);
139 base::LazyInstance
<CloseHandleHooks
> g_hooks
= LAZY_INSTANCE_INITIALIZER
;
141 void CloseHandleHooks::AddIATPatch(HMODULE module
) {
145 base::win::IATPatchFunction
* patch
= new base::win::IATPatchFunction
;
147 // There is no guarantee that |module| is still loaded at this point.
148 if (patch
->PatchFromModule(module
, "kernel32.dll", "CloseHandle",
153 } __except((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
||
154 GetExceptionCode() == EXCEPTION_GUARD_PAGE
||
155 GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
) ?
156 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
161 hooks_
.push_back(patch
);
162 if (!g_close_function
) {
163 // Things are probably messed up if each intercepted function points to
164 // a different place, but we need only one function to call.
166 reinterpret_cast<CloseHandleType
>(patch
->original_function());
170 void CloseHandleHooks::AddEATPatch() {
171 // An attempt to restore the entry on the table at destruction is not safe.
172 EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle",
173 &CloseHandleHook
, reinterpret_cast<void**>(&g_close_function
));
176 void CloseHandleHooks::Unpatch() {
177 for (std::vector
<base::win::IATPatchFunction
*>::iterator it
= hooks_
.begin();
178 it
!= hooks_
.end(); ++it
) {
185 #if defined(ARCH_CPU_X86_64)
187 #elif defined(NDEBUG)
188 version_info::Channel channel
= chrome::GetChannel();
189 if (channel
== version_info::Channel::CANARY
||
190 channel
== version_info::Channel::DEV
||
191 channel
== version_info::Channel::UNKNOWN
) {
201 void PatchLoadedModules(CloseHandleHooks
* hooks
) {
202 const DWORD kSize
= 256;
204 scoped_ptr
<HMODULE
[]> modules(new HMODULE
[kSize
]);
205 if (!EnumProcessModules(GetCurrentProcess(), modules
.get(),
206 kSize
* sizeof(HMODULE
), &returned
)) {
209 returned
/= sizeof(HMODULE
);
210 returned
= std::min(kSize
, returned
);
212 for (DWORD current
= 0; current
< returned
; current
++) {
213 hooks
->AddIATPatch(modules
[current
]);
219 void InstallCloseHandleHooks() {
221 CloseHandleHooks
* hooks
= g_hooks
.Pointer();
223 // Performing EAT interception first is safer in the presence of other
224 // threads attempting to call CloseHandle.
225 hooks
->AddEATPatch();
226 PatchLoadedModules(hooks
);
230 void RemoveCloseHandleHooks() {
231 // We are partching all loaded modules without forcing them to stay in memory,
232 // removing patches is not safe.