summaryrefslogtreecommitdiffstats
path: root/chrome_frame/function_stub.h
blob: 55f0c5ea5535160b0c40d30a0286b96f3c348818 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
// 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 <windows.h>
#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<HMODULE>(&__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<uintptr_t>(absolute_to) -
            reinterpret_cast<uintptr_t>(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<uintptr_t>(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<const void*>(&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<HMODULE>(&__ImageBase) &&
        code_.hook_.pop_return_addr_ == POP_EAX;
  }

  inline PROC code() const {
    return reinterpret_cast<PROC>(const_cast<Opcodes*>(&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<FunctionStub*>(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<Opcodes*>(address);
    if (code->hook_.pop_return_addr_ == POP_EAX) {
      FunctionStub* stub = reinterpret_cast<FunctionStub*>(
          reinterpret_cast<uint8*>(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<FunctionStub*>(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_