1 // Copyright (c) 2009 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 "chrome_frame/function_stub.h" 6 7 #include <new> 8 9 #include "base/synchronization/lock.h" 10 #include "base/logging.h" 11 12 #ifndef _M_IX86 13 #error Only x86 supported right now. 14 #endif 15 16 namespace { 17 typedef enum AsmConstants { 18 POP_EAX = 0x58, 19 PUSH_IND = 0x35ff, 20 PUSH_EAX = 0x50, 21 JUMP_IND = 0x25ff, 22 }; 23 24 // A quick and dirty wrapper class that allows us to defer allocating 25 // the executable heap until first use, and to release it teardown. 26 class ExecutableHeap { 27 public: 28 ExecutableHeap() : heap_(NULL) { 29 } 30 31 ~ExecutableHeap() { 32 if (heap_ != NULL) { 33 BOOL ret = ::HeapDestroy(heap_); 34 heap_ = NULL; 35 } 36 } 37 38 void* Allocate(size_t size) { 39 if (!heap_) 40 CreateHeap(); 41 42 DCHECK(heap_); 43 44 return ::HeapAlloc(heap_, 0, size); 45 } 46 47 void Free(void* ptr) { 48 DCHECK(heap_ != NULL); 49 ::HeapFree(heap_, 0, ptr); 50 } 51 52 void CreateHeap() { 53 base::AutoLock lock(init_lock_); 54 55 if (heap_ == NULL) 56 heap_ = ::HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0); 57 } 58 59 private: 60 base::Lock init_lock_; 61 HANDLE heap_; 62 }; 63 64 // Our executable heap instance, all stubs are allocated from here. 65 ExecutableHeap heap_; 66 67 } // namespace 68 69 extern "C" IMAGE_DOS_HEADER __ImageBase; 70 71 bool FunctionStub::is_valid() const { 72 return signature_ == reinterpret_cast<HMODULE>(&__ImageBase) && 73 !is_bypassed(); 74 } 75 76 FunctionStub::FunctionStub(uintptr_t extra_argument, void* dest) 77 : signature_(reinterpret_cast<HMODULE>(&__ImageBase)), 78 argument_(extra_argument), 79 destination_function_(reinterpret_cast<uintptr_t>(dest)) { 80 bypass_address_ = reinterpret_cast<uintptr_t>(&stub_.pop_return_addr_); 81 Init(&stub_); 82 } 83 84 FunctionStub::~FunctionStub() { 85 } 86 87 void FunctionStub::Init(FunctionStubAsm* stub) { 88 DCHECK(stub != NULL); 89 90 stub->jump_to_bypass_ = JUMP_IND; 91 stub->bypass_target_addr_ = reinterpret_cast<uintptr_t>(&bypass_address_); 92 stub->pop_return_addr_ = POP_EAX; 93 stub->push_ = PUSH_IND; 94 stub->arg_addr_ = reinterpret_cast<uintptr_t>(&argument_); 95 stub->push_return_addr_ = PUSH_EAX; 96 stub->jump_to_target = JUMP_IND; 97 stub->target_addr_ = reinterpret_cast<uintptr_t>(&destination_function_); 98 99 // Flush the instruction cache for the newly written code. 100 BOOL ret = ::FlushInstructionCache(::GetCurrentProcess(), 101 stub, 102 sizeof(*stub)); 103 } 104 105 void FunctionStub::BypassStub(void* new_target) { 106 set_bypass_address(reinterpret_cast<uintptr_t>(new_target)); 107 } 108 109 FunctionStub* FunctionStub::Create(uintptr_t extra_argument, void* dest) { 110 DCHECK(dest); 111 FunctionStub* stub = 112 reinterpret_cast<FunctionStub*>(heap_.Allocate(sizeof(FunctionStub))); 113 114 if (stub != NULL) 115 new (stub) FunctionStub(extra_argument, dest); 116 117 return stub; 118 } 119 120 FunctionStub* FunctionStub::FromCode(void* address) { 121 // Address points to arbitrary code here, which may e.g. 122 // lie at the end of an executable segment, which in turn 123 // may terminate earlier than the last address we probe. 124 // We therefore execute under an SEH, so as not to crash 125 // on failed probes. 126 __try { 127 // Retrieve the candidata function stub. 128 FunctionStub* candidate = CONTAINING_RECORD(address, FunctionStub, stub_); 129 if (candidate->stub_.jump_to_bypass_ == JUMP_IND && 130 candidate->signature_ == reinterpret_cast<HMODULE>(&__ImageBase)) { 131 return candidate; 132 } 133 } __except(EXCEPTION_EXECUTE_HANDLER) { 134 } 135 136 return NULL; 137 } 138 139 bool FunctionStub::Destroy(FunctionStub* stub) { 140 heap_.Free(stub); 141 142 return true; 143 } 144