summaryrefslogtreecommitdiffstats
path: root/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp
blob: 999d76bb21a8004ec25a7a7205454268f2813f0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
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