1 // Copyright 2015 The Chromium Authors
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/handle_hooks_win.h"
6
7 #include <windows.h>
8
9 #include <psapi.h>
10 #include <stddef.h>
11
12 #include "base/logging.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/numerics/safe_conversions.h"
15 #include "base/win/iat_patch_function.h"
16 #include "base/win/pe_image.h"
17 #include "base/win/scoped_handle.h"
18 #include "build/build_config.h"
19
20 namespace {
21
22 using CloseHandleType = decltype(&::CloseHandle);
23 using DuplicateHandleType = decltype(&::DuplicateHandle);
24
25 CloseHandleType g_close_function = nullptr;
26 DuplicateHandleType g_duplicate_function = nullptr;
27
28 // The entry point for CloseHandle interception. This function notifies the
29 // verifier about the handle that is being closed, and calls the original
30 // function.
CloseHandleHook(HANDLE handle)31 BOOL WINAPI CloseHandleHook(HANDLE handle) {
32 base::win::OnHandleBeingClosed(handle,
33 base::win::HandleOperation::kCloseHandleHook);
34 return g_close_function(handle);
35 }
36
DuplicateHandleHook(HANDLE source_process,HANDLE source_handle,HANDLE target_process,HANDLE * target_handle,DWORD desired_access,BOOL inherit_handle,DWORD options)37 BOOL WINAPI DuplicateHandleHook(HANDLE source_process,
38 HANDLE source_handle,
39 HANDLE target_process,
40 HANDLE* target_handle,
41 DWORD desired_access,
42 BOOL inherit_handle,
43 DWORD options) {
44 if ((options & DUPLICATE_CLOSE_SOURCE) &&
45 (GetProcessId(source_process) == ::GetCurrentProcessId())) {
46 base::win::OnHandleBeingClosed(
47 source_handle, base::win::HandleOperation::kDuplicateHandleHook);
48 }
49
50 return g_duplicate_function(source_process, source_handle, target_process,
51 target_handle, desired_access, inherit_handle,
52 options);
53 }
54
55 } // namespace
56
57 namespace base {
58 namespace debug {
59
60 namespace {
61
62 // Provides a simple way to temporarily change the protection of a memory page.
63 class AutoProtectMemory {
64 public:
AutoProtectMemory()65 AutoProtectMemory()
66 : changed_(false), address_(nullptr), bytes_(0), old_protect_(0) {}
67
68 AutoProtectMemory(const AutoProtectMemory&) = delete;
69 AutoProtectMemory& operator=(const AutoProtectMemory&) = delete;
70
~AutoProtectMemory()71 ~AutoProtectMemory() { RevertProtection(); }
72
73 // Grants write access to a given memory range.
74 bool ChangeProtection(void* address, size_t bytes);
75
76 // Restores the original page protection.
77 void RevertProtection();
78
79 private:
80 bool changed_;
81 raw_ptr<void> address_;
82 size_t bytes_;
83 DWORD old_protect_;
84 };
85
ChangeProtection(void * address,size_t bytes)86 bool AutoProtectMemory::ChangeProtection(void* address, size_t bytes) {
87 DCHECK(!changed_);
88 DCHECK(address);
89
90 // Change the page protection so that we can write.
91 MEMORY_BASIC_INFORMATION memory_info;
92 if (!VirtualQuery(address, &memory_info, sizeof(memory_info)))
93 return false;
94
95 DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
96 PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
97 memory_info.Protect;
98
99 DWORD protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
100 if (!VirtualProtect(address, bytes, protect, &old_protect_))
101 return false;
102
103 changed_ = true;
104 address_ = address;
105 bytes_ = bytes;
106 return true;
107 }
108
RevertProtection()109 void AutoProtectMemory::RevertProtection() {
110 if (!changed_)
111 return;
112
113 DCHECK(address_);
114 DCHECK(bytes_);
115
116 VirtualProtect(address_, bytes_, old_protect_, &old_protect_);
117 changed_ = false;
118 address_ = nullptr;
119 bytes_ = 0;
120 old_protect_ = 0;
121 }
122
123 #if defined(ARCH_CPU_32_BITS)
124 // Performs an EAT interception. Only supported on 32-bit.
EATPatch(HMODULE module,const char * function_name,void * new_function,void ** old_function)125 void EATPatch(HMODULE module,
126 const char* function_name,
127 void* new_function,
128 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 *eat_entry =
149 base::checked_cast<DWORD>(reinterpret_cast<uintptr_t>(new_function) -
150 reinterpret_cast<uintptr_t>(module));
151 }
152 #endif // defined(ARCH_CPU_32_BITS)
153
154 // Performs an IAT interception.
IATPatch(HMODULE module,const char * function_name,void * new_function,void ** old_function)155 std::unique_ptr<base::win::IATPatchFunction> IATPatch(HMODULE module,
156 const char* function_name,
157 void* new_function,
158 void** old_function) {
159 if (!module)
160 return nullptr;
161
162 auto patch = std::make_unique<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 return nullptr;
168 }
169 } __except ((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
170 GetExceptionCode() == EXCEPTION_GUARD_PAGE ||
171 GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR)
172 ? EXCEPTION_EXECUTE_HANDLER
173 : EXCEPTION_CONTINUE_SEARCH) {
174 // Leak the patch.
175 std::ignore = patch.release();
176 return nullptr;
177 }
178
179 if (!(*old_function)) {
180 // Things are probably messed up if each intercepted function points to
181 // a different place, but we need only one function to call.
182 *old_function = patch->original_function();
183 }
184 return patch;
185 }
186
187 } // namespace
188
189 // static
AddIATPatch(HMODULE module)190 void HandleHooks::AddIATPatch(HMODULE module) {
191 if (!module)
192 return;
193
194 auto close_handle_patch =
195 IATPatch(module, "CloseHandle", reinterpret_cast<void*>(&CloseHandleHook),
196 reinterpret_cast<void**>(&g_close_function));
197 if (!close_handle_patch)
198 return;
199 // This is intentionally leaked.
200 std::ignore = close_handle_patch.release();
201
202 auto duplicate_handle_patch = IATPatch(
203 module, "DuplicateHandle", reinterpret_cast<void*>(&DuplicateHandleHook),
204 reinterpret_cast<void**>(&g_duplicate_function));
205 if (!duplicate_handle_patch)
206 return;
207 // This is intentionally leaked.
208 std::ignore = duplicate_handle_patch.release();
209 }
210
211 #if defined(ARCH_CPU_32_BITS)
212 // static
AddEATPatch()213 void HandleHooks::AddEATPatch() {
214 // An attempt to restore the entry on the table at destruction is not safe.
215 EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle",
216 reinterpret_cast<void*>(&CloseHandleHook),
217 reinterpret_cast<void**>(&g_close_function));
218 EATPatch(GetModuleHandleA("kernel32.dll"), "DuplicateHandle",
219 reinterpret_cast<void*>(&DuplicateHandleHook),
220 reinterpret_cast<void**>(&g_duplicate_function));
221 }
222 #endif // defined(ARCH_CPU_32_BITS)
223
224 // static
PatchLoadedModules()225 void HandleHooks::PatchLoadedModules() {
226 const DWORD kSize = 256;
227 DWORD returned;
228 auto modules = std::make_unique<HMODULE[]>(kSize);
229 if (!::EnumProcessModules(GetCurrentProcess(), modules.get(),
230 kSize * sizeof(HMODULE), &returned)) {
231 return;
232 }
233 returned /= sizeof(HMODULE);
234 returned = std::min(kSize, returned);
235
236 for (DWORD current = 0; current < returned; current++) {
237 AddIATPatch(modules[current]);
238 }
239 }
240
241 } // namespace debug
242 } // namespace base
243