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