security/sandbox/win/src/sidestep_resolver.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/sandbox/win/src/sidestep_resolver.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,202 @@
     1.4 +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +#include "sandbox/win/src/sidestep_resolver.h"
     1.9 +
    1.10 +#include "base/win/pe_image.h"
    1.11 +#include "sandbox/win/src/sandbox_nt_util.h"
    1.12 +#include "sandbox/win/src/sidestep/preamble_patcher.h"
    1.13 +
    1.14 +namespace {
    1.15 +
    1.16 +const size_t kSizeOfSidestepStub = sidestep::kMaxPreambleStubSize;
    1.17 +
    1.18 +struct SidestepThunk {
    1.19 +  char sidestep[kSizeOfSidestepStub];  // Storage for the sidestep stub.
    1.20 +  int internal_thunk;  // Dummy member to the beginning of the internal thunk.
    1.21 +};
    1.22 +
    1.23 +struct SmartThunk {
    1.24 +  const void* module_base;  // Target module's base.
    1.25 +  const void* interceptor;  // Real interceptor.
    1.26 +  SidestepThunk sidestep;  // Standard sidestep thunk.
    1.27 +};
    1.28 +
    1.29 +}  // namespace
    1.30 +
    1.31 +namespace sandbox {
    1.32 +
    1.33 +NTSTATUS SidestepResolverThunk::Setup(const void* target_module,
    1.34 +                                      const void* interceptor_module,
    1.35 +                                      const char* target_name,
    1.36 +                                      const char* interceptor_name,
    1.37 +                                      const void* interceptor_entry_point,
    1.38 +                                      void* thunk_storage,
    1.39 +                                      size_t storage_bytes,
    1.40 +                                      size_t* storage_used) {
    1.41 +  NTSTATUS ret = Init(target_module, interceptor_module, target_name,
    1.42 +                      interceptor_name, interceptor_entry_point,
    1.43 +                      thunk_storage, storage_bytes);
    1.44 +  if (!NT_SUCCESS(ret))
    1.45 +    return ret;
    1.46 +
    1.47 +  SidestepThunk* thunk = reinterpret_cast<SidestepThunk*>(thunk_storage);
    1.48 +
    1.49 +  size_t internal_bytes = storage_bytes - kSizeOfSidestepStub;
    1.50 +  if (!SetInternalThunk(&thunk->internal_thunk, internal_bytes, thunk_storage,
    1.51 +                        interceptor_))
    1.52 +    return STATUS_BUFFER_TOO_SMALL;
    1.53 +
    1.54 +  AutoProtectMemory memory;
    1.55 +  ret = memory.ChangeProtection(target_, kSizeOfSidestepStub, PAGE_READWRITE);
    1.56 +  if (!NT_SUCCESS(ret))
    1.57 +    return ret;
    1.58 +
    1.59 +  sidestep::SideStepError rv = sidestep::PreamblePatcher::Patch(
    1.60 +      target_, reinterpret_cast<void*>(&thunk->internal_thunk), thunk_storage,
    1.61 +      kSizeOfSidestepStub);
    1.62 +
    1.63 +  if (sidestep::SIDESTEP_INSUFFICIENT_BUFFER == rv)
    1.64 +    return STATUS_BUFFER_TOO_SMALL;
    1.65 +
    1.66 +  if (sidestep::SIDESTEP_SUCCESS != rv)
    1.67 +    return STATUS_UNSUCCESSFUL;
    1.68 +
    1.69 +  if (storage_used)
    1.70 +    *storage_used = GetThunkSize();
    1.71 +
    1.72 +  return ret;
    1.73 +}
    1.74 +
    1.75 +size_t SidestepResolverThunk::GetThunkSize() const {
    1.76 +  return GetInternalThunkSize() + kSizeOfSidestepStub;
    1.77 +}
    1.78 +
    1.79 +// This is basically a wrapper around the normal sidestep patch that extends
    1.80 +// the thunk to use a chained interceptor. It uses the fact that
    1.81 +// SetInternalThunk generates the code to pass as the first parameter whatever
    1.82 +// it receives as original_function; we let SidestepResolverThunk set this value
    1.83 +// to its saved code, and then we change it to our thunk data.
    1.84 +NTSTATUS SmartSidestepResolverThunk::Setup(const void* target_module,
    1.85 +                                           const void* interceptor_module,
    1.86 +                                           const char* target_name,
    1.87 +                                           const char* interceptor_name,
    1.88 +                                           const void* interceptor_entry_point,
    1.89 +                                           void* thunk_storage,
    1.90 +                                           size_t storage_bytes,
    1.91 +                                           size_t* storage_used) {
    1.92 +  if (storage_bytes < GetThunkSize())
    1.93 +    return STATUS_BUFFER_TOO_SMALL;
    1.94 +
    1.95 +  SmartThunk* thunk = reinterpret_cast<SmartThunk*>(thunk_storage);
    1.96 +  thunk->module_base = target_module;
    1.97 +
    1.98 +  NTSTATUS ret;
    1.99 +  if (interceptor_entry_point) {
   1.100 +    thunk->interceptor = interceptor_entry_point;
   1.101 +  } else {
   1.102 +    ret = ResolveInterceptor(interceptor_module, interceptor_name,
   1.103 +                             &thunk->interceptor);
   1.104 +    if (!NT_SUCCESS(ret))
   1.105 +      return ret;
   1.106 +  }
   1.107 +
   1.108 +  // Perform a standard sidestep patch on the last part of the thunk, but point
   1.109 +  // to our internal smart interceptor.
   1.110 +  size_t standard_bytes = storage_bytes - offsetof(SmartThunk, sidestep);
   1.111 +  ret = SidestepResolverThunk::Setup(target_module, interceptor_module,
   1.112 +                                     target_name, NULL, &SmartStub,
   1.113 +                                     &thunk->sidestep, standard_bytes, NULL);
   1.114 +  if (!NT_SUCCESS(ret))
   1.115 +    return ret;
   1.116 +
   1.117 +  // Fix the internal thunk to pass the whole buffer to the interceptor.
   1.118 +  SetInternalThunk(&thunk->sidestep.internal_thunk, GetInternalThunkSize(),
   1.119 +                   thunk_storage, &SmartStub);
   1.120 +
   1.121 +  if (storage_used)
   1.122 +    *storage_used = GetThunkSize();
   1.123 +
   1.124 +  return ret;
   1.125 +}
   1.126 +
   1.127 +size_t SmartSidestepResolverThunk::GetThunkSize() const {
   1.128 +  return GetInternalThunkSize() + kSizeOfSidestepStub +
   1.129 +         offsetof(SmartThunk, sidestep);
   1.130 +}
   1.131 +
   1.132 +// This code must basically either call the intended interceptor or skip the
   1.133 +// call and invoke instead the original function. In any case, we are saving
   1.134 +// the registers that may be trashed by our c++ code.
   1.135 +//
   1.136 +// This function is called with a first parameter inserted by us, that points
   1.137 +// to our SmartThunk. When we call the interceptor we have to replace this
   1.138 +// parameter with the one expected by that function (stored inside our
   1.139 +// structure); on the other hand, when we skip the interceptor we have to remove
   1.140 +// that extra argument before calling the original function.
   1.141 +//
   1.142 +// When we skip the interceptor, the transformation of the stack looks like:
   1.143 +//  On Entry:                         On Use:                     On Exit:
   1.144 +//  [param 2] = first real argument   [param 2] (esp+1c)          [param 2]
   1.145 +//  [param 1] = our SmartThunk        [param 1] (esp+18)          [ret address]
   1.146 +//  [ret address] = real caller       [ret address] (esp+14)      [xxx]
   1.147 +//  [xxx]                             [addr to jump to] (esp+10)  [xxx]
   1.148 +//  [xxx]                             [saved eax]                 [xxx]
   1.149 +//  [xxx]                             [saved ebx]                 [xxx]
   1.150 +//  [xxx]                             [saved ecx]                 [xxx]
   1.151 +//  [xxx]                             [saved edx]                 [xxx]
   1.152 +__declspec(naked)
   1.153 +void SmartSidestepResolverThunk::SmartStub() {
   1.154 +  __asm {
   1.155 +    push eax                  // Space for the jump.
   1.156 +    push eax                  // Save registers.
   1.157 +    push ebx
   1.158 +    push ecx
   1.159 +    push edx
   1.160 +    mov ebx, [esp + 0x18]     // First parameter = SmartThunk.
   1.161 +    mov edx, [esp + 0x14]     // Get the return address.
   1.162 +    mov eax, [ebx]SmartThunk.module_base
   1.163 +    push edx
   1.164 +    push eax
   1.165 +    call SmartSidestepResolverThunk::IsInternalCall
   1.166 +    add esp, 8
   1.167 +
   1.168 +    test eax, eax
   1.169 +    lea edx, [ebx]SmartThunk.sidestep   // The original function.
   1.170 +    jz call_interceptor
   1.171 +
   1.172 +    // Skip this call
   1.173 +    mov ecx, [esp + 0x14]               // Return address.
   1.174 +    mov [esp + 0x18], ecx               // Remove first parameter.
   1.175 +    mov [esp + 0x10], edx
   1.176 +    pop edx                             // Restore registers.
   1.177 +    pop ecx
   1.178 +    pop ebx
   1.179 +    pop eax
   1.180 +    ret 4                               // Jump to original function.
   1.181 +
   1.182 +  call_interceptor:
   1.183 +    mov ecx, [ebx]SmartThunk.interceptor
   1.184 +    mov [esp + 0x18], edx               // Replace first parameter.
   1.185 +    mov [esp + 0x10], ecx
   1.186 +    pop edx                             // Restore registers.
   1.187 +    pop ecx
   1.188 +    pop ebx
   1.189 +    pop eax
   1.190 +    ret                                 // Jump to original function.
   1.191 +  }
   1.192 +}
   1.193 +
   1.194 +bool SmartSidestepResolverThunk::IsInternalCall(const void* base,
   1.195 +                                                void* return_address) {
   1.196 +  DCHECK_NT(base);
   1.197 +  DCHECK_NT(return_address);
   1.198 +
   1.199 +  base::win::PEImage pe(base);
   1.200 +  if (pe.GetImageSectionFromAddr(return_address))
   1.201 +    return true;
   1.202 +  return false;
   1.203 +}
   1.204 +
   1.205 +}  // namespace sandbox

mercurial