Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(383)

Side by Side Diff: chrome/app/close_handle_hook_win.cc

Issue 1549633005: Move CloseHandle hook into base/debug. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/app/close_handle_hook_win.h ('k') | chrome/chrome_dll.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "chrome/app/close_handle_hook_win.h"
6
7 #include <Windows.h>
8 #include <psapi.h>
9 #include <stddef.h>
10
11 #include <algorithm>
12 #include <vector>
13
14 #include "base/lazy_instance.h"
15 #include "base/macros.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/win/iat_patch_function.h"
18 #include "base/win/pe_image.h"
19 #include "base/win/scoped_handle.h"
20 #include "build/build_config.h"
21 #include "chrome/common/channel_info.h"
22 #include "components/version_info/version_info.h"
23
24 namespace {
25
26 typedef BOOL (WINAPI* CloseHandleType) (HANDLE handle);
27
28 typedef BOOL (WINAPI* DuplicateHandleType)(HANDLE source_process,
29 HANDLE source_handle,
30 HANDLE target_process,
31 HANDLE* target_handle,
32 DWORD desired_access,
33 BOOL inherit_handle,
34 DWORD options);
35
36 CloseHandleType g_close_function = NULL;
37 DuplicateHandleType g_duplicate_function = NULL;
38
39 // The entry point for CloseHandle interception. This function notifies the
40 // verifier about the handle that is being closed, and calls the original
41 // function.
42 BOOL WINAPI CloseHandleHook(HANDLE handle) {
43 base::win::OnHandleBeingClosed(handle);
44 return g_close_function(handle);
45 }
46
47 BOOL WINAPI DuplicateHandleHook(HANDLE source_process,
48 HANDLE source_handle,
49 HANDLE target_process,
50 HANDLE* target_handle,
51 DWORD desired_access,
52 BOOL inherit_handle,
53 DWORD options) {
54 if ((options & DUPLICATE_CLOSE_SOURCE) &&
55 (GetProcessId(source_process) == ::GetCurrentProcessId())) {
56 base::win::OnHandleBeingClosed(source_handle);
57 }
58
59 return g_duplicate_function(source_process, source_handle, target_process,
60 target_handle, desired_access, inherit_handle,
61 options);
62 }
63
64 // Provides a simple way to temporarily change the protection of a memory page.
65 class AutoProtectMemory {
66 public:
67 AutoProtectMemory()
68 : changed_(false), address_(NULL), bytes_(0), old_protect_(0) {}
69
70 ~AutoProtectMemory() {
71 RevertProtection();
72 }
73
74 // Grants write access to a given memory range.
75 bool ChangeProtection(void* address, size_t bytes);
76
77 // Restores the original page protection.
78 void RevertProtection();
79
80 private:
81 bool changed_;
82 void* address_;
83 size_t bytes_;
84 DWORD old_protect_;
85
86 DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory);
87 };
88
89 bool AutoProtectMemory::ChangeProtection(void* address, size_t bytes) {
90 DCHECK(!changed_);
91 DCHECK(address);
92
93 // Change the page protection so that we can write.
94 MEMORY_BASIC_INFORMATION memory_info;
95 if (!VirtualQuery(address, &memory_info, sizeof(memory_info)))
96 return false;
97
98 DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
99 PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
100 memory_info.Protect;
101
102 DWORD protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
103 if (!VirtualProtect(address, bytes, protect, &old_protect_))
104 return false;
105
106 changed_ = true;
107 address_ = address;
108 bytes_ = bytes;
109 return true;
110 }
111
112 void AutoProtectMemory::RevertProtection() {
113 if (!changed_)
114 return;
115
116 DCHECK(address_);
117 DCHECK(bytes_);
118
119 VirtualProtect(address_, bytes_, old_protect_, &old_protect_);
120 changed_ = false;
121 address_ = NULL;
122 bytes_ = 0;
123 old_protect_ = 0;
124 }
125
126 // Performs an EAT interception.
127 void EATPatch(HMODULE module, const char* function_name,
128 void* new_function, void** old_function) {
129 if (!module)
130 return;
131
132 base::win::PEImage pe(module);
133 if (!pe.VerifyMagic())
134 return;
135
136 DWORD* eat_entry = pe.GetExportEntry(function_name);
137 if (!eat_entry)
138 return;
139
140 if (!(*old_function))
141 *old_function = pe.RVAToAddr(*eat_entry);
142
143 AutoProtectMemory memory;
144 if (!memory.ChangeProtection(eat_entry, sizeof(DWORD)))
145 return;
146
147 // Perform the patch.
148 #pragma warning(push)
149 #pragma warning(disable : 4311 4302)
150 // These casts generate truncation warnings because they are 32 bit specific.
151 *eat_entry = reinterpret_cast<DWORD>(new_function) -
152 reinterpret_cast<DWORD>(module);
153 #pragma warning(pop)
154 }
155
156 // Performs an IAT interception.
157 base::win::IATPatchFunction* IATPatch(HMODULE module, const char* function_name,
158 void* new_function, void** old_function) {
159 if (!module)
160 return NULL;
161
162 base::win::IATPatchFunction* patch = new base::win::IATPatchFunction;
163 __try {
164 // There is no guarantee that |module| is still loaded at this point.
165 if (patch->PatchFromModule(module, "kernel32.dll", function_name,
166 new_function)) {
167 delete patch;
168 return NULL;
169 }
170 } __except((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
171 GetExceptionCode() == EXCEPTION_GUARD_PAGE ||
172 GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR) ?
173 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
174 // Leak the patch.
175 return NULL;
176 }
177
178 if (!(*old_function)) {
179 // Things are probably messed up if each intercepted function points to
180 // a different place, but we need only one function to call.
181 *old_function = patch->original_function();
182 }
183 return patch;
184 }
185
186 // Keeps track of all the hooks needed to intercept functions which could
187 // possibly close handles.
188 class HandleHooks {
189 public:
190 HandleHooks() {}
191 ~HandleHooks() {}
192
193 void AddIATPatch(HMODULE module);
194 void AddEATPatch();
195 void Unpatch();
196
197 private:
198 std::vector<base::win::IATPatchFunction*> hooks_;
199 DISALLOW_COPY_AND_ASSIGN(HandleHooks);
200 };
201 base::LazyInstance<HandleHooks> g_hooks = LAZY_INSTANCE_INITIALIZER;
202
203 void HandleHooks::AddIATPatch(HMODULE module) {
204 if (!module)
205 return;
206
207 base::win::IATPatchFunction* patch = NULL;
208 patch = IATPatch(module, "CloseHandle", &CloseHandleHook,
209 reinterpret_cast<void**>(&g_close_function));
210 if (!patch)
211 return;
212 hooks_.push_back(patch);
213
214 patch = IATPatch(module, "DuplicateHandle", &DuplicateHandleHook,
215 reinterpret_cast<void**>(&g_duplicate_function));
216 if (!patch)
217 return;
218 hooks_.push_back(patch);
219 }
220
221 void HandleHooks::AddEATPatch() {
222 // An attempt to restore the entry on the table at destruction is not safe.
223 EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle",
224 &CloseHandleHook, reinterpret_cast<void**>(&g_close_function));
225 EATPatch(GetModuleHandleA("kernel32.dll"), "DuplicateHandle",
226 &DuplicateHandleHook,
227 reinterpret_cast<void**>(&g_duplicate_function));
228 }
229
230 void HandleHooks::Unpatch() {
231 for (std::vector<base::win::IATPatchFunction*>::iterator it = hooks_.begin();
232 it != hooks_.end(); ++it) {
233 (*it)->Unpatch();
234 delete *it;
235 }
236 }
237
238 bool UseHooks() {
239 #if defined(ARCH_CPU_X86_64)
240 return false;
241 #elif defined(NDEBUG)
242 version_info::Channel channel = chrome::GetChannel();
243 if (channel == version_info::Channel::CANARY ||
244 channel == version_info::Channel::DEV) {
245 return true;
246 }
247
248 return false;
249 #else // NDEBUG
250 return true;
251 #endif
252 }
253
254 void PatchLoadedModules(HandleHooks* hooks) {
255 const DWORD kSize = 256;
256 DWORD returned;
257 scoped_ptr<HMODULE[]> modules(new HMODULE[kSize]);
258 if (!EnumProcessModules(GetCurrentProcess(), modules.get(),
259 kSize * sizeof(HMODULE), &returned)) {
260 return;
261 }
262 returned /= sizeof(HMODULE);
263 returned = std::min(kSize, returned);
264
265 for (DWORD current = 0; current < returned; current++) {
266 hooks->AddIATPatch(modules[current]);
267 }
268 }
269
270 } // namespace
271
272 void InstallHandleHooks() {
273 if (UseHooks()) {
274 HandleHooks* hooks = g_hooks.Pointer();
275
276 // Performing EAT interception first is safer in the presence of other
277 // threads attempting to call CloseHandle.
278 hooks->AddEATPatch();
279 PatchLoadedModules(hooks);
280 } else {
281 base::win::DisableHandleVerifier();
282 }
283 }
284
285 void RemoveHandleHooks() {
286 // We are partching all loaded modules without forcing them to stay in memory,
287 // removing patches is not safe.
288 }
OLDNEW
« no previous file with comments | « chrome/app/close_handle_hook_win.h ('k') | chrome/chrome_dll.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698