blob: 7a6e279c485632279fe7d113471cdf737537057e [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
Tom Sepezbee5f7cc2025-11-04 20:39:4812#include "base/compiler_specific.h"
Will Harrisd4b45e02022-05-21 00:30:4813#include "base/logging.h"
Keishi Hattori0e45c022021-11-27 09:25:5214#include "base/memory/raw_ptr.h"
Will Harrisd4b45e02022-05-21 00:30:4815#include "base/numerics/safe_conversions.h"
rvargas86d7c902014-09-04 22:58:0916#include "base/win/iat_patch_function.h"
rvargasae02eef2014-09-30 18:42:2417#include "base/win/pe_image.h"
rvargas86d7c902014-09-04 22:58:0918#include "base/win/scoped_handle.h"
avi2729e442015-12-26 05:27:4519#include "build/build_config.h"
wfh7969a4762015-12-16 20:15:3120
rvargas86d7c902014-09-04 22:58:0921namespace {
22
Will Harrisd4b45e02022-05-21 00:30:4823using CloseHandleType = decltype(&::CloseHandle);
24using DuplicateHandleType = decltype(&::DuplicateHandle);
shrikantbb11d70c2015-08-10 23:43:0425
Will Harrisd4b45e02022-05-21 00:30:4826CloseHandleType g_close_function = nullptr;
27DuplicateHandleType g_duplicate_function = nullptr;
rvargas86d7c902014-09-04 22:58:0928
29// The entry point for CloseHandle interception. This function notifies the
30// verifier about the handle that is being closed, and calls the original
31// function.
32BOOL WINAPI CloseHandleHook(HANDLE handle) {
Will Harrisd4b45e02022-05-21 00:30:4833 base::win::OnHandleBeingClosed(handle,
34 base::win::HandleOperation::kCloseHandleHook);
rvargas86d7c902014-09-04 22:58:0935 return g_close_function(handle);
36}
37
shrikantbb11d70c2015-08-10 23:43:0438BOOL WINAPI DuplicateHandleHook(HANDLE source_process,
39 HANDLE source_handle,
40 HANDLE target_process,
41 HANDLE* target_handle,
42 DWORD desired_access,
43 BOOL inherit_handle,
44 DWORD options) {
45 if ((options & DUPLICATE_CLOSE_SOURCE) &&
46 (GetProcessId(source_process) == ::GetCurrentProcessId())) {
Will Harrisd4b45e02022-05-21 00:30:4847 base::win::OnHandleBeingClosed(
48 source_handle, base::win::HandleOperation::kDuplicateHandleHook);
shrikantbb11d70c2015-08-10 23:43:0449 }
50
51 return g_duplicate_function(source_process, source_handle, target_process,
52 target_handle, desired_access, inherit_handle,
53 options);
54}
55
wfhb5408302016-01-06 20:24:0656} // namespace
57
58namespace base {
59namespace debug {
60
61namespace {
62
rvargasae02eef2014-09-30 18:42:2463// Provides a simple way to temporarily change the protection of a memory page.
64class AutoProtectMemory {
65 public:
66 AutoProtectMemory()
Keishi Hattorid837ccb2020-10-12 04:37:3867 : changed_(false), address_(nullptr), bytes_(0), old_protect_(0) {}
rvargasae02eef2014-09-30 18:42:2468
Peter Boström7319bbd2021-09-15 22:59:3869 AutoProtectMemory(const AutoProtectMemory&) = delete;
70 AutoProtectMemory& operator=(const AutoProtectMemory&) = delete;
71
Will Harrisd4b45e02022-05-21 00:30:4872 ~AutoProtectMemory() { RevertProtection(); }
rvargasae02eef2014-09-30 18:42:2473
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_;
Keishi Hattori0e45c022021-11-27 09:25:5282 raw_ptr<void> address_;
rvargasae02eef2014-09-30 18:42:2483 size_t bytes_;
84 DWORD old_protect_;
rvargasae02eef2014-09-30 18:42:2485};
86
87bool AutoProtectMemory::ChangeProtection(void* address, size_t bytes) {
88 DCHECK(!changed_);
89 DCHECK(address);
90
91 // Change the page protection so that we can write.
92 MEMORY_BASIC_INFORMATION memory_info;
Peter Kasting134ef9af2024-12-28 02:30:0993 if (!VirtualQuery(address, &memory_info, sizeof(memory_info))) {
rvargasae02eef2014-09-30 18:42:2494 return false;
Peter Kasting134ef9af2024-12-28 02:30:0995 }
rvargasae02eef2014-09-30 18:42:2496
97 DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
Will Harrisd4b45e02022-05-21 00:30:4898 PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
rvargasae02eef2014-09-30 18:42:2499 memory_info.Protect;
100
101 DWORD protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
Peter Kasting134ef9af2024-12-28 02:30:09102 if (!VirtualProtect(address, bytes, protect, &old_protect_)) {
rvargasae02eef2014-09-30 18:42:24103 return false;
Peter Kasting134ef9af2024-12-28 02:30:09104 }
rvargasae02eef2014-09-30 18:42:24105
106 changed_ = true;
107 address_ = address;
108 bytes_ = bytes;
109 return true;
110}
111
112void AutoProtectMemory::RevertProtection() {
Peter Kasting134ef9af2024-12-28 02:30:09113 if (!changed_) {
rvargasae02eef2014-09-30 18:42:24114 return;
Peter Kasting134ef9af2024-12-28 02:30:09115 }
rvargasae02eef2014-09-30 18:42:24116
117 DCHECK(address_);
118 DCHECK(bytes_);
119
120 VirtualProtect(address_, bytes_, old_protect_, &old_protect_);
121 changed_ = false;
Keishi Hattorid837ccb2020-10-12 04:37:38122 address_ = nullptr;
rvargasae02eef2014-09-30 18:42:24123 bytes_ = 0;
124 old_protect_ = 0;
125}
126
Will Harrisd4b45e02022-05-21 00:30:48127#if defined(ARCH_CPU_32_BITS)
128// Performs an EAT interception. Only supported on 32-bit.
129void EATPatch(HMODULE module,
130 const char* function_name,
131 void* new_function,
132 void** old_function) {
Peter Kasting134ef9af2024-12-28 02:30:09133 if (!module) {
wfh360c1862015-12-21 19:48:06134 return;
Peter Kasting134ef9af2024-12-28 02:30:09135 }
rvargasae02eef2014-09-30 18:42:24136
137 base::win::PEImage pe(module);
Peter Kasting134ef9af2024-12-28 02:30:09138 if (!pe.VerifyMagic()) {
wfh360c1862015-12-21 19:48:06139 return;
Peter Kasting134ef9af2024-12-28 02:30:09140 }
rvargasae02eef2014-09-30 18:42:24141
142 DWORD* eat_entry = pe.GetExportEntry(function_name);
Peter Kasting134ef9af2024-12-28 02:30:09143 if (!eat_entry) {
wfh360c1862015-12-21 19:48:06144 return;
Peter Kasting134ef9af2024-12-28 02:30:09145 }
rvargasae02eef2014-09-30 18:42:24146
Peter Kasting134ef9af2024-12-28 02:30:09147 if (!(*old_function)) {
rvargasae02eef2014-09-30 18:42:24148 *old_function = pe.RVAToAddr(*eat_entry);
Peter Kasting134ef9af2024-12-28 02:30:09149 }
rvargasae02eef2014-09-30 18:42:24150
151 AutoProtectMemory memory;
Peter Kasting134ef9af2024-12-28 02:30:09152 if (!memory.ChangeProtection(eat_entry, sizeof(DWORD))) {
wfh360c1862015-12-21 19:48:06153 return;
Peter Kasting134ef9af2024-12-28 02:30:09154 }
rvargasae02eef2014-09-30 18:42:24155
156 // Perform the patch.
Will Harrisd4b45e02022-05-21 00:30:48157 *eat_entry =
158 base::checked_cast<DWORD>(reinterpret_cast<uintptr_t>(new_function) -
159 reinterpret_cast<uintptr_t>(module));
rvargasae02eef2014-09-30 18:42:24160}
Will Harrisd4b45e02022-05-21 00:30:48161#endif // defined(ARCH_CPU_32_BITS)
rvargasae02eef2014-09-30 18:42:24162
shrikantbb11d70c2015-08-10 23:43:04163// Performs an IAT interception.
Will Harrisd4b45e02022-05-21 00:30:48164std::unique_ptr<base::win::IATPatchFunction> IATPatch(HMODULE module,
165 const char* function_name,
166 void* new_function,
167 void** old_function) {
Peter Kasting134ef9af2024-12-28 02:30:09168 if (!module) {
Will Harrisd4b45e02022-05-21 00:30:48169 return nullptr;
Peter Kasting134ef9af2024-12-28 02:30:09170 }
shrikantbb11d70c2015-08-10 23:43:04171
Will Harrisd4b45e02022-05-21 00:30:48172 auto patch = std::make_unique<base::win::IATPatchFunction>();
shrikantbb11d70c2015-08-10 23:43:04173 __try {
174 // There is no guarantee that |module| is still loaded at this point.
175 if (patch->PatchFromModule(module, "kernel32.dll", function_name,
176 new_function)) {
Will Harrisd4b45e02022-05-21 00:30:48177 return nullptr;
shrikantbb11d70c2015-08-10 23:43:04178 }
Will Harrisd4b45e02022-05-21 00:30:48179 } __except ((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
180 GetExceptionCode() == EXCEPTION_GUARD_PAGE ||
181 GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR)
182 ? EXCEPTION_EXECUTE_HANDLER
183 : EXCEPTION_CONTINUE_SEARCH) {
shrikantbb11d70c2015-08-10 23:43:04184 // Leak the patch.
Will Harrisd4b45e02022-05-21 00:30:48185 std::ignore = patch.release();
186 return nullptr;
shrikantbb11d70c2015-08-10 23:43:04187 }
188
189 if (!(*old_function)) {
190 // Things are probably messed up if each intercepted function points to
191 // a different place, but we need only one function to call.
192 *old_function = patch->original_function();
193 }
194 return patch;
195}
196
Will Harrisd4b45e02022-05-21 00:30:48197} // namespace
Peter Boström7319bbd2021-09-15 22:59:38198
Will Harrisd4b45e02022-05-21 00:30:48199// static
wfh360c1862015-12-21 19:48:06200void HandleHooks::AddIATPatch(HMODULE module) {
Peter Kasting134ef9af2024-12-28 02:30:09201 if (!module) {
wfh360c1862015-12-21 19:48:06202 return;
Peter Kasting134ef9af2024-12-28 02:30:09203 }
rvargas86d7c902014-09-04 22:58:09204
Will Harrisd4b45e02022-05-21 00:30:48205 auto close_handle_patch =
Nico Weber2638cbd2018-03-12 17:11:15206 IATPatch(module, "CloseHandle", reinterpret_cast<void*>(&CloseHandleHook),
207 reinterpret_cast<void**>(&g_close_function));
Peter Kasting134ef9af2024-12-28 02:30:09208 if (!close_handle_patch) {
wfh360c1862015-12-21 19:48:06209 return;
Peter Kasting134ef9af2024-12-28 02:30:09210 }
Will Harrisd4b45e02022-05-21 00:30:48211 // This is intentionally leaked.
212 std::ignore = close_handle_patch.release();
shrikantbb11d70c2015-08-10 23:43:04213
Will Harrisd4b45e02022-05-21 00:30:48214 auto duplicate_handle_patch = IATPatch(
215 module, "DuplicateHandle", reinterpret_cast<void*>(&DuplicateHandleHook),
216 reinterpret_cast<void**>(&g_duplicate_function));
Peter Kasting134ef9af2024-12-28 02:30:09217 if (!duplicate_handle_patch) {
wfh360c1862015-12-21 19:48:06218 return;
Peter Kasting134ef9af2024-12-28 02:30:09219 }
Will Harrisd4b45e02022-05-21 00:30:48220 // This is intentionally leaked.
221 std::ignore = duplicate_handle_patch.release();
rvargas86d7c902014-09-04 22:58:09222}
223
Will Harrisd4b45e02022-05-21 00:30:48224#if defined(ARCH_CPU_32_BITS)
225// static
wfh360c1862015-12-21 19:48:06226void HandleHooks::AddEATPatch() {
rvargasae02eef2014-09-30 18:42:24227 // An attempt to restore the entry on the table at destruction is not safe.
wfh360c1862015-12-21 19:48:06228 EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle",
Nico Weber2638cbd2018-03-12 17:11:15229 reinterpret_cast<void*>(&CloseHandleHook),
230 reinterpret_cast<void**>(&g_close_function));
wfh360c1862015-12-21 19:48:06231 EATPatch(GetModuleHandleA("kernel32.dll"), "DuplicateHandle",
Nico Weber2638cbd2018-03-12 17:11:15232 reinterpret_cast<void*>(&DuplicateHandleHook),
wfh360c1862015-12-21 19:48:06233 reinterpret_cast<void**>(&g_duplicate_function));
rvargasae02eef2014-09-30 18:42:24234}
Will Harrisd4b45e02022-05-21 00:30:48235#endif // defined(ARCH_CPU_32_BITS)
rvargasae02eef2014-09-30 18:42:24236
Will Harrisd4b45e02022-05-21 00:30:48237// static
238void HandleHooks::PatchLoadedModules() {
rvargasae02eef2014-09-30 18:42:24239 const DWORD kSize = 256;
240 DWORD returned;
Will Harrisd4b45e02022-05-21 00:30:48241 auto modules = std::make_unique<HMODULE[]>(kSize);
242 if (!::EnumProcessModules(GetCurrentProcess(), modules.get(),
243 kSize * sizeof(HMODULE), &returned)) {
wfh360c1862015-12-21 19:48:06244 return;
rvargas86d7c902014-09-04 22:58:09245 }
rvargasae02eef2014-09-30 18:42:24246 returned /= sizeof(HMODULE);
247 returned = std::min(kSize, returned);
248
249 for (DWORD current = 0; current < returned; current++) {
Tom Sepezbee5f7cc2025-11-04 20:39:48250 AddIATPatch(UNSAFE_TODO(modules[current]));
rvargasae02eef2014-09-30 18:42:24251 }
rvargas86d7c902014-09-04 22:58:09252}
253
wfhb5408302016-01-06 20:24:06254} // namespace debug
255} // namespace base