• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Setup(const void * target_module,const void * interceptor_module,const char * target_name,const char * interceptor_name,const void * interceptor_entry_point,void * thunk_storage,size_t storage_bytes,size_t * storage_used)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 
GetThunkSize() const72 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.
Setup(const void * target_module,const void * interceptor_module,const char * target_name,const char * interceptor_name,const void * interceptor_entry_point,void * thunk_storage,size_t storage_bytes,size_t * storage_used)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 
GetThunkSize() const124 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)
SmartStub()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 
IsInternalCall(const void * base,void * return_address)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
203