michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "sandbox/win/src/sidestep_resolver.h" michael@0: michael@0: #include "base/win/pe_image.h" michael@0: #include "sandbox/win/src/sandbox_nt_util.h" michael@0: #include "sandbox/win/src/sidestep/preamble_patcher.h" michael@0: michael@0: namespace { michael@0: michael@0: const size_t kSizeOfSidestepStub = sidestep::kMaxPreambleStubSize; michael@0: michael@0: struct SidestepThunk { michael@0: char sidestep[kSizeOfSidestepStub]; // Storage for the sidestep stub. michael@0: int internal_thunk; // Dummy member to the beginning of the internal thunk. michael@0: }; michael@0: michael@0: struct SmartThunk { michael@0: const void* module_base; // Target module's base. michael@0: const void* interceptor; // Real interceptor. michael@0: SidestepThunk sidestep; // Standard sidestep thunk. michael@0: }; michael@0: michael@0: } // namespace michael@0: michael@0: namespace sandbox { michael@0: michael@0: NTSTATUS SidestepResolverThunk::Setup(const void* target_module, michael@0: const void* interceptor_module, michael@0: const char* target_name, michael@0: const char* interceptor_name, michael@0: const void* interceptor_entry_point, michael@0: void* thunk_storage, michael@0: size_t storage_bytes, michael@0: size_t* storage_used) { michael@0: NTSTATUS ret = Init(target_module, interceptor_module, target_name, michael@0: interceptor_name, interceptor_entry_point, michael@0: thunk_storage, storage_bytes); michael@0: if (!NT_SUCCESS(ret)) michael@0: return ret; michael@0: michael@0: SidestepThunk* thunk = reinterpret_cast(thunk_storage); michael@0: michael@0: size_t internal_bytes = storage_bytes - kSizeOfSidestepStub; michael@0: if (!SetInternalThunk(&thunk->internal_thunk, internal_bytes, thunk_storage, michael@0: interceptor_)) michael@0: return STATUS_BUFFER_TOO_SMALL; michael@0: michael@0: AutoProtectMemory memory; michael@0: ret = memory.ChangeProtection(target_, kSizeOfSidestepStub, PAGE_READWRITE); michael@0: if (!NT_SUCCESS(ret)) michael@0: return ret; michael@0: michael@0: sidestep::SideStepError rv = sidestep::PreamblePatcher::Patch( michael@0: target_, reinterpret_cast(&thunk->internal_thunk), thunk_storage, michael@0: kSizeOfSidestepStub); michael@0: michael@0: if (sidestep::SIDESTEP_INSUFFICIENT_BUFFER == rv) michael@0: return STATUS_BUFFER_TOO_SMALL; michael@0: michael@0: if (sidestep::SIDESTEP_SUCCESS != rv) michael@0: return STATUS_UNSUCCESSFUL; michael@0: michael@0: if (storage_used) michael@0: *storage_used = GetThunkSize(); michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: size_t SidestepResolverThunk::GetThunkSize() const { michael@0: return GetInternalThunkSize() + kSizeOfSidestepStub; michael@0: } michael@0: michael@0: // This is basically a wrapper around the normal sidestep patch that extends michael@0: // the thunk to use a chained interceptor. It uses the fact that michael@0: // SetInternalThunk generates the code to pass as the first parameter whatever michael@0: // it receives as original_function; we let SidestepResolverThunk set this value michael@0: // to its saved code, and then we change it to our thunk data. michael@0: NTSTATUS SmartSidestepResolverThunk::Setup(const void* target_module, michael@0: const void* interceptor_module, michael@0: const char* target_name, michael@0: const char* interceptor_name, michael@0: const void* interceptor_entry_point, michael@0: void* thunk_storage, michael@0: size_t storage_bytes, michael@0: size_t* storage_used) { michael@0: if (storage_bytes < GetThunkSize()) michael@0: return STATUS_BUFFER_TOO_SMALL; michael@0: michael@0: SmartThunk* thunk = reinterpret_cast(thunk_storage); michael@0: thunk->module_base = target_module; michael@0: michael@0: NTSTATUS ret; michael@0: if (interceptor_entry_point) { michael@0: thunk->interceptor = interceptor_entry_point; michael@0: } else { michael@0: ret = ResolveInterceptor(interceptor_module, interceptor_name, michael@0: &thunk->interceptor); michael@0: if (!NT_SUCCESS(ret)) michael@0: return ret; michael@0: } michael@0: michael@0: // Perform a standard sidestep patch on the last part of the thunk, but point michael@0: // to our internal smart interceptor. michael@0: size_t standard_bytes = storage_bytes - offsetof(SmartThunk, sidestep); michael@0: ret = SidestepResolverThunk::Setup(target_module, interceptor_module, michael@0: target_name, NULL, &SmartStub, michael@0: &thunk->sidestep, standard_bytes, NULL); michael@0: if (!NT_SUCCESS(ret)) michael@0: return ret; michael@0: michael@0: // Fix the internal thunk to pass the whole buffer to the interceptor. michael@0: SetInternalThunk(&thunk->sidestep.internal_thunk, GetInternalThunkSize(), michael@0: thunk_storage, &SmartStub); michael@0: michael@0: if (storage_used) michael@0: *storage_used = GetThunkSize(); michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: size_t SmartSidestepResolverThunk::GetThunkSize() const { michael@0: return GetInternalThunkSize() + kSizeOfSidestepStub + michael@0: offsetof(SmartThunk, sidestep); michael@0: } michael@0: michael@0: // This code must basically either call the intended interceptor or skip the michael@0: // call and invoke instead the original function. In any case, we are saving michael@0: // the registers that may be trashed by our c++ code. michael@0: // michael@0: // This function is called with a first parameter inserted by us, that points michael@0: // to our SmartThunk. When we call the interceptor we have to replace this michael@0: // parameter with the one expected by that function (stored inside our michael@0: // structure); on the other hand, when we skip the interceptor we have to remove michael@0: // that extra argument before calling the original function. michael@0: // michael@0: // When we skip the interceptor, the transformation of the stack looks like: michael@0: // On Entry: On Use: On Exit: michael@0: // [param 2] = first real argument [param 2] (esp+1c) [param 2] michael@0: // [param 1] = our SmartThunk [param 1] (esp+18) [ret address] michael@0: // [ret address] = real caller [ret address] (esp+14) [xxx] michael@0: // [xxx] [addr to jump to] (esp+10) [xxx] michael@0: // [xxx] [saved eax] [xxx] michael@0: // [xxx] [saved ebx] [xxx] michael@0: // [xxx] [saved ecx] [xxx] michael@0: // [xxx] [saved edx] [xxx] michael@0: __declspec(naked) michael@0: void SmartSidestepResolverThunk::SmartStub() { michael@0: __asm { michael@0: push eax // Space for the jump. michael@0: push eax // Save registers. michael@0: push ebx michael@0: push ecx michael@0: push edx michael@0: mov ebx, [esp + 0x18] // First parameter = SmartThunk. michael@0: mov edx, [esp + 0x14] // Get the return address. michael@0: mov eax, [ebx]SmartThunk.module_base michael@0: push edx michael@0: push eax michael@0: call SmartSidestepResolverThunk::IsInternalCall michael@0: add esp, 8 michael@0: michael@0: test eax, eax michael@0: lea edx, [ebx]SmartThunk.sidestep // The original function. michael@0: jz call_interceptor michael@0: michael@0: // Skip this call michael@0: mov ecx, [esp + 0x14] // Return address. michael@0: mov [esp + 0x18], ecx // Remove first parameter. michael@0: mov [esp + 0x10], edx michael@0: pop edx // Restore registers. michael@0: pop ecx michael@0: pop ebx michael@0: pop eax michael@0: ret 4 // Jump to original function. michael@0: michael@0: call_interceptor: michael@0: mov ecx, [ebx]SmartThunk.interceptor michael@0: mov [esp + 0x18], edx // Replace first parameter. michael@0: mov [esp + 0x10], ecx michael@0: pop edx // Restore registers. michael@0: pop ecx michael@0: pop ebx michael@0: pop eax michael@0: ret // Jump to original function. michael@0: } michael@0: } michael@0: michael@0: bool SmartSidestepResolverThunk::IsInternalCall(const void* base, michael@0: void* return_address) { michael@0: DCHECK_NT(base); michael@0: DCHECK_NT(return_address); michael@0: michael@0: base::win::PEImage pe(base); michael@0: if (pe.GetImageSectionFromAddr(return_address)) michael@0: return true; michael@0: return false; michael@0: } michael@0: michael@0: } // namespace sandbox