xref: /aosp_15_r20/external/cronet/base/debug/handle_hooks_win.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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