// 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. #ifndef CHROME_FRAME_FUNCTION_STUB_H_ #define CHROME_FRAME_FUNCTION_STUB_H_ #include #include "base/logging.h" // IMPORTANT: The struct below must be byte aligned. #pragma pack(push) #pragma pack(1) #ifndef _M_IX86 #error Only x86 supported right now. #endif extern "C" IMAGE_DOS_HEADER __ImageBase; // This struct is assembly code + signature. The purpose of the struct is to be // able to hook an existing function with our own and store information such // as the original function pointer with the code stub. Typically this is used // for patching entries of a vtable or e.g. a globally registered wndproc // for a class as opposed to a window. // When unhooking, you can just call the BypassStub() function and leave the // stub in memory. This unhooks your function while leaving the (potential) // chain of patches intact. // // @note: This class is meant for __stdcall calling convention and // it uses eax as a temporary variable. The struct can // be improved in the future to save eax before the // operation and then restore it. // // For instance if the function prototype is: // // @code // LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); // @endcode // // and we would like to add one static argument to make it, say: // // @code // LRESULT MyNewWndProc(WNDPROC original, HWND hwnd, UINT msg, // WPARAM wparam, LPARAM lparam); // @endcode // // That can be achieved by wrapping the function up with a FunctionStub: // // @code // FunctionStub* stub = FunctionStub::Create(original_wndproc, MyNewWndProc); // SetClassLongPtr(wnd, GCLP_WNDPROC, stub->code()); // @endcode struct FunctionStub { private: typedef enum AsmConstants { POP_EAX = 0x58, PUSH = 0x68, PUSH_EAX = 0x50, JUMP_RELATIVE = 0xE9 }; FunctionStub(uintptr_t extra_argument, void* dest) : signature_(reinterpret_cast(&__ImageBase)) { Opcodes::Hook& hook = code_.hook_; hook.pop_return_addr_ = POP_EAX; hook.push_ = PUSH; hook.arg_ = extra_argument; hook.push_return_addr_ = PUSH_EAX; hook.jump_to_ = JUMP_RELATIVE; // Calculate the target jump to the destination function. hook.target_ = CalculateRelativeJump(dest, &hook.jump_to_); // Allow the process to execute this struct as code. DWORD old_protect = 0; // Allow reads too since we want read-only member variable access at // all times. ::VirtualProtect(this, sizeof(FunctionStub), PAGE_EXECUTE_READ, &old_protect); } ~FunctionStub() { // No more execution rights. DWORD old_protect = 0; ::VirtualProtect(this, sizeof(FunctionStub), PAGE_READWRITE, &old_protect); } // Calculates the target value for a relative jump. // The function assumes that the size of the opcode is 1 byte. inline uintptr_t CalculateRelativeJump(const void* absolute_to, const void* absolute_from) const { return (reinterpret_cast(absolute_to) - reinterpret_cast(absolute_from)) - (sizeof(uint8) + sizeof(uintptr_t)); } // Does the opposite of what CalculateRelativeJump does, which is to // calculate back the absolute address that previously was relative to // some other address. inline uintptr_t CalculateAbsoluteAddress(const void* relative_to, uintptr_t relative_address) const { return relative_address + sizeof(uint8) + sizeof(uintptr_t) + reinterpret_cast(relative_to); } // Used to identify function stubs that belong to this module. HMODULE signature_; // IMPORTANT: Do not change the order of member variables union Opcodes { // Use this struct when the stub forwards the call to our hook function // providing an extra argument. struct Hook { uint8 pop_return_addr_; // pop eax uint8 push_; // push arg ; push... uintptr_t arg_; // ; extra argument uint8 push_return_addr_; // push eax ; push the return address uint8 jump_to_; // jmp ; jump... uintptr_t target_; // ; to the hook function } hook_; // When the stub is bypassed, we jump directly to a given target, // usually the originally hooked function. struct Bypassed { uint8 jump_to_; // jmp to uintptr_t target_; // relative target. } bypassed_; }; Opcodes code_; public: // Neutralizes this stub and converts it to a direct jump to a new target. void BypassStub(void* new_target) { DWORD old_protect = 0; // Temporarily allow us to write to member variables ::VirtualProtect(this, sizeof(FunctionStub), PAGE_EXECUTE_READWRITE, &old_protect); // Now, just change the first 5 bytes to jump directly to the new target. Opcodes::Bypassed& bypassed = code_.bypassed_; bypassed.jump_to_ = JUMP_RELATIVE; bypassed.target_ = CalculateRelativeJump(new_target, &bypassed.jump_to_); // Restore the previous protection flags. ::VirtualProtect(this, sizeof(FunctionStub), old_protect, &old_protect); // Flush the instruction cache just in case. ::FlushInstructionCache(::GetCurrentProcess(), this, sizeof(FunctionStub)); } // @returns the argument supplied in the call to @ref Create inline uintptr_t argument() const { DCHECK(code_.hook_.pop_return_addr_ == POP_EAX) << "stub has been disabled"; return code_.hook_.arg_; } inline bool is_bypassed() const { return code_.bypassed_.jump_to_ == JUMP_RELATIVE; } inline uintptr_t absolute_target() const { DCHECK(code_.hook_.pop_return_addr_ == POP_EAX) << "stub has been disabled"; return CalculateAbsoluteAddress( reinterpret_cast(&code_.hook_.jump_to_), code_.hook_.target_); } // Returns true if the stub is valid and enabled. // Don't call this method after bypassing the stub. inline bool is_valid() const { return signature_ == reinterpret_cast(&__ImageBase) && code_.hook_.pop_return_addr_ == POP_EAX; } inline PROC code() const { return reinterpret_cast(const_cast(&code_)); } // Use to create a new function stub as shown above. // // @param extra_argument The static argument to pass to the function. // @param dest Target function to which the stub applies. // @returns NULL if an error occurs, otherwise a pointer to the // function stub. // // TODO(tommi): Change this so that VirtualAlloc isn't called for // every stub. Instead we should re-use each allocation for // multiple stubs. In practice I'm guessing that there would // only be one allocation per process, since each allocation actually // allocates at least one page of memory (4K). Size of FunctionStub // is 12 bytes, so one page could house 341 function stubs. // When stubs are created frequently, VirtualAlloc could fail // and last error is ERROR_NOT_ENOUGH_MEMORY (8). static FunctionStub* Create(uintptr_t extra_argument, void* dest) { DCHECK(dest); // Use VirtualAlloc to get memory for the stub. This gives us a // whole page that we don't share with anyone else. // Initially the memory must be READWRITE to allow for construction // PAGE_EXECUTE is set in constructor. FunctionStub* ret = reinterpret_cast(VirtualAlloc(NULL, sizeof(FunctionStub), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); if (!ret) { NOTREACHED(); } else { // Construct ret->FunctionStub::FunctionStub(extra_argument, dest); } return ret; } static FunctionStub* FromCode(void* address) { Opcodes* code = reinterpret_cast(address); if (code->hook_.pop_return_addr_ == POP_EAX) { FunctionStub* stub = reinterpret_cast( reinterpret_cast(address) - sizeof(HMODULE)); if (stub->is_valid()) return stub; } return NULL; } // Deallocates a FunctionStub. The stub must not be in use on any thread! static bool Destroy(FunctionStub* stub) { DCHECK(stub != NULL); FunctionStub* to_free = reinterpret_cast(stub); to_free->FunctionStub::~FunctionStub(); BOOL success = VirtualFree(to_free, sizeof(FunctionStub), MEM_DECOMMIT); DCHECK(success) << "VirtualFree"; return success != FALSE; } }; #pragma pack(pop) #endif // CHROME_FRAME_FUNCTION_STUB_H_