OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 #include "preamble_patcher.h" | |
6 #include "memory_hook.h" | |
7 #include "mini_disassembler.h" | |
8 | |
9 // compatibility shims | |
10 #include "base/logging.h" | |
11 | |
12 // Definitions of assembly statements we need | |
13 #define ASM_JMP32REL 0xE9 | |
14 #define ASM_INT3 0xCC | |
15 | |
16 namespace sidestep { | |
17 | |
18 SideStepError PreamblePatcher::RawPatchWithStubAndProtections( | |
19 void* target_function, void *replacement_function, | |
20 unsigned char* preamble_stub, unsigned long stub_size, | |
21 unsigned long* bytes_needed) { | |
22 // We need to be able to write to a process-local copy of the first | |
23 // MAX_PREAMBLE_STUB_SIZE bytes of target_function. We may be giving execute | |
24 // privilege to something that doesn't have it, but that's the price to pay | |
25 // for tools. | |
26 DWORD old_target_function_protect = 0; | |
27 BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function), | |
28 MAX_PREAMBLE_STUB_SIZE, | |
29 PAGE_EXECUTE_READWRITE, | |
30 &old_target_function_protect); | |
31 if (!succeeded) { | |
32 ASSERT(false, "Failed to make page containing target function " | |
33 "copy-on-write."); | |
34 return SIDESTEP_ACCESS_DENIED; | |
35 } | |
36 | |
37 SideStepError error_code = RawPatchWithStub(target_function, | |
38 replacement_function, | |
39 preamble_stub, | |
40 stub_size, | |
41 bytes_needed); | |
42 if (SIDESTEP_SUCCESS != error_code) { | |
43 ASSERT1(false); | |
44 return error_code; | |
45 } | |
46 | |
47 // Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of | |
48 // pTargetFunction to what they were before we started goofing around. | |
49 succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function), | |
50 MAX_PREAMBLE_STUB_SIZE, | |
51 old_target_function_protect, | |
52 &old_target_function_protect); | |
53 if (!succeeded) { | |
54 ASSERT(false, "Failed to restore protection to target function."); | |
55 // We must not return an error here because the function has actually | |
56 // been patched, and returning an error would likely cause our client | |
57 // code not to unpatch it. So we just keep going. | |
58 } | |
59 | |
60 // Flush the instruction cache to make sure the processor doesn't execute the | |
61 // old version of the instructions (before our patch). | |
62 // | |
63 // FlushInstructionCache is actually a no-op at least on single-processor | |
64 // XP machines. I'm not sure why this is so, but it is, yet I want to keep | |
65 // the call to the API here for correctness in case there is a difference in | |
66 // some variants of Windows/hardware. | |
67 succeeded = ::FlushInstructionCache(::GetCurrentProcess(), | |
68 target_function, | |
69 MAX_PREAMBLE_STUB_SIZE); | |
70 if (!succeeded) { | |
71 ASSERT(false, "Failed to flush instruction cache."); | |
72 // We must not return an error here because the function has actually | |
73 // been patched, and returning an error would likely cause our client | |
74 // code not to unpatch it. So we just keep going. | |
75 } | |
76 | |
77 return SIDESTEP_SUCCESS; | |
78 } | |
79 | |
80 SideStepError PreamblePatcher::RawPatch(void* target_function, | |
81 void* replacement_function, | |
82 void** original_function_stub) { | |
83 if (!target_function || !replacement_function || !original_function_stub || | |
84 (*original_function_stub) || target_function == replacement_function) { | |
85 ASSERT(false, "Preconditions not met"); | |
86 return SIDESTEP_INVALID_PARAMETER; | |
87 } | |
88 | |
89 // @see MAX_PREAMBLE_STUB_SIZE for an explanation of how we arrives at | |
90 // this size | |
91 unsigned char* preamble_stub = | |
92 reinterpret_cast<unsigned char*>( | |
93 MemoryHook::Alloc(sizeof(unsigned char) * MAX_PREAMBLE_STUB_SIZE)); | |
94 if (!preamble_stub) { | |
95 ASSERT(false, "Unable to allocate preamble-stub."); | |
96 return SIDESTEP_INSUFFICIENT_BUFFER; | |
97 } | |
98 | |
99 // Change the protection of the newly allocated preamble stub to | |
100 // PAGE_EXECUTE_READWRITE. This is required to work with DEP (Data | |
101 // Execution Prevention) which will cause an exception if code is executed | |
102 // from a page on which you do not have read access. | |
103 DWORD old_stub_protect = 0; | |
104 BOOL succeeded = VirtualProtect(preamble_stub, MAX_PREAMBLE_STUB_SIZE, | |
105 PAGE_EXECUTE_READWRITE, &old_stub_protect); | |
106 if (!succeeded) { | |
107 ASSERT(false, "Failed to make page preamble stub read-write-execute."); | |
108 delete[] preamble_stub; | |
109 return SIDESTEP_ACCESS_DENIED; | |
110 } | |
111 | |
112 SideStepError error_code = RawPatchWithStubAndProtections(target_function, | |
113 replacement_function, | |
114 preamble_stub, | |
115 MAX_PREAMBLE_STUB_SIZE, | |
116 NULL); | |
117 if (SIDESTEP_SUCCESS != error_code) { | |
118 ASSERT1(false); | |
119 delete[] preamble_stub; | |
120 return error_code; | |
121 } | |
122 | |
123 *original_function_stub = reinterpret_cast<void*>(preamble_stub); | |
124 | |
125 // NOTE: For hooking malloc/free, we don't want to use streams which | |
126 // allocate. Basically, we've hooked malloc, but not necessarily | |
127 // hooked free yet. To do anything which uses the heap could crash | |
128 // with a mismatched malloc/free! | |
129 //VLOG(1) << "PreamblePatcher::RawPatch successfully patched 0x" | |
130 // << target_function; | |
131 | |
132 return SIDESTEP_SUCCESS; | |
133 } | |
134 | |
135 SideStepError PreamblePatcher::Unpatch(void* target_function, | |
136 void* replacement_function, | |
137 void* original_function_stub) { | |
138 ASSERT1(target_function && original_function_stub); | |
139 if (!target_function || !original_function_stub) { | |
140 return SIDESTEP_INVALID_PARAMETER; | |
141 } | |
142 | |
143 // We disassemble the preamble of the _stub_ to see how many bytes we | |
144 // originally copied to the stub. | |
145 MiniDisassembler disassembler; | |
146 unsigned int preamble_bytes = 0; | |
147 while (preamble_bytes < 5) { | |
148 InstructionType instruction_type = disassembler.Disassemble( | |
149 reinterpret_cast<unsigned char*>(original_function_stub) + | |
150 preamble_bytes, preamble_bytes); | |
151 if (IT_GENERIC != instruction_type) { | |
152 ASSERT(false, "Should only have generic instructions in stub!!"); | |
153 return SIDESTEP_UNSUPPORTED_INSTRUCTION; | |
154 } | |
155 } | |
156 | |
157 // Before unpatching, target_function should be a JMP to | |
158 // replacement_function. If it's not, then either it's an error, or | |
159 // we're falling into the case where the original instruction was a | |
160 // JMP, and we patched the jumped_to address rather than the JMP | |
161 // itself. (For instance, if malloc() is just a JMP to __malloc(), | |
162 // we patched __malloc() and not malloc().) | |
163 unsigned char* target = reinterpret_cast<unsigned char*>(target_function); | |
164 while (1) { // we stop when target is a JMP to replacement_function | |
165 if (target[0] != ASM_JMP32REL) { | |
166 ASSERT(false, "target_function does not look like it was patched."); | |
167 return SIDESTEP_INVALID_PARAMETER; | |
168 } | |
169 int relative_offset; // Windows guarantees int is 4 bytes | |
170 ASSERT1(sizeof(relative_offset) == 4); | |
171 memcpy(reinterpret_cast<void*>(&relative_offset), | |
172 reinterpret_cast<void*>(target + 1), 4); | |
173 unsigned char* jump_to = target + 5 + relative_offset; | |
174 if (jump_to == replacement_function) | |
175 break; | |
176 target = jump_to; // follow the jmp | |
177 } | |
178 | |
179 // We need to be able to write to a process-local copy of the first | |
180 // MAX_PREAMBLE_STUB_SIZE bytes of target_function. We may be giving execute | |
181 // privilege to something that doesn't have it, but that's the price to pay | |
182 // for tools. | |
183 DWORD old_target_function_protect = 0; | |
184 BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target), | |
185 MAX_PREAMBLE_STUB_SIZE, | |
186 PAGE_EXECUTE_READWRITE, | |
187 &old_target_function_protect); | |
188 if (!succeeded) { | |
189 ASSERT(false, "Failed to make page containing target function " | |
190 "copy-on-write."); | |
191 return SIDESTEP_ACCESS_DENIED; | |
192 } | |
193 | |
194 // Replace the first few bytes of the original function with the bytes we | |
195 // previously moved to the preamble stub. | |
196 memcpy(reinterpret_cast<void*>(target), | |
197 original_function_stub, preamble_bytes); | |
198 | |
199 // Stub is now useless so delete it. | |
200 // [csilvers: Commented out for perftools because it causes big problems | |
201 // when we're unpatching malloc. We just let this live on as a leak.] | |
202 //delete original_function_stub; | |
203 | |
204 // Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of | |
205 // target to what they were before we started goofing around. | |
206 succeeded = ::VirtualProtect(reinterpret_cast<void*>(target), | |
207 MAX_PREAMBLE_STUB_SIZE, | |
208 old_target_function_protect, | |
209 &old_target_function_protect); | |
210 | |
211 // Flush the instruction cache to make sure the processor doesn't execute the | |
212 // old version of the instructions (before our patch). | |
213 // | |
214 // See comment on FlushInstructionCache elsewhere in this file. | |
215 succeeded = ::FlushInstructionCache(::GetCurrentProcess(), | |
216 target, | |
217 MAX_PREAMBLE_STUB_SIZE); | |
218 if (!succeeded) { | |
219 ASSERT(false, "Failed to flush instruction cache."); | |
220 return SIDESTEP_UNEXPECTED; | |
221 } | |
222 | |
223 VLOG(1) << "PreamblePatcher::Unpatch successfully unpatched 0x" | |
224 << target_function; | |
225 return SIDESTEP_SUCCESS; | |
226 } | |
227 | |
228 }; // namespace sidestep | |
OLD | NEW |