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