Home | History | Annotate | Download | only in src
      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
    203