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
|
// 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 "preamble_patcher.h"
#include "mini_disassembler.h"
// Definitions of assembly statements we need
#define ASM_JMP32REL 0xE9
#define ASM_INT3 0xCC
namespace sidestep {
SideStepError PreamblePatcher::RawPatchWithStub(
void* target_function,
void *replacement_function,
unsigned char* preamble_stub,
unsigned long stub_size,
unsigned long* bytes_needed) {
if ((NULL == target_function) ||
(NULL == replacement_function) ||
(NULL == preamble_stub)) {
ASSERT(false, "Invalid parameters - either pTargetFunction or "
"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);
// First, deal with a special case that we see with functions that
// point into an IAT table (including functions linked statically
// into the application): these function already starts with
// ASM_JMP32REL. For instance, malloc() might be implemented as a
// JMP to __malloc(). In that case, we replace the destination of
// the JMP (__malloc), rather than the JMP itself (malloc). This
// way we get the correct behavior no matter how malloc gets called.
if (target[0] == ASM_JMP32REL) {
// target[1-4] holds the place the jmp goes to, but it's
// relative to the next instruction.
int relative_offset; // Windows guarantees int is 4 bytes
ASSERT1(sizeof(relative_offset) == 4);
memcpy(reinterpret_cast<void*>(&relative_offset),
reinterpret_cast<void*>(target + 1), 4);
// I'd like to just say "target = target + 5 + relative_offset" here, but
// I can't, because the new target will need to have its protections set.
return RawPatchWithStubAndProtections(target + 5 + relative_offset,
replacement_function, preamble_stub,
stub_size, bytes_needed);
}
// 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, "Unable to patch because there is a jump instruction "
"in the first 5 bytes.");
return SIDESTEP_JUMP_INSTRUCTION;
} else if (IT_RETURN == instruction_type) {
ASSERT(false, "Unable to patch because function is too short");
return SIDESTEP_FUNCTION_TOO_SMALL;
} else if (IT_GENERIC != instruction_type) {
ASSERT(false, "Disassembler encountered unsupported instruction "
"(either unused or unknown)");
return SIDESTEP_UNSUPPORTED_INSTRUCTION;
}
}
if (NULL != bytes_needed)
*bytes_needed = preamble_bytes + 5;
// Inv: cbPreamble 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) {
ASSERT1(false);
return SIDESTEP_INSUFFICIENT_BUFFER;
}
// First, copy the preamble that we will overwrite.
memcpy(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
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4244)
#endif
int relative_offset_to_target_rest
= ((reinterpret_cast<unsigned char*>(target) + preamble_bytes) -
(preamble_stub + preamble_bytes + 5));
#ifdef _MSC_VER
#pragma warning(pop)
#endif
// jmp (Jump near, relative, displacement relative to next instruction)
preamble_stub[preamble_bytes] = ASM_JMP32REL;
// copy the address
memcpy(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.
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4244)
#endif
int offset_to_replacement_function =
reinterpret_cast<unsigned char*>(replacement_function) -
reinterpret_cast<unsigned char*>(target) - 5;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
// complete the jmp instruction
memcpy(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) {
memset(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
|