Home | History | Annotate | Download | only in sidestep
      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 
     18 namespace {
     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.
     23 inline 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.
     36 inline 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 
     49 namespace sidestep {
     50 
     51 SideStepError 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