| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/debug/close_handle_hook_win.h" | 5 #include "chrome/app/close_handle_hook_win.h" |
| 6 | 6 |
| 7 #include <Windows.h> | 7 #include <Windows.h> |
| 8 #include <psapi.h> | 8 #include <psapi.h> |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| 11 #include <vector> | 11 #include <vector> |
| 12 | 12 |
| 13 #include "base/lazy_instance.h" | 13 #include "base/lazy_instance.h" |
| 14 #include "base/memory/scoped_ptr.h" | 14 #include "base/memory/scoped_ptr.h" |
| 15 #include "base/win/iat_patch_function.h" | 15 #include "base/win/iat_patch_function.h" |
| 16 #include "base/win/pe_image.h" | 16 #include "base/win/pe_image.h" |
| 17 #include "base/win/scoped_handle.h" | 17 #include "base/win/scoped_handle.h" |
| 18 #include "chrome/common/channel_info.h" |
| 19 #include "components/version_info/version_info.h" |
| 18 | 20 |
| 19 namespace { | 21 namespace { |
| 20 | 22 |
| 21 typedef BOOL (WINAPI* CloseHandleType) (HANDLE handle); | 23 typedef BOOL (WINAPI* CloseHandleType) (HANDLE handle); |
| 22 | 24 |
| 23 typedef BOOL (WINAPI* DuplicateHandleType)(HANDLE source_process, | 25 typedef BOOL (WINAPI* DuplicateHandleType)(HANDLE source_process, |
| 24 HANDLE source_handle, | 26 HANDLE source_handle, |
| 25 HANDLE target_process, | 27 HANDLE target_process, |
| 26 HANDLE* target_handle, | 28 HANDLE* target_handle, |
| 27 DWORD desired_access, | 29 DWORD desired_access, |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 112 DCHECK(bytes_); | 114 DCHECK(bytes_); |
| 113 | 115 |
| 114 VirtualProtect(address_, bytes_, old_protect_, &old_protect_); | 116 VirtualProtect(address_, bytes_, old_protect_, &old_protect_); |
| 115 changed_ = false; | 117 changed_ = false; |
| 116 address_ = NULL; | 118 address_ = NULL; |
| 117 bytes_ = 0; | 119 bytes_ = 0; |
| 118 old_protect_ = 0; | 120 old_protect_ = 0; |
| 119 } | 121 } |
| 120 | 122 |
| 121 // Performs an EAT interception. | 123 // Performs an EAT interception. |
| 122 bool EATPatch(HMODULE module, const char* function_name, | 124 void EATPatch(HMODULE module, const char* function_name, |
| 123 void* new_function, void** old_function) { | 125 void* new_function, void** old_function) { |
| 124 if (!module) | 126 if (!module) |
| 125 return false; | 127 return; |
| 126 | 128 |
| 127 base::win::PEImage pe(module); | 129 base::win::PEImage pe(module); |
| 128 if (!pe.VerifyMagic()) | 130 if (!pe.VerifyMagic()) |
| 129 return false; | 131 return; |
| 130 | 132 |
| 131 DWORD* eat_entry = pe.GetExportEntry(function_name); | 133 DWORD* eat_entry = pe.GetExportEntry(function_name); |
| 132 if (!eat_entry) | 134 if (!eat_entry) |
| 133 return false; | 135 return; |
| 134 | 136 |
| 135 if (!(*old_function)) | 137 if (!(*old_function)) |
| 136 *old_function = pe.RVAToAddr(*eat_entry); | 138 *old_function = pe.RVAToAddr(*eat_entry); |
| 137 | 139 |
| 138 AutoProtectMemory memory; | 140 AutoProtectMemory memory; |
| 139 if (!memory.ChangeProtection(eat_entry, sizeof(DWORD))) | 141 if (!memory.ChangeProtection(eat_entry, sizeof(DWORD))) |
| 140 return false; | 142 return; |
| 141 | 143 |
| 142 // Perform the patch. | 144 // Perform the patch. |
| 143 #pragma warning(push) | 145 #pragma warning(push) |
| 144 #pragma warning(disable : 4311 4302) | 146 #pragma warning(disable : 4311 4302) |
| 145 // These casts generate truncation warnings because they are 32 bit specific. | 147 // These casts generate truncation warnings because they are 32 bit specific. |
| 146 *eat_entry = reinterpret_cast<DWORD>(new_function) - | 148 *eat_entry = reinterpret_cast<DWORD>(new_function) - |
| 147 reinterpret_cast<DWORD>(module); | 149 reinterpret_cast<DWORD>(module); |
| 148 #pragma warning(pop) | 150 #pragma warning(pop) |
| 149 return true; | |
| 150 } | 151 } |
| 151 | 152 |
| 152 // Performs an IAT interception. | 153 // Performs an IAT interception. |
| 153 base::win::IATPatchFunction* IATPatch(HMODULE module, const char* function_name, | 154 base::win::IATPatchFunction* IATPatch(HMODULE module, const char* function_name, |
| 154 void* new_function, void** old_function) { | 155 void* new_function, void** old_function) { |
| 155 if (!module) | 156 if (!module) |
| 156 return NULL; | 157 return NULL; |
| 157 | 158 |
| 158 base::win::IATPatchFunction* patch = new base::win::IATPatchFunction; | 159 base::win::IATPatchFunction* patch = new base::win::IATPatchFunction; |
| 159 __try { | 160 __try { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 179 return patch; | 180 return patch; |
| 180 } | 181 } |
| 181 | 182 |
| 182 // Keeps track of all the hooks needed to intercept functions which could | 183 // Keeps track of all the hooks needed to intercept functions which could |
| 183 // possibly close handles. | 184 // possibly close handles. |
| 184 class HandleHooks { | 185 class HandleHooks { |
| 185 public: | 186 public: |
| 186 HandleHooks() {} | 187 HandleHooks() {} |
| 187 ~HandleHooks() {} | 188 ~HandleHooks() {} |
| 188 | 189 |
| 189 bool AddIATPatch(HMODULE module); | 190 void AddIATPatch(HMODULE module); |
| 190 bool AddEATPatch(); | 191 void AddEATPatch(); |
| 191 bool Unpatch(); | 192 void Unpatch(); |
| 192 | 193 |
| 193 private: | 194 private: |
| 194 std::vector<base::win::IATPatchFunction*> hooks_; | 195 std::vector<base::win::IATPatchFunction*> hooks_; |
| 195 DISALLOW_COPY_AND_ASSIGN(HandleHooks); | 196 DISALLOW_COPY_AND_ASSIGN(HandleHooks); |
| 196 }; | 197 }; |
| 197 base::LazyInstance<HandleHooks> g_hooks = LAZY_INSTANCE_INITIALIZER; | 198 base::LazyInstance<HandleHooks> g_hooks = LAZY_INSTANCE_INITIALIZER; |
| 198 | 199 |
| 199 bool HandleHooks::AddIATPatch(HMODULE module) { | 200 void HandleHooks::AddIATPatch(HMODULE module) { |
| 200 if (!module) | 201 if (!module) |
| 201 return false; | 202 return; |
| 202 | 203 |
| 203 base::win::IATPatchFunction* patch = NULL; | 204 base::win::IATPatchFunction* patch = NULL; |
| 204 patch = IATPatch(module, "CloseHandle", &CloseHandleHook, | 205 patch = IATPatch(module, "CloseHandle", &CloseHandleHook, |
| 205 reinterpret_cast<void**>(&g_close_function)); | 206 reinterpret_cast<void**>(&g_close_function)); |
| 206 if (!patch) | 207 if (!patch) |
| 207 return false; | 208 return; |
| 208 hooks_.push_back(patch); | 209 hooks_.push_back(patch); |
| 209 | 210 |
| 210 patch = IATPatch(module, "DuplicateHandle", &DuplicateHandleHook, | 211 patch = IATPatch(module, "DuplicateHandle", &DuplicateHandleHook, |
| 211 reinterpret_cast<void**>(&g_duplicate_function)); | 212 reinterpret_cast<void**>(&g_duplicate_function)); |
| 212 if (!patch) | 213 if (!patch) |
| 213 return false; | 214 return; |
| 214 hooks_.push_back(patch); | 215 hooks_.push_back(patch); |
| 215 return true; | |
| 216 } | 216 } |
| 217 | 217 |
| 218 bool HandleHooks::AddEATPatch() { | 218 void HandleHooks::AddEATPatch() { |
| 219 // An attempt to restore the entry on the table at destruction is not safe. | |
| 220 // An attempt to restore the entry on the table at destruction is not safe. | 219 // An attempt to restore the entry on the table at destruction is not safe. |
| 221 return (EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle", | 220 EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle", |
| 222 &CloseHandleHook, | 221 &CloseHandleHook, reinterpret_cast<void**>(&g_close_function)); |
| 223 reinterpret_cast<void**>(&g_close_function)) && | 222 EATPatch(GetModuleHandleA("kernel32.dll"), "DuplicateHandle", |
| 224 EATPatch(GetModuleHandleA("kernel32.dll"), "DuplicateHandle", | 223 &DuplicateHandleHook, |
| 225 &DuplicateHandleHook, | 224 reinterpret_cast<void**>(&g_duplicate_function)); |
| 226 reinterpret_cast<void**>(&g_duplicate_function))); | |
| 227 } | 225 } |
| 228 | 226 |
| 229 bool HandleHooks::Unpatch() { | 227 void HandleHooks::Unpatch() { |
| 230 DWORD err = NO_ERROR; | |
| 231 for (std::vector<base::win::IATPatchFunction*>::iterator it = hooks_.begin(); | 228 for (std::vector<base::win::IATPatchFunction*>::iterator it = hooks_.begin(); |
| 232 it != hooks_.end(); ++it) { | 229 it != hooks_.end(); ++it) { |
| 233 err = (*it)->Unpatch(); | 230 (*it)->Unpatch(); |
| 234 if (err != NO_ERROR) | |
| 235 break; | |
| 236 delete *it; | 231 delete *it; |
| 237 } | 232 } |
| 238 return (err == NO_ERROR); | |
| 239 } | 233 } |
| 240 | 234 |
| 241 bool PatchLoadedModules(HandleHooks* hooks) { | 235 bool UseHooks() { |
| 236 #if defined(ARCH_CPU_X86_64) |
| 237 return false; |
| 238 #elif defined(NDEBUG) |
| 239 version_info::Channel channel = chrome::GetChannel(); |
| 240 if (channel == version_info::Channel::CANARY || |
| 241 channel == version_info::Channel::DEV) { |
| 242 return true; |
| 243 } |
| 244 |
| 245 return false; |
| 246 #else // NDEBUG |
| 247 return true; |
| 248 #endif |
| 249 } |
| 250 |
| 251 void PatchLoadedModules(HandleHooks* hooks) { |
| 242 const DWORD kSize = 256; | 252 const DWORD kSize = 256; |
| 243 DWORD returned; | 253 DWORD returned; |
| 244 scoped_ptr<HMODULE[]> modules(new HMODULE[kSize]); | 254 scoped_ptr<HMODULE[]> modules(new HMODULE[kSize]); |
| 245 if (!EnumProcessModules(GetCurrentProcess(), modules.get(), | 255 if (!EnumProcessModules(GetCurrentProcess(), modules.get(), |
| 246 kSize * sizeof(HMODULE), &returned)) { | 256 kSize * sizeof(HMODULE), &returned)) { |
| 247 return false; | 257 return; |
| 248 } | 258 } |
| 249 returned /= sizeof(HMODULE); | 259 returned /= sizeof(HMODULE); |
| 250 returned = std::min(kSize, returned); | 260 returned = std::min(kSize, returned); |
| 251 | 261 |
| 252 bool success = false; | |
| 253 | |
| 254 for (DWORD current = 0; current < returned; current++) { | 262 for (DWORD current = 0; current < returned; current++) { |
| 255 success = hooks->AddIATPatch(modules[current]); | 263 hooks->AddIATPatch(modules[current]); |
| 256 if (!success) | |
| 257 break; | |
| 258 } | 264 } |
| 259 | |
| 260 return success; | |
| 261 } | 265 } |
| 262 | 266 |
| 263 } // namespace | 267 } // namespace |
| 264 | 268 |
| 265 namespace base { | 269 void InstallHandleHooks() { |
| 266 namespace debug { | 270 if (UseHooks()) { |
| 271 HandleHooks* hooks = g_hooks.Pointer(); |
| 267 | 272 |
| 268 bool InstallHandleHooks() { | 273 // Performing EAT interception first is safer in the presence of other |
| 269 HandleHooks* hooks = g_hooks.Pointer(); | 274 // threads attempting to call CloseHandle. |
| 270 | 275 hooks->AddEATPatch(); |
| 271 // Performing EAT interception first is safer in the presence of other | 276 PatchLoadedModules(hooks); |
| 272 // threads attempting to call CloseHandle. | 277 } else { |
| 273 return (hooks->AddEATPatch() && PatchLoadedModules(hooks)); | 278 base::win::DisableHandleVerifier(); |
| 279 } |
| 274 } | 280 } |
| 275 | 281 |
| 276 void RemoveHandleHooks() { | 282 void RemoveHandleHooks() { |
| 277 // We are patching all loaded modules without forcing them to stay in memory, | 283 // We are partching all loaded modules without forcing them to stay in memory, |
| 278 // removing patches is not safe. | 284 // removing patches is not safe. |
| 279 } | 285 } |
| 280 | |
| 281 } // namespace debug | |
| 282 } // namespace base | |
| OLD | NEW |