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.
5 #include "sandbox/win/src/sidestep_resolver.h"
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"
13 const size_t kSizeOfSidestepStub
= sidestep::kMaxPreambleStubSize
;
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.
21 const void* module_base
; // Target module's base.
22 const void* interceptor
; // Real interceptor.
23 SidestepThunk sidestep
; // Standard sidestep thunk.
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
,
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
);
44 SidestepThunk
* thunk
= reinterpret_cast<SidestepThunk
*>(thunk_storage
);
46 size_t internal_bytes
= storage_bytes
- kSizeOfSidestepStub
;
47 if (!SetInternalThunk(&thunk
->internal_thunk
, internal_bytes
, thunk_storage
,
49 return STATUS_BUFFER_TOO_SMALL
;
51 AutoProtectMemory memory
;
52 ret
= memory
.ChangeProtection(target_
, kSizeOfSidestepStub
, PAGE_READWRITE
);
56 sidestep::SideStepError rv
= sidestep::PreamblePatcher::Patch(
57 target_
, reinterpret_cast<void*>(&thunk
->internal_thunk
), thunk_storage
,
60 if (sidestep::SIDESTEP_INSUFFICIENT_BUFFER
== rv
)
61 return STATUS_BUFFER_TOO_SMALL
;
63 if (sidestep::SIDESTEP_SUCCESS
!= rv
)
64 return STATUS_UNSUCCESSFUL
;
67 *storage_used
= GetThunkSize();
72 size_t SidestepResolverThunk::GetThunkSize() const {
73 return GetInternalThunkSize() + kSizeOfSidestepStub
;
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
,
88 size_t* storage_used
) {
89 if (storage_bytes
< GetThunkSize())
90 return STATUS_BUFFER_TOO_SMALL
;
92 SmartThunk
* thunk
= reinterpret_cast<SmartThunk
*>(thunk_storage
);
93 thunk
->module_base
= target_module
;
96 if (interceptor_entry_point
) {
97 thunk
->interceptor
= interceptor_entry_point
;
99 ret
= ResolveInterceptor(interceptor_module
, interceptor_name
,
100 &thunk
->interceptor
);
101 if (!NT_SUCCESS(ret
))
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
))
114 // Fix the internal thunk to pass the whole buffer to the interceptor.
115 SetInternalThunk(&thunk
->sidestep
.internal_thunk
, GetInternalThunkSize(),
116 thunk_storage
, &SmartStub
);
119 *storage_used
= GetThunkSize();
124 size_t SmartSidestepResolverThunk::GetThunkSize() const {
125 return GetInternalThunkSize() + kSizeOfSidestepStub
+
126 offsetof(SmartThunk
, sidestep
);
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.
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.
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]
150 void SmartSidestepResolverThunk::SmartStub() {
152 push eax
// Space for the jump.
153 push eax
// Save registers.
157 mov ebx
, [esp
+ 0x18] // First parameter = SmartThunk.
158 mov edx
, [esp
+ 0x14] // Get the return address.
159 mov eax
, [ebx
]SmartThunk
.module_base
162 call
SmartSidestepResolverThunk::IsInternalCall
166 lea edx
, [ebx
]SmartThunk
.sidestep
// The original function.
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.
177 ret
4 // Jump to original function.
180 mov ecx
, [ebx
]SmartThunk
.interceptor
181 mov
[esp
+ 0x18], edx
// Replace first parameter.
182 mov
[esp
+ 0x10], ecx
183 pop edx
// Restore registers.
187 ret
// Jump to original function.
191 bool SmartSidestepResolverThunk::IsInternalCall(const void* base
,
192 void* return_address
) {
194 DCHECK_NT(return_address
);
196 base::win::PEImage
pe(base
);
197 if (pe
.GetImageSectionFromAddr(return_address
))
202 } // namespace sandbox