OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 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 "sandbox/win/src/sidestep_resolver.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include "base/win/pe_image.h" | |
10 #include "sandbox/win/src/sandbox_nt_util.h" | |
11 #include "sandbox/win/src/sidestep/preamble_patcher.h" | |
12 | |
13 namespace { | |
14 | |
15 const size_t kSizeOfSidestepStub = sidestep::kMaxPreambleStubSize; | |
16 | |
17 struct SidestepThunk { | |
18 char sidestep[kSizeOfSidestepStub]; // Storage for the sidestep stub. | |
19 int internal_thunk; // Dummy member to the beginning of the internal thunk. | |
20 }; | |
21 | |
22 struct SmartThunk { | |
23 const void* module_base; // Target module's base. | |
24 const void* interceptor; // Real interceptor. | |
25 SidestepThunk sidestep; // Standard sidestep thunk. | |
26 }; | |
27 | |
28 } // namespace | |
29 | |
30 namespace sandbox { | |
31 | |
32 NTSTATUS SidestepResolverThunk::Setup(const void* target_module, | |
33 const void* interceptor_module, | |
34 const char* target_name, | |
35 const char* interceptor_name, | |
36 const void* interceptor_entry_point, | |
37 void* thunk_storage, | |
38 size_t storage_bytes, | |
39 size_t* storage_used) { | |
40 NTSTATUS ret = Init(target_module, interceptor_module, target_name, | |
41 interceptor_name, interceptor_entry_point, | |
42 thunk_storage, storage_bytes); | |
43 if (!NT_SUCCESS(ret)) | |
44 return ret; | |
45 | |
46 SidestepThunk* thunk = reinterpret_cast<SidestepThunk*>(thunk_storage); | |
47 | |
48 size_t internal_bytes = storage_bytes - kSizeOfSidestepStub; | |
49 if (!SetInternalThunk(&thunk->internal_thunk, internal_bytes, thunk_storage, | |
50 interceptor_)) | |
51 return STATUS_BUFFER_TOO_SMALL; | |
52 | |
53 AutoProtectMemory memory; | |
54 ret = memory.ChangeProtection(target_, kSizeOfSidestepStub, PAGE_READWRITE); | |
55 if (!NT_SUCCESS(ret)) | |
56 return ret; | |
57 | |
58 sidestep::SideStepError rv = sidestep::PreamblePatcher::Patch( | |
59 target_, reinterpret_cast<void*>(&thunk->internal_thunk), thunk_storage, | |
60 kSizeOfSidestepStub); | |
61 | |
62 if (sidestep::SIDESTEP_INSUFFICIENT_BUFFER == rv) | |
63 return STATUS_BUFFER_TOO_SMALL; | |
64 | |
65 if (sidestep::SIDESTEP_SUCCESS != rv) | |
66 return STATUS_UNSUCCESSFUL; | |
67 | |
68 if (storage_used) | |
69 *storage_used = GetThunkSize(); | |
70 | |
71 return ret; | |
72 } | |
73 | |
74 size_t SidestepResolverThunk::GetThunkSize() const { | |
75 return GetInternalThunkSize() + kSizeOfSidestepStub; | |
76 } | |
77 | |
78 // This is basically a wrapper around the normal sidestep patch that extends | |
79 // the thunk to use a chained interceptor. It uses the fact that | |
80 // SetInternalThunk generates the code to pass as the first parameter whatever | |
81 // it receives as original_function; we let SidestepResolverThunk set this value | |
82 // to its saved code, and then we change it to our thunk data. | |
83 NTSTATUS SmartSidestepResolverThunk::Setup(const void* target_module, | |
84 const void* interceptor_module, | |
85 const char* target_name, | |
86 const char* interceptor_name, | |
87 const void* interceptor_entry_point, | |
88 void* thunk_storage, | |
89 size_t storage_bytes, | |
90 size_t* storage_used) { | |
91 if (storage_bytes < GetThunkSize()) | |
92 return STATUS_BUFFER_TOO_SMALL; | |
93 | |
94 SmartThunk* thunk = reinterpret_cast<SmartThunk*>(thunk_storage); | |
95 thunk->module_base = target_module; | |
96 | |
97 NTSTATUS ret; | |
98 if (interceptor_entry_point) { | |
99 thunk->interceptor = interceptor_entry_point; | |
100 } else { | |
101 ret = ResolveInterceptor(interceptor_module, interceptor_name, | |
102 &thunk->interceptor); | |
103 if (!NT_SUCCESS(ret)) | |
104 return ret; | |
105 } | |
106 | |
107 // Perform a standard sidestep patch on the last part of the thunk, but point | |
108 // to our internal smart interceptor. | |
109 size_t standard_bytes = storage_bytes - offsetof(SmartThunk, sidestep); | |
110 ret = SidestepResolverThunk::Setup(target_module, interceptor_module, | |
111 target_name, NULL, &SmartStub, | |
112 &thunk->sidestep, standard_bytes, NULL); | |
113 if (!NT_SUCCESS(ret)) | |
114 return ret; | |
115 | |
116 // Fix the internal thunk to pass the whole buffer to the interceptor. | |
117 SetInternalThunk(&thunk->sidestep.internal_thunk, GetInternalThunkSize(), | |
118 thunk_storage, &SmartStub); | |
119 | |
120 if (storage_used) | |
121 *storage_used = GetThunkSize(); | |
122 | |
123 return ret; | |
124 } | |
125 | |
126 size_t SmartSidestepResolverThunk::GetThunkSize() const { | |
127 return GetInternalThunkSize() + kSizeOfSidestepStub + | |
128 offsetof(SmartThunk, sidestep); | |
129 } | |
130 | |
131 // This code must basically either call the intended interceptor or skip the | |
132 // call and invoke instead the original function. In any case, we are saving | |
133 // the registers that may be trashed by our c++ code. | |
134 // | |
135 // This function is called with a first parameter inserted by us, that points | |
136 // to our SmartThunk. When we call the interceptor we have to replace this | |
137 // parameter with the one expected by that function (stored inside our | |
138 // structure); on the other hand, when we skip the interceptor we have to remove | |
139 // that extra argument before calling the original function. | |
140 // | |
141 // When we skip the interceptor, the transformation of the stack looks like: | |
142 // On Entry: On Use: On Exit: | |
143 // [param 2] = first real argument [param 2] (esp+1c) [param 2] | |
144 // [param 1] = our SmartThunk [param 1] (esp+18) [ret address] | |
145 // [ret address] = real caller [ret address] (esp+14) [xxx] | |
146 // [xxx] [addr to jump to] (esp+10) [xxx] | |
147 // [xxx] [saved eax] [xxx] | |
148 // [xxx] [saved ebx] [xxx] | |
149 // [xxx] [saved ecx] [xxx] | |
150 // [xxx] [saved edx] [xxx] | |
151 __declspec(naked) | |
152 void SmartSidestepResolverThunk::SmartStub() { | |
153 __asm { | |
154 push eax // Space for the jump. | |
155 push eax // Save registers. | |
156 push ebx | |
157 push ecx | |
158 push edx | |
159 mov ebx, [esp + 0x18] // First parameter = SmartThunk. | |
160 mov edx, [esp + 0x14] // Get the return address. | |
161 mov eax, [ebx]SmartThunk.module_base | |
162 push edx | |
163 push eax | |
164 call SmartSidestepResolverThunk::IsInternalCall | |
165 add esp, 8 | |
166 | |
167 test eax, eax | |
168 lea edx, [ebx]SmartThunk.sidestep // The original function. | |
169 jz call_interceptor | |
170 | |
171 // Skip this call | |
172 mov ecx, [esp + 0x14] // Return address. | |
173 mov [esp + 0x18], ecx // Remove first parameter. | |
174 mov [esp + 0x10], edx | |
175 pop edx // Restore registers. | |
176 pop ecx | |
177 pop ebx | |
178 pop eax | |
179 ret 4 // Jump to original function. | |
180 | |
181 call_interceptor: | |
182 mov ecx, [ebx]SmartThunk.interceptor | |
183 mov [esp + 0x18], edx // Replace first parameter. | |
184 mov [esp + 0x10], ecx | |
185 pop edx // Restore registers. | |
186 pop ecx | |
187 pop ebx | |
188 pop eax | |
189 ret // Jump to original function. | |
190 } | |
191 } | |
192 | |
193 bool SmartSidestepResolverThunk::IsInternalCall(const void* base, | |
194 void* return_address) { | |
195 DCHECK_NT(base); | |
196 DCHECK_NT(return_address); | |
197 | |
198 base::win::PEImage pe(base); | |
199 if (pe.GetImageSectionFromAddr(return_address)) | |
200 return true; | |
201 return false; | |
202 } | |
203 | |
204 } // namespace sandbox | |
OLD | NEW |