Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / app / close_handle_hook_win.cc
blobefec307a62a511e6dc57838bddceab0918224d8c
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"
7 #include <Windows.h>
8 #include <psapi.h>
10 #include <vector>
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"
20 namespace {
22 typedef BOOL (WINAPI* CloseHandleType) (HANDLE handle);
24 typedef BOOL (WINAPI* DuplicateHandleType)(HANDLE source_process,
25 HANDLE source_handle,
26 HANDLE target_process,
27 HANDLE* target_handle,
28 DWORD desired_access,
29 BOOL inherit_handle,
30 DWORD options);
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
37 // function.
38 BOOL WINAPI CloseHandleHook(HANDLE handle) {
39 base::win::OnHandleBeingClosed(handle);
40 return g_close_function(handle);
43 BOOL WINAPI DuplicateHandleHook(HANDLE source_process,
44 HANDLE source_handle,
45 HANDLE target_process,
46 HANDLE* target_handle,
47 DWORD desired_access,
48 BOOL inherit_handle,
49 DWORD options) {
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,
57 options);
60 // Provides a simple way to temporarily change the protection of a memory page.
61 class AutoProtectMemory {
62 public:
63 AutoProtectMemory()
64 : changed_(false), address_(NULL), bytes_(0), old_protect_(0) {}
66 ~AutoProtectMemory() {
67 RevertProtection();
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();
76 private:
77 bool changed_;
78 void* address_;
79 size_t bytes_;
80 DWORD old_protect_;
82 DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory);
85 bool AutoProtectMemory::ChangeProtection(void* address, size_t bytes) {
86 DCHECK(!changed_);
87 DCHECK(address);
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)))
92 return false;
94 DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
95 PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
96 memory_info.Protect;
98 DWORD protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
99 if (!VirtualProtect(address, bytes, protect, &old_protect_))
100 return false;
102 changed_ = true;
103 address_ = address;
104 bytes_ = bytes;
105 return true;
108 void AutoProtectMemory::RevertProtection() {
109 if (!changed_)
110 return;
112 DCHECK(address_);
113 DCHECK(bytes_);
115 VirtualProtect(address_, bytes_, old_protect_, &old_protect_);
116 changed_ = false;
117 address_ = NULL;
118 bytes_ = 0;
119 old_protect_ = 0;
122 // Performs an EAT interception.
123 void EATPatch(HMODULE module, const char* function_name,
124 void* new_function, void** old_function) {
125 if (!module)
126 return;
128 base::win::PEImage pe(module);
129 if (!pe.VerifyMagic())
130 return;
132 DWORD* eat_entry = pe.GetExportEntry(function_name);
133 if (!eat_entry)
134 return;
136 if (!(*old_function))
137 *old_function = pe.RVAToAddr(*eat_entry);
139 AutoProtectMemory memory;
140 if (!memory.ChangeProtection(eat_entry, sizeof(DWORD)))
141 return;
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);
149 #pragma warning(pop)
152 // Performs an IAT interception.
153 base::win::IATPatchFunction* IATPatch(HMODULE module, const char* function_name,
154 void* new_function, void** old_function) {
155 if (!module)
156 return NULL;
158 base::win::IATPatchFunction* patch = new base::win::IATPatchFunction;
159 __try {
160 // There is no guarantee that |module| is still loaded at this point.
161 if (patch->PatchFromModule(module, "kernel32.dll", function_name,
162 new_function)) {
163 delete patch;
164 return NULL;
166 } __except((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
167 GetExceptionCode() == EXCEPTION_GUARD_PAGE ||
168 GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR) ?
169 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
170 // Leak the patch.
171 return NULL;
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();
179 return patch;
182 // Keeps track of all the hooks needed to intercept functions which could
183 // possibly close handles.
184 class HandleHooks {
185 public:
186 HandleHooks() {}
187 ~HandleHooks() {}
189 void AddIATPatch(HMODULE module);
190 void AddEATPatch();
191 void Unpatch();
193 private:
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) {
200 if (!module)
201 return;
203 base::win::IATPatchFunction* patch = NULL;
204 patch = IATPatch(module, "CloseHandle", &CloseHandleHook,
205 reinterpret_cast<void**>(&g_close_function));
206 if (!patch)
207 return;
208 hooks_.push_back(patch);
210 patch = IATPatch(module, "DuplicateHandle", &DuplicateHandleHook,
211 reinterpret_cast<void**>(&g_duplicate_function));
212 if (!patch)
213 return;
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) {
229 (*it)->Unpatch();
230 delete *it;
234 bool UseHooks() {
235 #if defined(ARCH_CPU_X86_64)
236 return false;
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 return true;
244 return false;
245 #else // NDEBUG
246 return true;
247 #endif
250 void PatchLoadedModules(HandleHooks* hooks) {
251 const DWORD kSize = 256;
252 DWORD returned;
253 scoped_ptr<HMODULE[]> modules(new HMODULE[kSize]);
254 if (!EnumProcessModules(GetCurrentProcess(), modules.get(),
255 kSize * sizeof(HMODULE), &returned)) {
256 return;
258 returned /= sizeof(HMODULE);
259 returned = std::min(kSize, returned);
261 for (DWORD current = 0; current < returned; current++) {
262 hooks->AddIATPatch(modules[current]);
266 } // namespace
268 void InstallHandleHooks() {
269 if (UseHooks()) {
270 HandleHooks* hooks = g_hooks.Pointer();
272 // Performing EAT interception first is safer in the presence of other
273 // threads attempting to call CloseHandle.
274 hooks->AddEATPatch();
275 PatchLoadedModules(hooks);
276 } else {
277 base::win::DisableHandleVerifier();
281 void RemoveHandleHooks() {
282 // We are partching all loaded modules without forcing them to stay in memory,
283 // removing patches is not safe.