michael@0: // Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: // Implementation of PreamblePatcher michael@0: michael@0: #include "sandbox/win/src/sidestep/preamble_patcher.h" michael@0: michael@0: #include "sandbox/win/src/sandbox_nt_util.h" michael@0: #include "sandbox/win/src/sidestep/mini_disassembler.h" michael@0: michael@0: // Definitions of assembly statements we need michael@0: #define ASM_JMP32REL 0xE9 michael@0: #define ASM_INT3 0xCC michael@0: michael@0: namespace { michael@0: michael@0: // Very basic memcpy. We are copying 4 to 12 bytes most of the time, so there michael@0: // is no attempt to optimize this code or have a general purpose function. michael@0: // We don't want to call the crt from this code. michael@0: inline void* RawMemcpy(void* destination, const void* source, size_t bytes) { michael@0: const char* from = reinterpret_cast(source); michael@0: char* to = reinterpret_cast(destination); michael@0: michael@0: for (size_t i = 0; i < bytes ; i++) michael@0: to[i] = from[i]; michael@0: michael@0: return destination; michael@0: } michael@0: michael@0: // Very basic memset. We are filling 1 to 7 bytes most of the time, so there michael@0: // is no attempt to optimize this code or have a general purpose function. michael@0: // We don't want to call the crt from this code. michael@0: inline void* RawMemset(void* destination, int value, size_t bytes) { michael@0: char* to = reinterpret_cast(destination); michael@0: michael@0: for (size_t i = 0; i < bytes ; i++) michael@0: to[i] = static_cast(value); michael@0: michael@0: return destination; michael@0: } michael@0: michael@0: } // namespace michael@0: michael@0: #define ASSERT(a, b) DCHECK_NT(a) michael@0: michael@0: namespace sidestep { michael@0: michael@0: SideStepError PreamblePatcher::RawPatchWithStub( michael@0: void* target_function, michael@0: void* replacement_function, michael@0: unsigned char* preamble_stub, michael@0: size_t stub_size, michael@0: size_t* bytes_needed) { michael@0: if ((NULL == target_function) || michael@0: (NULL == replacement_function) || michael@0: (NULL == preamble_stub)) { michael@0: ASSERT(false, (L"Invalid parameters - either pTargetFunction or " michael@0: L"pReplacementFunction or pPreambleStub were NULL.")); michael@0: return SIDESTEP_INVALID_PARAMETER; michael@0: } michael@0: michael@0: // TODO(V7:joi) Siggi and I just had a discussion and decided that both michael@0: // patching and unpatching are actually unsafe. We also discussed a michael@0: // method of making it safe, which is to freeze all other threads in the michael@0: // process, check their thread context to see if their eip is currently michael@0: // inside the block of instructions we need to copy to the stub, and if so michael@0: // wait a bit and try again, then unfreeze all threads once we've patched. michael@0: // Not implementing this for now since we're only using SideStep for unit michael@0: // testing, but if we ever use it for production code this is what we michael@0: // should do. michael@0: // michael@0: // NOTE: Stoyan suggests we can write 8 or even 10 bytes atomically using michael@0: // FPU instructions, and on newer processors we could use cmpxchg8b or michael@0: // cmpxchg16b. So it might be possible to do the patching/unpatching michael@0: // atomically and avoid having to freeze other threads. Note though, that michael@0: // doing it atomically does not help if one of the other threads happens michael@0: // to have its eip in the middle of the bytes you change while you change michael@0: // them. michael@0: unsigned char* target = reinterpret_cast(target_function); michael@0: michael@0: // Let's disassemble the preamble of the target function to see if we can michael@0: // patch, and to see how much of the preamble we need to take. We need 5 michael@0: // bytes for our jmp instruction, so let's find the minimum number of michael@0: // instructions to get 5 bytes. michael@0: MiniDisassembler disassembler; michael@0: unsigned int preamble_bytes = 0; michael@0: while (preamble_bytes < 5) { michael@0: InstructionType instruction_type = michael@0: disassembler.Disassemble(target + preamble_bytes, &preamble_bytes); michael@0: if (IT_JUMP == instruction_type) { michael@0: ASSERT(false, (L"Unable to patch because there is a jump instruction " michael@0: L"in the first 5 bytes.")); michael@0: return SIDESTEP_JUMP_INSTRUCTION; michael@0: } else if (IT_RETURN == instruction_type) { michael@0: ASSERT(false, (L"Unable to patch because function is too short")); michael@0: return SIDESTEP_FUNCTION_TOO_SMALL; michael@0: } else if (IT_GENERIC != instruction_type) { michael@0: ASSERT(false, (L"Disassembler encountered unsupported instruction " michael@0: L"(either unused or unknown")); michael@0: return SIDESTEP_UNSUPPORTED_INSTRUCTION; michael@0: } michael@0: } michael@0: michael@0: if (NULL != bytes_needed) michael@0: *bytes_needed = preamble_bytes + 5; michael@0: michael@0: // Inv: preamble_bytes is the number of bytes (at least 5) that we need to michael@0: // take from the preamble to have whole instructions that are 5 bytes or more michael@0: // in size total. The size of the stub required is cbPreamble + size of michael@0: // jmp (5) michael@0: if (preamble_bytes + 5 > stub_size) { michael@0: NOTREACHED_NT(); michael@0: return SIDESTEP_INSUFFICIENT_BUFFER; michael@0: } michael@0: michael@0: // First, copy the preamble that we will overwrite. michael@0: RawMemcpy(reinterpret_cast(preamble_stub), michael@0: reinterpret_cast(target), preamble_bytes); michael@0: michael@0: // Now, make a jmp instruction to the rest of the target function (minus the michael@0: // preamble bytes we moved into the stub) and copy it into our preamble-stub. michael@0: // find address to jump to, relative to next address after jmp instruction michael@0: #pragma warning(push) michael@0: #pragma warning(disable:4244) michael@0: // This assignment generates a warning because it is 32 bit specific. michael@0: int relative_offset_to_target_rest michael@0: = ((reinterpret_cast(target) + preamble_bytes) - michael@0: (preamble_stub + preamble_bytes + 5)); michael@0: #pragma warning(pop) michael@0: // jmp (Jump near, relative, displacement relative to next instruction) michael@0: preamble_stub[preamble_bytes] = ASM_JMP32REL; michael@0: // copy the address michael@0: RawMemcpy(reinterpret_cast(preamble_stub + preamble_bytes + 1), michael@0: reinterpret_cast(&relative_offset_to_target_rest), 4); michael@0: michael@0: // Inv: preamble_stub points to assembly code that will execute the michael@0: // original function by first executing the first cbPreamble bytes of the michael@0: // preamble, then jumping to the rest of the function. michael@0: michael@0: // Overwrite the first 5 bytes of the target function with a jump to our michael@0: // replacement function. michael@0: // (Jump near, relative, displacement relative to next instruction) michael@0: target[0] = ASM_JMP32REL; michael@0: michael@0: // Find offset from instruction after jmp, to the replacement function. michael@0: #pragma warning(push) michael@0: #pragma warning(disable:4244) michael@0: int offset_to_replacement_function = michael@0: reinterpret_cast(replacement_function) - michael@0: reinterpret_cast(target) - 5; michael@0: #pragma warning(pop) michael@0: // complete the jmp instruction michael@0: RawMemcpy(reinterpret_cast(target + 1), michael@0: reinterpret_cast(&offset_to_replacement_function), 4); michael@0: // Set any remaining bytes that were moved to the preamble-stub to INT3 so michael@0: // as not to cause confusion (otherwise you might see some strange michael@0: // instructions if you look at the disassembly, or even invalid michael@0: // instructions). Also, by doing this, we will break into the debugger if michael@0: // some code calls into this portion of the code. If this happens, it michael@0: // means that this function cannot be patched using this patcher without michael@0: // further thought. michael@0: if (preamble_bytes > 5) { michael@0: RawMemset(reinterpret_cast(target + 5), ASM_INT3, michael@0: preamble_bytes - 5); michael@0: } michael@0: michael@0: // Inv: The memory pointed to by target_function now points to a relative michael@0: // jump instruction that jumps over to the preamble_stub. The preamble michael@0: // stub contains the first stub_size bytes of the original target michael@0: // function's preamble code, followed by a relative jump back to the next michael@0: // instruction after the first cbPreamble bytes. michael@0: michael@0: return SIDESTEP_SUCCESS; michael@0: } michael@0: michael@0: }; // namespace sidestep michael@0: michael@0: #undef ASSERT