security/sandbox/win/src/sidestep_resolver.cc

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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"
    11 namespace {
    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.
    18 };
    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 };
    26 }  // namespace
    28 namespace sandbox {
    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;
    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,
    48                         interceptor_))
    49     return STATUS_BUFFER_TOO_SMALL;
    51   AutoProtectMemory memory;
    52   ret = memory.ChangeProtection(target_, kSizeOfSidestepStub, PAGE_READWRITE);
    53   if (!NT_SUCCESS(ret))
    54     return ret;
    56   sidestep::SideStepError rv = sidestep::PreamblePatcher::Patch(
    57       target_, reinterpret_cast<void*>(&thunk->internal_thunk), thunk_storage,
    58       kSizeOfSidestepStub);
    60   if (sidestep::SIDESTEP_INSUFFICIENT_BUFFER == rv)
    61     return STATUS_BUFFER_TOO_SMALL;
    63   if (sidestep::SIDESTEP_SUCCESS != rv)
    64     return STATUS_UNSUCCESSFUL;
    66   if (storage_used)
    67     *storage_used = GetThunkSize();
    69   return ret;
    70 }
    72 size_t SidestepResolverThunk::GetThunkSize() const {
    73   return GetInternalThunkSize() + kSizeOfSidestepStub;
    74 }
    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;
    92   SmartThunk* thunk = reinterpret_cast<SmartThunk*>(thunk_storage);
    93   thunk->module_base = target_module;
    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   }
   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;
   114   // Fix the internal thunk to pass the whole buffer to the interceptor.
   115   SetInternalThunk(&thunk->sidestep.internal_thunk, GetInternalThunkSize(),
   116                    thunk_storage, &SmartStub);
   118   if (storage_used)
   119     *storage_used = GetThunkSize();
   121   return ret;
   122 }
   124 size_t SmartSidestepResolverThunk::GetThunkSize() const {
   125   return GetInternalThunkSize() + kSizeOfSidestepStub +
   126          offsetof(SmartThunk, sidestep);
   127 }
   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
   165     test eax, eax
   166     lea edx, [ebx]SmartThunk.sidestep   // The original function.
   167     jz call_interceptor
   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.
   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 }
   191 bool SmartSidestepResolverThunk::IsInternalCall(const void* base,
   192                                                 void* return_address) {
   193   DCHECK_NT(base);
   194   DCHECK_NT(return_address);
   196   base::win::PEImage pe(base);
   197   if (pe.GetImageSectionFromAddr(return_address))
   198     return true;
   199   return false;
   200 }
   202 }  // namespace sandbox

mercurial