// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome_frame/function_stub.h" #include #include "base/synchronization/lock.h" #include "base/logging.h" #ifndef _M_IX86 #error Only x86 supported right now. #endif namespace { typedef enum AsmConstants { POP_EAX = 0x58, PUSH_IND = 0x35ff, PUSH_EAX = 0x50, JUMP_IND = 0x25ff, }; // A quick and dirty wrapper class that allows us to defer allocating // the executable heap until first use, and to release it teardown. class ExecutableHeap { public: ExecutableHeap() : heap_(NULL) { } ~ExecutableHeap() { if (heap_ != NULL) { BOOL ret = ::HeapDestroy(heap_); heap_ = NULL; } } void* Allocate(size_t size) { if (!heap_) CreateHeap(); DCHECK(heap_); return ::HeapAlloc(heap_, 0, size); } void Free(void* ptr) { DCHECK(heap_ != NULL); ::HeapFree(heap_, 0, ptr); } void CreateHeap() { base::AutoLock lock(init_lock_); if (heap_ == NULL) heap_ = ::HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0); } private: base::Lock init_lock_; HANDLE heap_; }; // Our executable heap instance, all stubs are allocated from here. ExecutableHeap heap_; } // namespace extern "C" IMAGE_DOS_HEADER __ImageBase; bool FunctionStub::is_valid() const { return signature_ == reinterpret_cast(&__ImageBase) && !is_bypassed(); } FunctionStub::FunctionStub(uintptr_t extra_argument, void* dest) : signature_(reinterpret_cast(&__ImageBase)), argument_(extra_argument), destination_function_(reinterpret_cast(dest)) { bypass_address_ = reinterpret_cast(&stub_.pop_return_addr_); Init(&stub_); } FunctionStub::~FunctionStub() { } void FunctionStub::Init(FunctionStubAsm* stub) { DCHECK(stub != NULL); stub->jump_to_bypass_ = JUMP_IND; stub->bypass_target_addr_ = reinterpret_cast(&bypass_address_); stub->pop_return_addr_ = POP_EAX; stub->push_ = PUSH_IND; stub->arg_addr_ = reinterpret_cast(&argument_); stub->push_return_addr_ = PUSH_EAX; stub->jump_to_target = JUMP_IND; stub->target_addr_ = reinterpret_cast(&destination_function_); // Flush the instruction cache for the newly written code. BOOL ret = ::FlushInstructionCache(::GetCurrentProcess(), stub, sizeof(*stub)); } void FunctionStub::BypassStub(void* new_target) { set_bypass_address(reinterpret_cast(new_target)); } FunctionStub* FunctionStub::Create(uintptr_t extra_argument, void* dest) { DCHECK(dest); FunctionStub* stub = reinterpret_cast(heap_.Allocate(sizeof(FunctionStub))); if (stub != NULL) new (stub) FunctionStub(extra_argument, dest); return stub; } FunctionStub* FunctionStub::FromCode(void* address) { // Address points to arbitrary code here, which may e.g. // lie at the end of an executable segment, which in turn // may terminate earlier than the last address we probe. // We therefore execute under an SEH, so as not to crash // on failed probes. __try { // Retrieve the candidata function stub. FunctionStub* candidate = CONTAINING_RECORD(address, FunctionStub, stub_); if (candidate->stub_.jump_to_bypass_ == JUMP_IND && candidate->signature_ == reinterpret_cast(&__ImageBase)) { return candidate; } } __except(EXCEPTION_EXECUTE_HANDLER) { } return NULL; } bool FunctionStub::Destroy(FunctionStub* stub) { heap_.Free(stub); return true; }