summaryrefslogtreecommitdiffstats
path: root/toolkit/xre/dllservices/mozglue/interceptor/PatcherBase.h
blob: e39a38fafd181bfc8750ef69e343876eeba397cb (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
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef mozilla_interceptor_PatcherBase_h
#define mozilla_interceptor_PatcherBase_h

#include "mozilla/interceptor/TargetFunction.h"

namespace mozilla {
namespace interceptor {

template <typename MMPolicy>
struct GetProcAddressSelector;

template <>
struct GetProcAddressSelector<MMPolicyOutOfProcess> {
  FARPROC operator()(HMODULE aModule, const char* aName,
                     const MMPolicyOutOfProcess& aMMPolicy) const {
    auto exportSection =
        mozilla::nt::PEExportSection<MMPolicyOutOfProcess>::Get(aModule,
                                                                aMMPolicy);
    return exportSection.GetProcAddress(aName);
  }
};

template <>
struct GetProcAddressSelector<MMPolicyInProcess> {
  FARPROC operator()(HMODULE aModule, const char* aName,
                     const MMPolicyInProcess&) const {
    // PEExportSection works for MMPolicyInProcess, too, but the native
    // GetProcAddress is still better because PEExportSection does not
    // solve a forwarded entry.
    return ::GetProcAddress(aModule, aName);
  }
};

template <typename VMPolicy>
class WindowsDllPatcherBase {
 protected:
  typedef typename VMPolicy::MMPolicyT MMPolicyT;

  template <typename... Args>
  explicit WindowsDllPatcherBase(Args&&... aArgs)
      : mVMPolicy(std::forward<Args>(aArgs)...) {}

  ReadOnlyTargetFunction<MMPolicyT> ResolveRedirectedAddress(
      FARPROC aOriginalFunction) {
    uintptr_t currAddr = reinterpret_cast<uintptr_t>(aOriginalFunction);

#if defined(_M_IX86) || defined(_M_X64)
    uintptr_t prevAddr = 0;
    while (prevAddr != currAddr) {
      ReadOnlyTargetFunction<MMPolicyT> currFunc(mVMPolicy, currAddr);
      prevAddr = currAddr;

      // If function entry is jmp rel8 stub to the internal implementation, we
      // resolve redirected address from the jump target.
      uintptr_t nextAddr = 0;
      if (currFunc.IsRelativeShortJump(&nextAddr)) {
        int8_t offset = nextAddr - currFunc.GetAddress() - 2;

#  if defined(_M_X64)
        // We redirect to the target of a short jump backwards if the target
        // is another jump (only 32-bit displacement is currently supported).
        // This case is used by GetFileAttributesW in Win7 x64.
        if ((offset < 0) && (currFunc.IsValidAtOffset(2 + offset))) {
          ReadOnlyTargetFunction<MMPolicyT> redirectFn(mVMPolicy, nextAddr);
          if (redirectFn.IsIndirectNearJump(&nextAddr)) {
            return redirectFn;
          }
        }
#  endif

        // We check the downstream has enough nop-space only when the offset is
        // positive.  Otherwise we stop chasing redirects and let the caller
        // fail to hook.
        if (offset > 0) {
          bool isNopSpace = true;
          for (int8_t i = 0; i < offset; i++) {
            if (currFunc[2 + i] != 0x90) {
              isNopSpace = false;
              break;
            }
          }

          if (isNopSpace) {
            currAddr = nextAddr;
          }
        }
#  if defined(_M_X64)
      } else if (currFunc.IsIndirectNearJump(&nextAddr) ||
                 currFunc.IsRelativeNearJump(&nextAddr)) {
#  else
      } else if (currFunc.IsIndirectNearJump(&nextAddr)) {
#  endif
        // If function entry is jmp [disp32] such as used by kernel32, we
        // resolve redirected address from import table. For x64, we resolve
        // a relative near jump for TestDllInterceptor with --disable-optimize.
        currAddr = nextAddr;
      }
    }
#endif  // defined(_M_IX86) || defined(_M_X64)

    if (currAddr != reinterpret_cast<uintptr_t>(aOriginalFunction) &&
        !mVMPolicy.IsPageAccessible(currAddr)) {
      currAddr = reinterpret_cast<uintptr_t>(aOriginalFunction);
    }
    return ReadOnlyTargetFunction<MMPolicyT>(mVMPolicy, currAddr);
  }

 public:
  FARPROC GetProcAddress(HMODULE aModule, const char* aName) const {
    GetProcAddressSelector<MMPolicyT> selector;
    return selector(aModule, aName, mVMPolicy);
  }

  bool IsPageAccessible(uintptr_t aAddress) const {
    return mVMPolicy.IsPageAccessible(aAddress);
  }

#if defined(NIGHTLY_BUILD)
  const Maybe<DetourError>& GetLastDetourError() const {
    return mVMPolicy.GetLastDetourError();
  }
#endif  // defined(NIGHTLY_BUILD)
  template <typename... Args>
  void SetLastDetourError(Args&&... aArgs) {
    mVMPolicy.SetLastDetourError(std::forward<Args>(aArgs)...);
  }

 protected:
  VMPolicy mVMPolicy;
};

}  // namespace interceptor
}  // namespace mozilla

#endif  // mozilla_interceptor_PatcherBase_h