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