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

Side by Side Diff: base/debug/close_handle_hook_win.cc

Issue 1545473002: Revert Active Verifier tests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years 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 | « base/debug/close_handle_hook_win.h ('k') | base/test/run_all_unittests.cc » ('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 2015 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 "base/debug/close_handle_hook_win.h"
6
7 #include <Windows.h>
8 #include <psapi.h>
9
10 #include <algorithm>
11 #include <vector>
12
13 #include "base/lazy_instance.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/win/iat_patch_function.h"
16 #include "base/win/pe_image.h"
17 #include "base/win/scoped_handle.h"
18
19 namespace {
20
21 typedef BOOL (WINAPI* CloseHandleType) (HANDLE handle);
22
23 typedef BOOL (WINAPI* DuplicateHandleType)(HANDLE source_process,
24 HANDLE source_handle,
25 HANDLE target_process,
26 HANDLE* target_handle,
27 DWORD desired_access,
28 BOOL inherit_handle,
29 DWORD options);
30
31 CloseHandleType g_close_function = NULL;
32 DuplicateHandleType g_duplicate_function = NULL;
33
34 // The entry point for CloseHandle interception. This function notifies the
35 // verifier about the handle that is being closed, and calls the original
36 // function.
37 BOOL WINAPI CloseHandleHook(HANDLE handle) {
38 base::win::OnHandleBeingClosed(handle);
39 return g_close_function(handle);
40 }
41
42 BOOL WINAPI DuplicateHandleHook(HANDLE source_process,
43 HANDLE source_handle,
44 HANDLE target_process,
45 HANDLE* target_handle,
46 DWORD desired_access,
47 BOOL inherit_handle,
48 DWORD options) {
49 if ((options & DUPLICATE_CLOSE_SOURCE) &&
50 (GetProcessId(source_process) == ::GetCurrentProcessId())) {
51 base::win::OnHandleBeingClosed(source_handle);
52 }
53
54 return g_duplicate_function(source_process, source_handle, target_process,
55 target_handle, desired_access, inherit_handle,
56 options);
57 }
58
59 // Provides a simple way to temporarily change the protection of a memory page.
60 class AutoProtectMemory {
61 public:
62 AutoProtectMemory()
63 : changed_(false), address_(NULL), bytes_(0), old_protect_(0) {}
64
65 ~AutoProtectMemory() {
66 RevertProtection();
67 }
68
69 // Grants write access to a given memory range.
70 bool ChangeProtection(void* address, size_t bytes);
71
72 // Restores the original page protection.
73 void RevertProtection();
74
75 private:
76 bool changed_;
77 void* address_;
78 size_t bytes_;
79 DWORD old_protect_;
80
81 DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory);
82 };
83
84 bool AutoProtectMemory::ChangeProtection(void* address, size_t bytes) {
85 DCHECK(!changed_);
86 DCHECK(address);
87
88 // Change the page protection so that we can write.
89 MEMORY_BASIC_INFORMATION memory_info;
90 if (!VirtualQuery(address, &memory_info, sizeof(memory_info)))
91 return false;
92
93 DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
94 PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
95 memory_info.Protect;
96
97 DWORD protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
98 if (!VirtualProtect(address, bytes, protect, &old_protect_))
99 return false;
100
101 changed_ = true;
102 address_ = address;
103 bytes_ = bytes;
104 return true;
105 }
106
107 void AutoProtectMemory::RevertProtection() {
108 if (!changed_)
109 return;
110
111 DCHECK(address_);
112 DCHECK(bytes_);
113
114 VirtualProtect(address_, bytes_, old_protect_, &old_protect_);
115 changed_ = false;
116 address_ = NULL;
117 bytes_ = 0;
118 old_protect_ = 0;
119 }
120
121 // Performs an EAT interception.
122 bool EATPatch(HMODULE module, const char* function_name,
123 void* new_function, void** old_function) {
124 if (!module)
125 return false;
126
127 base::win::PEImage pe(module);
128 if (!pe.VerifyMagic())
129 return false;
130
131 DWORD* eat_entry = pe.GetExportEntry(function_name);
132 if (!eat_entry)
133 return false;
134
135 if (!(*old_function))
136 *old_function = pe.RVAToAddr(*eat_entry);
137
138 AutoProtectMemory memory;
139 if (!memory.ChangeProtection(eat_entry, sizeof(DWORD)))
140 return false;
141
142 // Perform the patch.
143 #pragma warning(push)
144 #pragma warning(disable : 4311 4302)
145 // These casts generate truncation warnings because they are 32 bit specific.
146 *eat_entry = reinterpret_cast<DWORD>(new_function) -
147 reinterpret_cast<DWORD>(module);
148 #pragma warning(pop)
149 return true;
150 }
151
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;
157
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;
165 }
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;
172 }
173
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();
178 }
179 return patch;
180 }
181
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() {}
188
189 bool AddIATPatch(HMODULE module);
190 bool AddEATPatch();
191 bool Unpatch();
192
193 private:
194 std::vector<base::win::IATPatchFunction*> hooks_;
195 DISALLOW_COPY_AND_ASSIGN(HandleHooks);
196 };
197 base::LazyInstance<HandleHooks> g_hooks = LAZY_INSTANCE_INITIALIZER;
198
199 bool HandleHooks::AddIATPatch(HMODULE module) {
200 if (!module)
201 return false;
202
203 base::win::IATPatchFunction* patch = NULL;
204 patch = IATPatch(module, "CloseHandle", &CloseHandleHook,
205 reinterpret_cast<void**>(&g_close_function));
206 if (!patch)
207 return false;
208 hooks_.push_back(patch);
209
210 patch = IATPatch(module, "DuplicateHandle", &DuplicateHandleHook,
211 reinterpret_cast<void**>(&g_duplicate_function));
212 if (!patch)
213 return false;
214 hooks_.push_back(patch);
215 return true;
216 }
217
218 bool HandleHooks::AddEATPatch() {
219 // An attempt to restore the entry on the table at destruction is not safe.
220 // An attempt to restore the entry on the table at destruction is not safe.
221 return (EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle",
222 &CloseHandleHook,
223 reinterpret_cast<void**>(&g_close_function)) &&
224 EATPatch(GetModuleHandleA("kernel32.dll"), "DuplicateHandle",
225 &DuplicateHandleHook,
226 reinterpret_cast<void**>(&g_duplicate_function)));
227 }
228
229 bool HandleHooks::Unpatch() {
230 DWORD err = NO_ERROR;
231 for (std::vector<base::win::IATPatchFunction*>::iterator it = hooks_.begin();
232 it != hooks_.end(); ++it) {
233 err = (*it)->Unpatch();
234 if (err != NO_ERROR)
235 break;
236 delete *it;
237 }
238 return (err == NO_ERROR);
239 }
240
241 bool PatchLoadedModules(HandleHooks* hooks) {
242 const DWORD kSize = 256;
243 DWORD returned;
244 scoped_ptr<HMODULE[]> modules(new HMODULE[kSize]);
245 if (!EnumProcessModules(GetCurrentProcess(), modules.get(),
246 kSize * sizeof(HMODULE), &returned)) {
247 return false;
248 }
249 returned /= sizeof(HMODULE);
250 returned = std::min(kSize, returned);
251
252 bool success = false;
253
254 for (DWORD current = 0; current < returned; current++) {
255 success = hooks->AddIATPatch(modules[current]);
256 if (!success)
257 break;
258 }
259
260 return success;
261 }
262
263 } // namespace
264
265 namespace base {
266 namespace debug {
267
268 bool InstallHandleHooks() {
269 HandleHooks* hooks = g_hooks.Pointer();
270
271 // Performing EAT interception first is safer in the presence of other
272 // threads attempting to call CloseHandle.
273 return (hooks->AddEATPatch() && PatchLoadedModules(hooks));
274 }
275
276 void RemoveHandleHooks() {
277 // We are patching all loaded modules without forcing them to stay in memory,
278 // removing patches is not safe.
279 }
280
281 } // namespace debug
282 } // namespace base
OLDNEW
« no previous file with comments | « base/debug/close_handle_hook_win.h ('k') | base/test/run_all_unittests.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698