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
);
24 typedef BOOL (WINAPI
* DuplicateHandleType
)(HANDLE source_process
,
26 HANDLE target_process
,
27 HANDLE
* target_handle
,
32 CloseHandleType g_close_function
= NULL
;
33 DuplicateHandleType g_duplicate_function
= NULL
;
35 // The entry point for CloseHandle interception. This function notifies the
36 // verifier about the handle that is being closed, and calls the original
38 BOOL WINAPI
CloseHandleHook(HANDLE handle
) {
39 base::win::OnHandleBeingClosed(handle
);
40 return g_close_function(handle
);
43 BOOL WINAPI
DuplicateHandleHook(HANDLE source_process
,
45 HANDLE target_process
,
46 HANDLE
* target_handle
,
50 if ((options
& DUPLICATE_CLOSE_SOURCE
) &&
51 (GetProcessId(source_process
) == ::GetCurrentProcessId())) {
52 base::win::OnHandleBeingClosed(source_handle
);
55 return g_duplicate_function(source_process
, source_handle
, target_process
,
56 target_handle
, desired_access
, inherit_handle
,
60 // Provides a simple way to temporarily change the protection of a memory page.
61 class AutoProtectMemory
{
64 : changed_(false), address_(NULL
), bytes_(0), old_protect_(0) {}
66 ~AutoProtectMemory() {
70 // Grants write access to a given memory range.
71 bool ChangeProtection(void* address
, size_t bytes
);
73 // Restores the original page protection.
74 void RevertProtection();
82 DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory
);
85 bool AutoProtectMemory::ChangeProtection(void* address
, size_t bytes
) {
89 // Change the page protection so that we can write.
90 MEMORY_BASIC_INFORMATION memory_info
;
91 if (!VirtualQuery(address
, &memory_info
, sizeof(memory_info
)))
94 DWORD is_executable
= (PAGE_EXECUTE
| PAGE_EXECUTE_READ
|
95 PAGE_EXECUTE_READWRITE
| PAGE_EXECUTE_WRITECOPY
) &
98 DWORD protect
= is_executable
? PAGE_EXECUTE_READWRITE
: PAGE_READWRITE
;
99 if (!VirtualProtect(address
, bytes
, protect
, &old_protect_
))
108 void AutoProtectMemory::RevertProtection() {
115 VirtualProtect(address_
, bytes_
, old_protect_
, &old_protect_
);
122 // Performs an EAT interception.
123 void EATPatch(HMODULE module
, const char* function_name
,
124 void* new_function
, void** old_function
) {
128 base::win::PEImage
pe(module
);
129 if (!pe
.VerifyMagic())
132 DWORD
* eat_entry
= pe
.GetExportEntry(function_name
);
136 if (!(*old_function
))
137 *old_function
= pe
.RVAToAddr(*eat_entry
);
139 AutoProtectMemory memory
;
140 if (!memory
.ChangeProtection(eat_entry
, sizeof(DWORD
)))
143 // Perform the patch.
144 #pragma warning(push)
145 #pragma warning(disable: 4311)
146 // These casts generate warnings because they are 32 bit specific.
147 *eat_entry
= reinterpret_cast<DWORD
>(new_function
) -
148 reinterpret_cast<DWORD
>(module
);
152 // Performs an IAT interception.
153 base::win::IATPatchFunction
* IATPatch(HMODULE module
, const char* function_name
,
154 void* new_function
, void** old_function
) {
158 base::win::IATPatchFunction
* patch
= new base::win::IATPatchFunction
;
160 // There is no guarantee that |module| is still loaded at this point.
161 if (patch
->PatchFromModule(module
, "kernel32.dll", function_name
,
166 } __except((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
||
167 GetExceptionCode() == EXCEPTION_GUARD_PAGE
||
168 GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR
) ?
169 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
174 if (!(*old_function
)) {
175 // Things are probably messed up if each intercepted function points to
176 // a different place, but we need only one function to call.
177 *old_function
= patch
->original_function();
182 // Keeps track of all the hooks needed to intercept functions which could
183 // possibly close handles.
189 void AddIATPatch(HMODULE module
);
194 std::vector
<base::win::IATPatchFunction
*> hooks_
;
195 DISALLOW_COPY_AND_ASSIGN(HandleHooks
);
197 base::LazyInstance
<HandleHooks
> g_hooks
= LAZY_INSTANCE_INITIALIZER
;
199 void HandleHooks::AddIATPatch(HMODULE module
) {
203 base::win::IATPatchFunction
* patch
= NULL
;
204 patch
= IATPatch(module
, "CloseHandle", &CloseHandleHook
,
205 reinterpret_cast<void**>(&g_close_function
));
208 hooks_
.push_back(patch
);
210 patch
= IATPatch(module
, "DuplicateHandle", &DuplicateHandleHook
,
211 reinterpret_cast<void**>(&g_duplicate_function
));
214 hooks_
.push_back(patch
);
217 void HandleHooks::AddEATPatch() {
218 // An attempt to restore the entry on the table at destruction is not safe.
219 EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle",
220 &CloseHandleHook
, reinterpret_cast<void**>(&g_close_function
));
221 EATPatch(GetModuleHandleA("kernel32.dll"), "DuplicateHandle",
222 &DuplicateHandleHook
,
223 reinterpret_cast<void**>(&g_duplicate_function
));
226 void HandleHooks::Unpatch() {
227 for (std::vector
<base::win::IATPatchFunction
*>::iterator it
= hooks_
.begin();
228 it
!= hooks_
.end(); ++it
) {
235 #if defined(ARCH_CPU_X86_64)
237 #elif defined(NDEBUG)
238 version_info::Channel channel
= chrome::GetChannel();
239 if (channel
== version_info::Channel::CANARY
||
240 channel
== version_info::Channel::DEV
||
241 channel
== version_info::Channel::UNKNOWN
) {
251 void PatchLoadedModules(HandleHooks
* hooks
) {
252 const DWORD kSize
= 256;
254 scoped_ptr
<HMODULE
[]> modules(new HMODULE
[kSize
]);
255 if (!EnumProcessModules(GetCurrentProcess(), modules
.get(),
256 kSize
* sizeof(HMODULE
), &returned
)) {
259 returned
/= sizeof(HMODULE
);
260 returned
= std::min(kSize
, returned
);
262 for (DWORD current
= 0; current
< returned
; current
++) {
263 hooks
->AddIATPatch(modules
[current
]);
269 void InstallHandleHooks() {
271 HandleHooks
* hooks
= g_hooks
.Pointer();
273 // Performing EAT interception first is safer in the presence of other
274 // threads attempting to call CloseHandle.
275 hooks
->AddEATPatch();
276 PatchLoadedModules(hooks
);
280 void RemoveHandleHooks() {
281 // We are partching all loaded modules without forcing them to stay in memory,
282 // removing patches is not safe.