Home | History | Annotate | Download | only in chrome_frame
      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