Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2015 The Chromium Authors |
rvargas | 86d7c90 | 2014-09-04 22:58:09 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 5 | #include "base/debug/handle_hooks_win.h" |
rvargas | 86d7c90 | 2014-09-04 22:58:09 | [diff] [blame] | 6 | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 7 | #include <windows.h> |
| 8 | |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 9 | #include <psapi.h> |
avi | 2729e44 | 2015-12-26 05:27:45 | [diff] [blame] | 10 | #include <stddef.h> |
rvargas | 86d7c90 | 2014-09-04 22:58:09 | [diff] [blame] | 11 | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 12 | #include "base/logging.h" |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 13 | #include "base/memory/raw_ptr.h" |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 14 | #include "base/numerics/safe_conversions.h" |
rvargas | 86d7c90 | 2014-09-04 22:58:09 | [diff] [blame] | 15 | #include "base/win/iat_patch_function.h" |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 16 | #include "base/win/pe_image.h" |
rvargas | 86d7c90 | 2014-09-04 22:58:09 | [diff] [blame] | 17 | #include "base/win/scoped_handle.h" |
avi | 2729e44 | 2015-12-26 05:27:45 | [diff] [blame] | 18 | #include "build/build_config.h" |
wfh | 7969a476 | 2015-12-16 20:15:31 | [diff] [blame] | 19 | |
rvargas | 86d7c90 | 2014-09-04 22:58:09 | [diff] [blame] | 20 | namespace { |
| 21 | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 22 | using CloseHandleType = decltype(&::CloseHandle); |
| 23 | using DuplicateHandleType = decltype(&::DuplicateHandle); |
shrikant | bb11d70c | 2015-08-10 23:43:04 | [diff] [blame] | 24 | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 25 | CloseHandleType g_close_function = nullptr; |
| 26 | DuplicateHandleType g_duplicate_function = nullptr; |
rvargas | 86d7c90 | 2014-09-04 22:58:09 | [diff] [blame] | 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. |
| 31 | BOOL WINAPI CloseHandleHook(HANDLE handle) { |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 32 | base::win::OnHandleBeingClosed(handle, |
| 33 | base::win::HandleOperation::kCloseHandleHook); |
rvargas | 86d7c90 | 2014-09-04 22:58:09 | [diff] [blame] | 34 | return g_close_function(handle); |
| 35 | } |
| 36 | |
shrikant | bb11d70c | 2015-08-10 23:43:04 | [diff] [blame] | 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())) { |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 46 | base::win::OnHandleBeingClosed( |
| 47 | source_handle, base::win::HandleOperation::kDuplicateHandleHook); |
shrikant | bb11d70c | 2015-08-10 23:43:04 | [diff] [blame] | 48 | } |
| 49 | |
| 50 | return g_duplicate_function(source_process, source_handle, target_process, |
| 51 | target_handle, desired_access, inherit_handle, |
| 52 | options); |
| 53 | } |
| 54 | |
wfh | b540830 | 2016-01-06 20:24:06 | [diff] [blame] | 55 | } // namespace |
| 56 | |
| 57 | namespace base { |
| 58 | namespace debug { |
| 59 | |
| 60 | namespace { |
| 61 | |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 62 | // Provides a simple way to temporarily change the protection of a memory page. |
| 63 | class AutoProtectMemory { |
| 64 | public: |
| 65 | AutoProtectMemory() |
Keishi Hattori | d837ccb | 2020-10-12 04:37:38 | [diff] [blame] | 66 | : changed_(false), address_(nullptr), bytes_(0), old_protect_(0) {} |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 67 | |
Peter Boström | 7319bbd | 2021-09-15 22:59:38 | [diff] [blame] | 68 | AutoProtectMemory(const AutoProtectMemory&) = delete; |
| 69 | AutoProtectMemory& operator=(const AutoProtectMemory&) = delete; |
| 70 | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 71 | ~AutoProtectMemory() { RevertProtection(); } |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 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_; |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 81 | raw_ptr<void> address_; |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 82 | size_t bytes_; |
| 83 | DWORD old_protect_; |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 84 | }; |
| 85 | |
| 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; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 92 | if (!VirtualQuery(address, &memory_info, sizeof(memory_info))) { |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 93 | return false; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 94 | } |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 95 | |
| 96 | DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 97 | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) & |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 98 | memory_info.Protect; |
| 99 | |
| 100 | DWORD protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 101 | if (!VirtualProtect(address, bytes, protect, &old_protect_)) { |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 102 | return false; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 103 | } |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 104 | |
| 105 | changed_ = true; |
| 106 | address_ = address; |
| 107 | bytes_ = bytes; |
| 108 | return true; |
| 109 | } |
| 110 | |
| 111 | void AutoProtectMemory::RevertProtection() { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 112 | if (!changed_) { |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 113 | return; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 114 | } |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 115 | |
| 116 | DCHECK(address_); |
| 117 | DCHECK(bytes_); |
| 118 | |
| 119 | VirtualProtect(address_, bytes_, old_protect_, &old_protect_); |
| 120 | changed_ = false; |
Keishi Hattori | d837ccb | 2020-10-12 04:37:38 | [diff] [blame] | 121 | address_ = nullptr; |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 122 | bytes_ = 0; |
| 123 | old_protect_ = 0; |
| 124 | } |
| 125 | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 126 | #if defined(ARCH_CPU_32_BITS) |
| 127 | // Performs an EAT interception. Only supported on 32-bit. |
| 128 | void EATPatch(HMODULE module, |
| 129 | const char* function_name, |
| 130 | void* new_function, |
| 131 | void** old_function) { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 132 | if (!module) { |
wfh | 360c186 | 2015-12-21 19:48:06 | [diff] [blame] | 133 | return; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 134 | } |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 135 | |
| 136 | base::win::PEImage pe(module); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 137 | if (!pe.VerifyMagic()) { |
wfh | 360c186 | 2015-12-21 19:48:06 | [diff] [blame] | 138 | return; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 139 | } |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 140 | |
| 141 | DWORD* eat_entry = pe.GetExportEntry(function_name); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 142 | if (!eat_entry) { |
wfh | 360c186 | 2015-12-21 19:48:06 | [diff] [blame] | 143 | return; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 144 | } |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 145 | |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 146 | if (!(*old_function)) { |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 147 | *old_function = pe.RVAToAddr(*eat_entry); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 148 | } |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 149 | |
| 150 | AutoProtectMemory memory; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 151 | if (!memory.ChangeProtection(eat_entry, sizeof(DWORD))) { |
wfh | 360c186 | 2015-12-21 19:48:06 | [diff] [blame] | 152 | return; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 153 | } |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 154 | |
| 155 | // Perform the patch. |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 156 | *eat_entry = |
| 157 | base::checked_cast<DWORD>(reinterpret_cast<uintptr_t>(new_function) - |
| 158 | reinterpret_cast<uintptr_t>(module)); |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 159 | } |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 160 | #endif // defined(ARCH_CPU_32_BITS) |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 161 | |
shrikant | bb11d70c | 2015-08-10 23:43:04 | [diff] [blame] | 162 | // Performs an IAT interception. |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 163 | std::unique_ptr<base::win::IATPatchFunction> IATPatch(HMODULE module, |
| 164 | const char* function_name, |
| 165 | void* new_function, |
| 166 | void** old_function) { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 167 | if (!module) { |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 168 | return nullptr; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 169 | } |
shrikant | bb11d70c | 2015-08-10 23:43:04 | [diff] [blame] | 170 | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 171 | auto patch = std::make_unique<base::win::IATPatchFunction>(); |
shrikant | bb11d70c | 2015-08-10 23:43:04 | [diff] [blame] | 172 | __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 Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 176 | return nullptr; |
shrikant | bb11d70c | 2015-08-10 23:43:04 | [diff] [blame] | 177 | } |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 178 | } __except ((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION || |
| 179 | GetExceptionCode() == EXCEPTION_GUARD_PAGE || |
| 180 | GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR) |
| 181 | ? EXCEPTION_EXECUTE_HANDLER |
| 182 | : EXCEPTION_CONTINUE_SEARCH) { |
shrikant | bb11d70c | 2015-08-10 23:43:04 | [diff] [blame] | 183 | // Leak the patch. |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 184 | std::ignore = patch.release(); |
| 185 | return nullptr; |
shrikant | bb11d70c | 2015-08-10 23:43:04 | [diff] [blame] | 186 | } |
| 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 Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 196 | } // namespace |
Peter Boström | 7319bbd | 2021-09-15 22:59:38 | [diff] [blame] | 197 | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 198 | // static |
wfh | 360c186 | 2015-12-21 19:48:06 | [diff] [blame] | 199 | void HandleHooks::AddIATPatch(HMODULE module) { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 200 | if (!module) { |
wfh | 360c186 | 2015-12-21 19:48:06 | [diff] [blame] | 201 | return; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 202 | } |
rvargas | 86d7c90 | 2014-09-04 22:58:09 | [diff] [blame] | 203 | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 204 | auto close_handle_patch = |
Nico Weber | 2638cbd | 2018-03-12 17:11:15 | [diff] [blame] | 205 | IATPatch(module, "CloseHandle", reinterpret_cast<void*>(&CloseHandleHook), |
| 206 | reinterpret_cast<void**>(&g_close_function)); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 207 | if (!close_handle_patch) { |
wfh | 360c186 | 2015-12-21 19:48:06 | [diff] [blame] | 208 | return; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 209 | } |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 210 | // This is intentionally leaked. |
| 211 | std::ignore = close_handle_patch.release(); |
shrikant | bb11d70c | 2015-08-10 23:43:04 | [diff] [blame] | 212 | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 213 | auto duplicate_handle_patch = IATPatch( |
| 214 | module, "DuplicateHandle", reinterpret_cast<void*>(&DuplicateHandleHook), |
| 215 | reinterpret_cast<void**>(&g_duplicate_function)); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 216 | if (!duplicate_handle_patch) { |
wfh | 360c186 | 2015-12-21 19:48:06 | [diff] [blame] | 217 | return; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 218 | } |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 219 | // This is intentionally leaked. |
| 220 | std::ignore = duplicate_handle_patch.release(); |
rvargas | 86d7c90 | 2014-09-04 22:58:09 | [diff] [blame] | 221 | } |
| 222 | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 223 | #if defined(ARCH_CPU_32_BITS) |
| 224 | // static |
wfh | 360c186 | 2015-12-21 19:48:06 | [diff] [blame] | 225 | void HandleHooks::AddEATPatch() { |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 226 | // An attempt to restore the entry on the table at destruction is not safe. |
wfh | 360c186 | 2015-12-21 19:48:06 | [diff] [blame] | 227 | EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle", |
Nico Weber | 2638cbd | 2018-03-12 17:11:15 | [diff] [blame] | 228 | reinterpret_cast<void*>(&CloseHandleHook), |
| 229 | reinterpret_cast<void**>(&g_close_function)); |
wfh | 360c186 | 2015-12-21 19:48:06 | [diff] [blame] | 230 | EATPatch(GetModuleHandleA("kernel32.dll"), "DuplicateHandle", |
Nico Weber | 2638cbd | 2018-03-12 17:11:15 | [diff] [blame] | 231 | reinterpret_cast<void*>(&DuplicateHandleHook), |
wfh | 360c186 | 2015-12-21 19:48:06 | [diff] [blame] | 232 | reinterpret_cast<void**>(&g_duplicate_function)); |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 233 | } |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 234 | #endif // defined(ARCH_CPU_32_BITS) |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 235 | |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 236 | // static |
| 237 | void HandleHooks::PatchLoadedModules() { |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 238 | const DWORD kSize = 256; |
| 239 | DWORD returned; |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 240 | auto modules = std::make_unique<HMODULE[]>(kSize); |
| 241 | if (!::EnumProcessModules(GetCurrentProcess(), modules.get(), |
| 242 | kSize * sizeof(HMODULE), &returned)) { |
wfh | 360c186 | 2015-12-21 19:48:06 | [diff] [blame] | 243 | return; |
rvargas | 86d7c90 | 2014-09-04 22:58:09 | [diff] [blame] | 244 | } |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 245 | returned /= sizeof(HMODULE); |
| 246 | returned = std::min(kSize, returned); |
| 247 | |
| 248 | for (DWORD current = 0; current < returned; current++) { |
Will Harris | d4b45e0 | 2022-05-21 00:30:48 | [diff] [blame] | 249 | AddIATPatch(modules[current]); |
rvargas | ae02eef | 2014-09-30 18:42:24 | [diff] [blame] | 250 | } |
rvargas | 86d7c90 | 2014-09-04 22:58:09 | [diff] [blame] | 251 | } |
| 252 | |
wfh | b540830 | 2016-01-06 20:24:06 | [diff] [blame] | 253 | } // namespace debug |
| 254 | } // namespace base |