1// Copyright (c) 2012 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// Implementation of PreamblePatcher 6 7#include "sandbox/win/src/sidestep/preamble_patcher.h" 8 9#include <stddef.h> 10 11#include "sandbox/win/src/sandbox_nt_util.h" 12#include "sandbox/win/src/sidestep/mini_disassembler.h" 13 14// Definitions of assembly statements we need 15#define ASM_JMP32REL 0xE9 16#define ASM_INT3 0xCC 17 18namespace { 19 20// Very basic memcpy. We are copying 4 to 12 bytes most of the time, so there 21// is no attempt to optimize this code or have a general purpose function. 22// We don't want to call the crt from this code. 23inline void* RawMemcpy(void* destination, const void* source, size_t bytes) { 24 const char* from = reinterpret_cast<const char*>(source); 25 char* to = reinterpret_cast<char*>(destination); 26 27 for (size_t i = 0; i < bytes ; i++) 28 to[i] = from[i]; 29 30 return destination; 31} 32 33// Very basic memset. We are filling 1 to 7 bytes most of the time, so there 34// is no attempt to optimize this code or have a general purpose function. 35// We don't want to call the crt from this code. 36inline void* RawMemset(void* destination, int value, size_t bytes) { 37 char* to = reinterpret_cast<char*>(destination); 38 39 for (size_t i = 0; i < bytes ; i++) 40 to[i] = static_cast<char>(value); 41 42 return destination; 43} 44 45} // namespace 46 47#define ASSERT(a, b) DCHECK_NT(a) 48 49namespace sidestep { 50 51SideStepError PreamblePatcher::RawPatchWithStub( 52 void* target_function, 53 void* replacement_function, 54 unsigned char* preamble_stub, 55 size_t stub_size, 56 size_t* bytes_needed) { 57 if ((NULL == target_function) || 58 (NULL == replacement_function) || 59 (NULL == preamble_stub)) { 60 ASSERT(false, (L"Invalid parameters - either pTargetFunction or " 61 L"pReplacementFunction or pPreambleStub were NULL.")); 62 return SIDESTEP_INVALID_PARAMETER; 63 } 64 65 // TODO(V7:joi) Siggi and I just had a discussion and decided that both 66 // patching and unpatching are actually unsafe. We also discussed a 67 // method of making it safe, which is to freeze all other threads in the 68 // process, check their thread context to see if their eip is currently 69 // inside the block of instructions we need to copy to the stub, and if so 70 // wait a bit and try again, then unfreeze all threads once we've patched. 71 // Not implementing this for now since we're only using SideStep for unit 72 // testing, but if we ever use it for production code this is what we 73 // should do. 74 // 75 // NOTE: Stoyan suggests we can write 8 or even 10 bytes atomically using 76 // FPU instructions, and on newer processors we could use cmpxchg8b or 77 // cmpxchg16b. So it might be possible to do the patching/unpatching 78 // atomically and avoid having to freeze other threads. Note though, that 79 // doing it atomically does not help if one of the other threads happens 80 // to have its eip in the middle of the bytes you change while you change 81 // them. 82 unsigned char* target = reinterpret_cast<unsigned char*>(target_function); 83 84 // Let's disassemble the preamble of the target function to see if we can 85 // patch, and to see how much of the preamble we need to take. We need 5 86 // bytes for our jmp instruction, so let's find the minimum number of 87 // instructions to get 5 bytes. 88 MiniDisassembler disassembler; 89 unsigned int preamble_bytes = 0; 90 while (preamble_bytes < 5) { 91 InstructionType instruction_type = 92 disassembler.Disassemble(target + preamble_bytes, &preamble_bytes); 93 if (IT_JUMP == instruction_type) { 94 ASSERT(false, (L"Unable to patch because there is a jump instruction " 95 L"in the first 5 bytes.")); 96 return SIDESTEP_JUMP_INSTRUCTION; 97 } else if (IT_RETURN == instruction_type) { 98 ASSERT(false, (L"Unable to patch because function is too short")); 99 return SIDESTEP_FUNCTION_TOO_SMALL; 100 } else if (IT_GENERIC != instruction_type) { 101 ASSERT(false, (L"Disassembler encountered unsupported instruction " 102 L"(either unused or unknown")); 103 return SIDESTEP_UNSUPPORTED_INSTRUCTION; 104 } 105 } 106 107 if (NULL != bytes_needed) 108 *bytes_needed = preamble_bytes + 5; 109 110 // Inv: preamble_bytes is the number of bytes (at least 5) that we need to 111 // take from the preamble to have whole instructions that are 5 bytes or more 112 // in size total. The size of the stub required is cbPreamble + size of 113 // jmp (5) 114 if (preamble_bytes + 5 > stub_size) { 115 NOTREACHED_NT(); 116 return SIDESTEP_INSUFFICIENT_BUFFER; 117 } 118 119 // First, copy the preamble that we will overwrite. 120 RawMemcpy(reinterpret_cast<void*>(preamble_stub), 121 reinterpret_cast<void*>(target), preamble_bytes); 122 123 // Now, make a jmp instruction to the rest of the target function (minus the 124 // preamble bytes we moved into the stub) and copy it into our preamble-stub. 125 // find address to jump to, relative to next address after jmp instruction 126#pragma warning(push) 127#pragma warning(disable:4244) 128 // This assignment generates a warning because it is 32 bit specific. 129 int relative_offset_to_target_rest 130 = ((reinterpret_cast<unsigned char*>(target) + preamble_bytes) - 131 (preamble_stub + preamble_bytes + 5)); 132#pragma warning(pop) 133 // jmp (Jump near, relative, displacement relative to next instruction) 134 preamble_stub[preamble_bytes] = ASM_JMP32REL; 135 // copy the address 136 RawMemcpy(reinterpret_cast<void*>(preamble_stub + preamble_bytes + 1), 137 reinterpret_cast<void*>(&relative_offset_to_target_rest), 4); 138 139 // Inv: preamble_stub points to assembly code that will execute the 140 // original function by first executing the first cbPreamble bytes of the 141 // preamble, then jumping to the rest of the function. 142 143 // Overwrite the first 5 bytes of the target function with a jump to our 144 // replacement function. 145 // (Jump near, relative, displacement relative to next instruction) 146 target[0] = ASM_JMP32REL; 147 148 // Find offset from instruction after jmp, to the replacement function. 149#pragma warning(push) 150#pragma warning(disable:4244) 151 int offset_to_replacement_function = 152 reinterpret_cast<unsigned char*>(replacement_function) - 153 reinterpret_cast<unsigned char*>(target) - 5; 154#pragma warning(pop) 155 // complete the jmp instruction 156 RawMemcpy(reinterpret_cast<void*>(target + 1), 157 reinterpret_cast<void*>(&offset_to_replacement_function), 4); 158 // Set any remaining bytes that were moved to the preamble-stub to INT3 so 159 // as not to cause confusion (otherwise you might see some strange 160 // instructions if you look at the disassembly, or even invalid 161 // instructions). Also, by doing this, we will break into the debugger if 162 // some code calls into this portion of the code. If this happens, it 163 // means that this function cannot be patched using this patcher without 164 // further thought. 165 if (preamble_bytes > 5) { 166 RawMemset(reinterpret_cast<void*>(target + 5), ASM_INT3, 167 preamble_bytes - 5); 168 } 169 170 // Inv: The memory pointed to by target_function now points to a relative 171 // jump instruction that jumps over to the preamble_stub. The preamble 172 // stub contains the first stub_size bytes of the original target 173 // function's preamble code, followed by a relative jump back to the next 174 // instruction after the first cbPreamble bytes. 175 176 return SIDESTEP_SUCCESS; 177} 178 179}; // namespace sidestep 180 181#undef ASSERT 182