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 11namespace { 12 13const size_t kSizeOfSidestepStub = sidestep::kMaxPreambleStubSize; 14 15struct 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 20struct 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 28namespace sandbox { 29 30NTSTATUS 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 72size_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. 81NTSTATUS 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 124size_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) 150void 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 191bool 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