blob: eb0722595ef0c7a238755e750c10eb07dfb24ae2 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2015 The Chromium Authors
rvargas86d7c902014-09-04 22:58:092// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Will Harrisd4b45e02022-05-21 00:30:485#include "base/debug/handle_hooks_win.h"
rvargas86d7c902014-09-04 22:58:096
Will Harrisd4b45e02022-05-21 00:30:487#include <windows.h>
8
rvargasae02eef2014-09-30 18:42:249#include <psapi.h>
avi2729e442015-12-26 05:27:4510#include <stddef.h>
rvargas86d7c902014-09-04 22:58:0911
Will Harrisd4b45e02022-05-21 00:30:4812#include "base/logging.h"
Keishi Hattori0e45c022021-11-27 09:25:5213#include "base/memory/raw_ptr.h"
Will Harrisd4b45e02022-05-21 00:30:4814#include "base/numerics/safe_conversions.h"
rvargas86d7c902014-09-04 22:58:0915#include "base/win/iat_patch_function.h"
rvargasae02eef2014-09-30 18:42:2416#include "base/win/pe_image.h"
rvargas86d7c902014-09-04 22:58:0917#include "base/win/scoped_handle.h"
avi2729e442015-12-26 05:27:4518#include "build/build_config.h"
wfh7969a4762015-12-16 20:15:3119
rvargas86d7c902014-09-04 22:58:0920namespace {
21
Will Harrisd4b45e02022-05-21 00:30:4822using CloseHandleType = decltype(&::CloseHandle);
23using DuplicateHandleType = decltype(&::DuplicateHandle);
shrikantbb11d70c2015-08-10 23:43:0424
Will Harrisd4b45e02022-05-21 00:30:4825CloseHandleType g_close_function = nullptr;
26DuplicateHandleType g_duplicate_function = nullptr;
rvargas86d7c902014-09-04 22:58:0927
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.
31BOOL WINAPI CloseHandleHook(HANDLE handle) {
Will Harrisd4b45e02022-05-21 00:30:4832 base::win::OnHandleBeingClosed(handle,
33 base::win::HandleOperation::kCloseHandleHook);
rvargas86d7c902014-09-04 22:58:0934 return g_close_function(handle);
35}
36
shrikantbb11d70c2015-08-10 23:43:0437BOOL 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())) {
Will Harrisd4b45e02022-05-21 00:30:4846 base::win::OnHandleBeingClosed(
47 source_handle, base::win::HandleOperation::kDuplicateHandleHook);
shrikantbb11d70c2015-08-10 23:43:0448 }
49
50 return g_duplicate_function(source_process, source_handle, target_process,
51 target_handle, desired_access, inherit_handle,
52 options);
53}
54
wfhb5408302016-01-06 20:24:0655} // namespace
56
57namespace base {
58namespace debug {
59
60namespace {
61
rvargasae02eef2014-09-30 18:42:2462// Provides a simple way to temporarily change the protection of a memory page.
63class AutoProtectMemory {
64 public:
65 AutoProtectMemory()
Keishi Hattorid837ccb2020-10-12 04:37:3866 : changed_(false), address_(nullptr), bytes_(0), old_protect_(0) {}
rvargasae02eef2014-09-30 18:42:2467
Peter Boström7319bbd2021-09-15 22:59:3868 AutoProtectMemory(const AutoProtectMemory&) = delete;
69 AutoProtectMemory& operator=(const AutoProtectMemory&) = delete;
70
Will Harrisd4b45e02022-05-21 00:30:4871 ~AutoProtectMemory() { RevertProtection(); }
rvargasae02eef2014-09-30 18:42:2472
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_;
Keishi Hattori0e45c022021-11-27 09:25:5281 raw_ptr<void> address_;
rvargasae02eef2014-09-30 18:42:2482 size_t bytes_;
83 DWORD old_protect_;
rvargasae02eef2014-09-30 18:42:2484};
85
86bool 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;
Peter Kasting134ef9af2024-12-28 02:30:0992 if (!VirtualQuery(address, &memory_info, sizeof(memory_info))) {
rvargasae02eef2014-09-30 18:42:2493 return false;
Peter Kasting134ef9af2024-12-28 02:30:0994 }
rvargasae02eef2014-09-30 18:42:2495
96 DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
Will Harrisd4b45e02022-05-21 00:30:4897 PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
rvargasae02eef2014-09-30 18:42:2498 memory_info.Protect;
99
100 DWORD protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
Peter Kasting134ef9af2024-12-28 02:30:09101 if (!VirtualProtect(address, bytes, protect, &old_protect_)) {
rvargasae02eef2014-09-30 18:42:24102 return false;
Peter Kasting134ef9af2024-12-28 02:30:09103 }
rvargasae02eef2014-09-30 18:42:24104
105 changed_ = true;
106 address_ = address;
107 bytes_ = bytes;
108 return true;
109}
110
111void AutoProtectMemory::RevertProtection() {
Peter Kasting134ef9af2024-12-28 02:30:09112 if (!changed_) {
rvargasae02eef2014-09-30 18:42:24113 return;
Peter Kasting134ef9af2024-12-28 02:30:09114 }
rvargasae02eef2014-09-30 18:42:24115
116 DCHECK(address_);
117 DCHECK(bytes_);
118
119 VirtualProtect(address_, bytes_, old_protect_, &old_protect_);
120 changed_ = false;
Keishi Hattorid837ccb2020-10-12 04:37:38121 address_ = nullptr;
rvargasae02eef2014-09-30 18:42:24122 bytes_ = 0;
123 old_protect_ = 0;
124}
125
Will Harrisd4b45e02022-05-21 00:30:48126#if defined(ARCH_CPU_32_BITS)
127// Performs an EAT interception. Only supported on 32-bit.
128void EATPatch(HMODULE module,
129 const char* function_name,
130 void* new_function,
131 void** old_function) {
Peter Kasting134ef9af2024-12-28 02:30:09132 if (!module) {
wfh360c1862015-12-21 19:48:06133 return;
Peter Kasting134ef9af2024-12-28 02:30:09134 }
rvargasae02eef2014-09-30 18:42:24135
136 base::win::PEImage pe(module);
Peter Kasting134ef9af2024-12-28 02:30:09137 if (!pe.VerifyMagic()) {
wfh360c1862015-12-21 19:48:06138 return;
Peter Kasting134ef9af2024-12-28 02:30:09139 }
rvargasae02eef2014-09-30 18:42:24140
141 DWORD* eat_entry = pe.GetExportEntry(function_name);
Peter Kasting134ef9af2024-12-28 02:30:09142 if (!eat_entry) {
wfh360c1862015-12-21 19:48:06143 return;
Peter Kasting134ef9af2024-12-28 02:30:09144 }
rvargasae02eef2014-09-30 18:42:24145
Peter Kasting134ef9af2024-12-28 02:30:09146 if (!(*old_function)) {
rvargasae02eef2014-09-30 18:42:24147 *old_function = pe.RVAToAddr(*eat_entry);
Peter Kasting134ef9af2024-12-28 02:30:09148 }
rvargasae02eef2014-09-30 18:42:24149
150 AutoProtectMemory memory;
Peter Kasting134ef9af2024-12-28 02:30:09151 if (!memory.ChangeProtection(eat_entry, sizeof(DWORD))) {
wfh360c1862015-12-21 19:48:06152 return;
Peter Kasting134ef9af2024-12-28 02:30:09153 }
rvargasae02eef2014-09-30 18:42:24154
155 // Perform the patch.
Will Harrisd4b45e02022-05-21 00:30:48156 *eat_entry =
157 base::checked_cast<DWORD>(reinterpret_cast<uintptr_t>(new_function) -
158 reinterpret_cast<uintptr_t>(module));
rvargasae02eef2014-09-30 18:42:24159}
Will Harrisd4b45e02022-05-21 00:30:48160#endif // defined(ARCH_CPU_32_BITS)
rvargasae02eef2014-09-30 18:42:24161
shrikantbb11d70c2015-08-10 23:43:04162// Performs an IAT interception.
Will Harrisd4b45e02022-05-21 00:30:48163std::unique_ptr<base::win::IATPatchFunction> IATPatch(HMODULE module,
164 const char* function_name,
165 void* new_function,
166 void** old_function) {
Peter Kasting134ef9af2024-12-28 02:30:09167 if (!module) {
Will Harrisd4b45e02022-05-21 00:30:48168 return nullptr;
Peter Kasting134ef9af2024-12-28 02:30:09169 }
shrikantbb11d70c2015-08-10 23:43:04170
Will Harrisd4b45e02022-05-21 00:30:48171 auto patch = std::make_unique<base::win::IATPatchFunction>();
shrikantbb11d70c2015-08-10 23:43:04172 __try {
173 // There is no guarantee that |module| is still loaded at this point.
174 if (patch->PatchFromModule(module, "kernel32.dll", function_name,
175 new_function)) {
Will Harrisd4b45e02022-05-21 00:30:48176 return nullptr;
shrikantbb11d70c2015-08-10 23:43:04177 }
Will Harrisd4b45e02022-05-21 00:30:48178 } __except ((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
179 GetExceptionCode() == EXCEPTION_GUARD_PAGE ||
180 GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR)
181 ? EXCEPTION_EXECUTE_HANDLER
182 : EXCEPTION_CONTINUE_SEARCH) {
shrikantbb11d70c2015-08-10 23:43:04183 // Leak the patch.
Will Harrisd4b45e02022-05-21 00:30:48184 std::ignore = patch.release();
185 return nullptr;
shrikantbb11d70c2015-08-10 23:43:04186 }
187
188 if (!(*old_function)) {
189 // Things are probably messed up if each intercepted function points to
190 // a different place, but we need only one function to call.
191 *old_function = patch->original_function();
192 }
193 return patch;
194}
195
Will Harrisd4b45e02022-05-21 00:30:48196} // namespace
Peter Boström7319bbd2021-09-15 22:59:38197
Will Harrisd4b45e02022-05-21 00:30:48198// static
wfh360c1862015-12-21 19:48:06199void HandleHooks::AddIATPatch(HMODULE module) {
Peter Kasting134ef9af2024-12-28 02:30:09200 if (!module) {
wfh360c1862015-12-21 19:48:06201 return;
Peter Kasting134ef9af2024-12-28 02:30:09202 }
rvargas86d7c902014-09-04 22:58:09203
Will Harrisd4b45e02022-05-21 00:30:48204 auto close_handle_patch =
Nico Weber2638cbd2018-03-12 17:11:15205 IATPatch(module, "CloseHandle", reinterpret_cast<void*>(&CloseHandleHook),
206 reinterpret_cast<void**>(&g_close_function));
Peter Kasting134ef9af2024-12-28 02:30:09207 if (!close_handle_patch) {
wfh360c1862015-12-21 19:48:06208 return;
Peter Kasting134ef9af2024-12-28 02:30:09209 }
Will Harrisd4b45e02022-05-21 00:30:48210 // This is intentionally leaked.
211 std::ignore = close_handle_patch.release();
shrikantbb11d70c2015-08-10 23:43:04212
Will Harrisd4b45e02022-05-21 00:30:48213 auto duplicate_handle_patch = IATPatch(
214 module, "DuplicateHandle", reinterpret_cast<void*>(&DuplicateHandleHook),
215 reinterpret_cast<void**>(&g_duplicate_function));
Peter Kasting134ef9af2024-12-28 02:30:09216 if (!duplicate_handle_patch) {
wfh360c1862015-12-21 19:48:06217 return;
Peter Kasting134ef9af2024-12-28 02:30:09218 }
Will Harrisd4b45e02022-05-21 00:30:48219 // This is intentionally leaked.
220 std::ignore = duplicate_handle_patch.release();
rvargas86d7c902014-09-04 22:58:09221}
222
Will Harrisd4b45e02022-05-21 00:30:48223#if defined(ARCH_CPU_32_BITS)
224// static
wfh360c1862015-12-21 19:48:06225void HandleHooks::AddEATPatch() {
rvargasae02eef2014-09-30 18:42:24226 // An attempt to restore the entry on the table at destruction is not safe.
wfh360c1862015-12-21 19:48:06227 EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle",
Nico Weber2638cbd2018-03-12 17:11:15228 reinterpret_cast<void*>(&CloseHandleHook),
229 reinterpret_cast<void**>(&g_close_function));
wfh360c1862015-12-21 19:48:06230 EATPatch(GetModuleHandleA("kernel32.dll"), "DuplicateHandle",
Nico Weber2638cbd2018-03-12 17:11:15231 reinterpret_cast<void*>(&DuplicateHandleHook),
wfh360c1862015-12-21 19:48:06232 reinterpret_cast<void**>(&g_duplicate_function));
rvargasae02eef2014-09-30 18:42:24233}
Will Harrisd4b45e02022-05-21 00:30:48234#endif // defined(ARCH_CPU_32_BITS)
rvargasae02eef2014-09-30 18:42:24235
Will Harrisd4b45e02022-05-21 00:30:48236// static
237void HandleHooks::PatchLoadedModules() {
rvargasae02eef2014-09-30 18:42:24238 const DWORD kSize = 256;
239 DWORD returned;
Will Harrisd4b45e02022-05-21 00:30:48240 auto modules = std::make_unique<HMODULE[]>(kSize);
241 if (!::EnumProcessModules(GetCurrentProcess(), modules.get(),
242 kSize * sizeof(HMODULE), &returned)) {
wfh360c1862015-12-21 19:48:06243 return;
rvargas86d7c902014-09-04 22:58:09244 }
rvargasae02eef2014-09-30 18:42:24245 returned /= sizeof(HMODULE);
246 returned = std::min(kSize, returned);
247
248 for (DWORD current = 0; current < returned; current++) {
Will Harrisd4b45e02022-05-21 00:30:48249 AddIATPatch(modules[current]);
rvargasae02eef2014-09-30 18:42:24250 }
rvargas86d7c902014-09-04 22:58:09251}
252
wfhb5408302016-01-06 20:24:06253} // namespace debug
254} // namespace base