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