| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 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 "preamble_patcher.h" | |
| 6 #include "memory_hook.h" | |
| 7 #include "mini_disassembler.h" | |
| 8 | |
| 9 // compatibility shims | |
| 10 #include "base/logging.h" | |
| 11 | |
| 12 // Definitions of assembly statements we need | |
| 13 #define ASM_JMP32REL 0xE9 | |
| 14 #define ASM_INT3 0xCC | |
| 15 | |
| 16 namespace sidestep { | |
| 17 | |
| 18 SideStepError PreamblePatcher::RawPatchWithStubAndProtections( | |
| 19 void* target_function, void *replacement_function, | |
| 20 unsigned char* preamble_stub, unsigned long stub_size, | |
| 21 unsigned long* bytes_needed) { | |
| 22 // We need to be able to write to a process-local copy of the first | |
| 23 // MAX_PREAMBLE_STUB_SIZE bytes of target_function. We may be giving execute | |
| 24 // privilege to something that doesn't have it, but that's the price to pay | |
| 25 // for tools. | |
| 26 DWORD old_target_function_protect = 0; | |
| 27 BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function), | |
| 28 MAX_PREAMBLE_STUB_SIZE, | |
| 29 PAGE_EXECUTE_READWRITE, | |
| 30 &old_target_function_protect); | |
| 31 if (!succeeded) { | |
| 32 ASSERT(false, "Failed to make page containing target function " | |
| 33 "copy-on-write."); | |
| 34 return SIDESTEP_ACCESS_DENIED; | |
| 35 } | |
| 36 | |
| 37 SideStepError error_code = RawPatchWithStub(target_function, | |
| 38 replacement_function, | |
| 39 preamble_stub, | |
| 40 stub_size, | |
| 41 bytes_needed); | |
| 42 if (SIDESTEP_SUCCESS != error_code) { | |
| 43 ASSERT1(false); | |
| 44 return error_code; | |
| 45 } | |
| 46 | |
| 47 // Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of | |
| 48 // pTargetFunction to what they were before we started goofing around. | |
| 49 succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function), | |
| 50 MAX_PREAMBLE_STUB_SIZE, | |
| 51 old_target_function_protect, | |
| 52 &old_target_function_protect); | |
| 53 if (!succeeded) { | |
| 54 ASSERT(false, "Failed to restore protection to target function."); | |
| 55 // We must not return an error here because the function has actually | |
| 56 // been patched, and returning an error would likely cause our client | |
| 57 // code not to unpatch it. So we just keep going. | |
| 58 } | |
| 59 | |
| 60 // Flush the instruction cache to make sure the processor doesn't execute the | |
| 61 // old version of the instructions (before our patch). | |
| 62 // | |
| 63 // FlushInstructionCache is actually a no-op at least on single-processor | |
| 64 // XP machines. I'm not sure why this is so, but it is, yet I want to keep | |
| 65 // the call to the API here for correctness in case there is a difference in | |
| 66 // some variants of Windows/hardware. | |
| 67 succeeded = ::FlushInstructionCache(::GetCurrentProcess(), | |
| 68 target_function, | |
| 69 MAX_PREAMBLE_STUB_SIZE); | |
| 70 if (!succeeded) { | |
| 71 ASSERT(false, "Failed to flush instruction cache."); | |
| 72 // We must not return an error here because the function has actually | |
| 73 // been patched, and returning an error would likely cause our client | |
| 74 // code not to unpatch it. So we just keep going. | |
| 75 } | |
| 76 | |
| 77 return SIDESTEP_SUCCESS; | |
| 78 } | |
| 79 | |
| 80 SideStepError PreamblePatcher::RawPatch(void* target_function, | |
| 81 void* replacement_function, | |
| 82 void** original_function_stub) { | |
| 83 if (!target_function || !replacement_function || !original_function_stub || | |
| 84 (*original_function_stub) || target_function == replacement_function) { | |
| 85 ASSERT(false, "Preconditions not met"); | |
| 86 return SIDESTEP_INVALID_PARAMETER; | |
| 87 } | |
| 88 | |
| 89 // @see MAX_PREAMBLE_STUB_SIZE for an explanation of how we arrives at | |
| 90 // this size | |
| 91 unsigned char* preamble_stub = | |
| 92 reinterpret_cast<unsigned char*>( | |
| 93 MemoryHook::Alloc(sizeof(unsigned char) * MAX_PREAMBLE_STUB_SIZE)); | |
| 94 if (!preamble_stub) { | |
| 95 ASSERT(false, "Unable to allocate preamble-stub."); | |
| 96 return SIDESTEP_INSUFFICIENT_BUFFER; | |
| 97 } | |
| 98 | |
| 99 // Change the protection of the newly allocated preamble stub to | |
| 100 // PAGE_EXECUTE_READWRITE. This is required to work with DEP (Data | |
| 101 // Execution Prevention) which will cause an exception if code is executed | |
| 102 // from a page on which you do not have read access. | |
| 103 DWORD old_stub_protect = 0; | |
| 104 BOOL succeeded = VirtualProtect(preamble_stub, MAX_PREAMBLE_STUB_SIZE, | |
| 105 PAGE_EXECUTE_READWRITE, &old_stub_protect); | |
| 106 if (!succeeded) { | |
| 107 ASSERT(false, "Failed to make page preamble stub read-write-execute."); | |
| 108 delete[] preamble_stub; | |
| 109 return SIDESTEP_ACCESS_DENIED; | |
| 110 } | |
| 111 | |
| 112 SideStepError error_code = RawPatchWithStubAndProtections(target_function, | |
| 113 replacement_function, | |
| 114 preamble_stub, | |
| 115 MAX_PREAMBLE_STUB_SIZE, | |
| 116 NULL); | |
| 117 if (SIDESTEP_SUCCESS != error_code) { | |
| 118 ASSERT1(false); | |
| 119 delete[] preamble_stub; | |
| 120 return error_code; | |
| 121 } | |
| 122 | |
| 123 *original_function_stub = reinterpret_cast<void*>(preamble_stub); | |
| 124 | |
| 125 // NOTE: For hooking malloc/free, we don't want to use streams which | |
| 126 // allocate. Basically, we've hooked malloc, but not necessarily | |
| 127 // hooked free yet. To do anything which uses the heap could crash | |
| 128 // with a mismatched malloc/free! | |
| 129 //VLOG(1) << "PreamblePatcher::RawPatch successfully patched 0x" | |
| 130 // << target_function; | |
| 131 | |
| 132 return SIDESTEP_SUCCESS; | |
| 133 } | |
| 134 | |
| 135 SideStepError PreamblePatcher::Unpatch(void* target_function, | |
| 136 void* replacement_function, | |
| 137 void* original_function_stub) { | |
| 138 ASSERT1(target_function && original_function_stub); | |
| 139 if (!target_function || !original_function_stub) { | |
| 140 return SIDESTEP_INVALID_PARAMETER; | |
| 141 } | |
| 142 | |
| 143 // We disassemble the preamble of the _stub_ to see how many bytes we | |
| 144 // originally copied to the stub. | |
| 145 MiniDisassembler disassembler; | |
| 146 unsigned int preamble_bytes = 0; | |
| 147 while (preamble_bytes < 5) { | |
| 148 InstructionType instruction_type = disassembler.Disassemble( | |
| 149 reinterpret_cast<unsigned char*>(original_function_stub) + | |
| 150 preamble_bytes, preamble_bytes); | |
| 151 if (IT_GENERIC != instruction_type) { | |
| 152 ASSERT(false, "Should only have generic instructions in stub!!"); | |
| 153 return SIDESTEP_UNSUPPORTED_INSTRUCTION; | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 // Before unpatching, target_function should be a JMP to | |
| 158 // replacement_function. If it's not, then either it's an error, or | |
| 159 // we're falling into the case where the original instruction was a | |
| 160 // JMP, and we patched the jumped_to address rather than the JMP | |
| 161 // itself. (For instance, if malloc() is just a JMP to __malloc(), | |
| 162 // we patched __malloc() and not malloc().) | |
| 163 unsigned char* target = reinterpret_cast<unsigned char*>(target_function); | |
| 164 while (1) { // we stop when target is a JMP to replacement_function | |
| 165 if (target[0] != ASM_JMP32REL) { | |
| 166 ASSERT(false, "target_function does not look like it was patched."); | |
| 167 return SIDESTEP_INVALID_PARAMETER; | |
| 168 } | |
| 169 int relative_offset; // Windows guarantees int is 4 bytes | |
| 170 ASSERT1(sizeof(relative_offset) == 4); | |
| 171 memcpy(reinterpret_cast<void*>(&relative_offset), | |
| 172 reinterpret_cast<void*>(target + 1), 4); | |
| 173 unsigned char* jump_to = target + 5 + relative_offset; | |
| 174 if (jump_to == replacement_function) | |
| 175 break; | |
| 176 target = jump_to; // follow the jmp | |
| 177 } | |
| 178 | |
| 179 // We need to be able to write to a process-local copy of the first | |
| 180 // MAX_PREAMBLE_STUB_SIZE bytes of target_function. We may be giving execute | |
| 181 // privilege to something that doesn't have it, but that's the price to pay | |
| 182 // for tools. | |
| 183 DWORD old_target_function_protect = 0; | |
| 184 BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target), | |
| 185 MAX_PREAMBLE_STUB_SIZE, | |
| 186 PAGE_EXECUTE_READWRITE, | |
| 187 &old_target_function_protect); | |
| 188 if (!succeeded) { | |
| 189 ASSERT(false, "Failed to make page containing target function " | |
| 190 "copy-on-write."); | |
| 191 return SIDESTEP_ACCESS_DENIED; | |
| 192 } | |
| 193 | |
| 194 // Replace the first few bytes of the original function with the bytes we | |
| 195 // previously moved to the preamble stub. | |
| 196 memcpy(reinterpret_cast<void*>(target), | |
| 197 original_function_stub, preamble_bytes); | |
| 198 | |
| 199 // Stub is now useless so delete it. | |
| 200 // [csilvers: Commented out for perftools because it causes big problems | |
| 201 // when we're unpatching malloc. We just let this live on as a leak.] | |
| 202 //delete original_function_stub; | |
| 203 | |
| 204 // Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of | |
| 205 // target to what they were before we started goofing around. | |
| 206 succeeded = ::VirtualProtect(reinterpret_cast<void*>(target), | |
| 207 MAX_PREAMBLE_STUB_SIZE, | |
| 208 old_target_function_protect, | |
| 209 &old_target_function_protect); | |
| 210 | |
| 211 // Flush the instruction cache to make sure the processor doesn't execute the | |
| 212 // old version of the instructions (before our patch). | |
| 213 // | |
| 214 // See comment on FlushInstructionCache elsewhere in this file. | |
| 215 succeeded = ::FlushInstructionCache(::GetCurrentProcess(), | |
| 216 target, | |
| 217 MAX_PREAMBLE_STUB_SIZE); | |
| 218 if (!succeeded) { | |
| 219 ASSERT(false, "Failed to flush instruction cache."); | |
| 220 return SIDESTEP_UNEXPECTED; | |
| 221 } | |
| 222 | |
| 223 VLOG(1) << "PreamblePatcher::Unpatch successfully unpatched 0x" | |
| 224 << target_function; | |
| 225 return SIDESTEP_SUCCESS; | |
| 226 } | |
| 227 | |
| 228 }; // namespace sidestep | |
| OLD | NEW |