summaryrefslogtreecommitdiffstats
path: root/security/sandbox/chromium/sandbox/win/src/interception.h
blob: 6b4612fb6c01dfdda0bef7a25b803dc465896f68 (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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
// Copyright (c) 2011 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.

// Defines InterceptionManager, the class in charge of setting up interceptions
// for the sandboxed process. For more details see
// http://dev.chromium.org/developers/design-documents/sandbox .

#ifndef SANDBOX_SRC_INTERCEPTION_H_
#define SANDBOX_SRC_INTERCEPTION_H_

#include <stddef.h>

#include <list>
#include <string>

#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "sandbox/win/src/interceptors.h"
#include "sandbox/win/src/sandbox_types.h"

namespace sandbox {

class TargetProcess;

// Internal structures used for communication between the broker and the target.
struct DllPatchInfo;
struct DllInterceptionData;

// The InterceptionManager executes on the parent application, and it is in
// charge of setting up the desired interceptions, and placing the Interception
// Agent into the child application.
//
// The exposed API consists of two methods: AddToPatchedFunctions to set up a
// particular interception, and InitializeInterceptions to actually go ahead and
// perform all interceptions and transfer data to the child application.
//
// The typical usage is something like this:
//
// InterceptionManager interception_manager(child);
// if (!interception_manager.AddToPatchedFunctions(
//         L"ntdll.dll", "NtCreateFile",
//         sandbox::INTERCEPTION_SERVICE_CALL, &MyNtCreateFile, MY_ID_1))
//   return false;
//
// if (!interception_manager.AddToPatchedFunctions(
//         L"kernel32.dll", "CreateDirectoryW",
//         sandbox::INTERCEPTION_EAT, L"MyCreateDirectoryW@12", MY_ID_2))
//   return false;
//
// sandbox::ResultCode rc = interception_manager.InitializeInterceptions();
// if (rc != sandbox::SBOX_ALL_OK) {
//   DWORD error = ::GetLastError();
//   return rc;
// }
//
// Any required syncronization must be performed outside this class. Also, it is
// not possible to perform further interceptions after InitializeInterceptions
// is called.
//
class InterceptionManager {
  // The unit test will access private members.
  // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes
  // do not work with sandbox tests.
  FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout1);
  FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout2);

 public:
  // An interception manager performs interceptions on a given child process.
  // If we are allowed to intercept functions that have been patched by somebody
  // else, relaxed should be set to true.
  // Note: We increase the child's reference count internally.
  InterceptionManager(TargetProcess* child_process, bool relaxed);
  ~InterceptionManager();

  // Patches function_name inside dll_name to point to replacement_code_address.
  // function_name has to be an exported symbol of dll_name.
  // Returns true on success.
  //
  // The new function should match the prototype and calling convention of the
  // function to intercept except for one extra argument (the first one) that
  // contains a pointer to the original function, to simplify the development
  // of interceptors (for IA32). In x64, there is no extra argument to the
  // interceptor, so the provided InterceptorId is used to keep a table of
  // intercepted functions so that the interceptor can index that table to get
  // the pointer that would have been the first argument (g_originals[id]).
  //
  // For example, to intercept NtClose, the following code could be used:
  //
  // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle);
  // NTSTATUS WINAPI MyNtCose(IN NtCloseFunction OriginalClose,
  //                          IN HANDLE Handle) {
  //   // do something
  //   // call the original function
  //   return OriginalClose(Handle);
  // }
  //
  // And in x64:
  //
  // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle);
  // NTSTATUS WINAPI MyNtCose64(IN HANDLE Handle) {
  //   // do something
  //   // call the original function
  //   NtCloseFunction OriginalClose = g_originals[NT_CLOSE_ID];
  //   return OriginalClose(Handle);
  // }
  bool AddToPatchedFunctions(const wchar_t* dll_name,
                             const char* function_name,
                             InterceptionType interception_type,
                             const void* replacement_code_address,
                             InterceptorId id);

  // Patches function_name inside dll_name to point to
  // replacement_function_name.
  bool AddToPatchedFunctions(const wchar_t* dll_name,
                             const char* function_name,
                             InterceptionType interception_type,
                             const char* replacement_function_name,
                             InterceptorId id);

  // The interception agent will unload the dll with dll_name.
  bool AddToUnloadModules(const wchar_t* dll_name);

  // Initializes all interceptions on the client.
  // Returns SBOX_ALL_OK on success, or an appropriate error code.
  //
  // The child process must be created suspended, and cannot be resumed until
  // after this method returns. In addition, no action should be performed on
  // the child that may cause it to resume momentarily, such as injecting
  // threads or APCs.
  //
  // This function must be called only once, after all interceptions have been
  // set up using AddToPatchedFunctions.
  ResultCode InitializeInterceptions();

 private:
  // Used to store the interception information until the actual set-up.
  struct InterceptionData {
    InterceptionData();
    InterceptionData(const InterceptionData& other);
    ~InterceptionData();

    InterceptionType type;            // Interception type.
    InterceptorId id;                 // Interceptor id.
    std::wstring dll;                 // Name of dll to intercept.
    std::string function;             // Name of function to intercept.
    std::string interceptor;          // Name of interceptor function.
    const void* interceptor_address;  // Interceptor's entry point.
  };

  // Calculates the size of the required configuration buffer.
  size_t GetBufferSize() const;

  // Rounds up the size of a given buffer, considering alignment (padding).
  // value is the current size of the buffer, and alignment is specified in
  // bytes.
  static inline size_t RoundUpToMultiple(size_t value, size_t alignment) {
    return ((value + alignment - 1) / alignment) * alignment;
  }

  // Sets up a given buffer with all the information that has to be transfered
  // to the child.
  // Returns true on success.
  //
  // The buffer size should be at least the value returned by GetBufferSize
  bool SetupConfigBuffer(void* buffer, size_t buffer_bytes);

  // Fills up the part of the transfer buffer that corresponds to information
  // about one dll to patch.
  // data is the first recorded interception for this dll.
  // Returns true on success.
  //
  // On successful return, buffer will be advanced from it's current position
  // to the point where the next block of configuration data should be written
  // (the actual interception info), and the current size of the buffer will
  // decrease to account the space used by this method.
  bool SetupDllInfo(const InterceptionData& data,
                    void** buffer,
                    size_t* buffer_bytes) const;

  // Fills up the part of the transfer buffer that corresponds to a single
  // function to patch.
  // dll_info points to the dll being updated with the interception stored on
  // data. The buffer pointer and remaining size are updated by this call.
  // Returns true on success.
  bool SetupInterceptionInfo(const InterceptionData& data,
                             void** buffer,
                             size_t* buffer_bytes,
                             DllPatchInfo* dll_info) const;

  // Returns true if this interception is to be performed by the child
  // as opposed to from the parent.
  bool IsInterceptionPerformedByChild(const InterceptionData& data) const;

  // Allocates a buffer on the child's address space (returned on
  // remote_buffer), and fills it with the contents of a local buffer.
  // Returns SBOX_ALL_OK on success.
  ResultCode CopyDataToChild(const void* local_buffer,
                             size_t buffer_bytes,
                             void** remote_buffer) const;

  // Performs the cold patch (from the parent) of ntdll.
  // Returns SBOX_ALL_OK on success.
  //
  // This method will insert additional interceptions to launch the interceptor
  // agent on the child process, if there are additional interceptions to do.
  ResultCode PatchNtdll(bool hot_patch_needed);

  // Peforms the actual interceptions on ntdll.
  // thunks is the memory to store all the thunks for this dll (on the child),
  // and dll_data is a local buffer to hold global dll interception info.
  // Returns SBOX_ALL_OK on success.
  ResultCode PatchClientFunctions(DllInterceptionData* thunks,
                                  size_t thunk_bytes,
                                  DllInterceptionData* dll_data);

  // The process to intercept.
  TargetProcess* child_;
  // Holds all interception info until the call to initialize (perform the
  // actual patch).
  std::list<InterceptionData> interceptions_;

  // Keep track of patches added by name.
  bool names_used_;

  // true if we are allowed to patch already-patched functions.
  bool relaxed_;

  DISALLOW_COPY_AND_ASSIGN(InterceptionManager);
};

// This macro simply calls interception_manager.AddToPatchedFunctions with
// the given service to intercept (INTERCEPTION_SERVICE_CALL), and assumes that
// the interceptor is called "TargetXXX", where XXX is the name of the service.
// Note that num_params is the number of bytes to pop out of the stack for
// the exported interceptor, following the calling convention of a service call
// (WINAPI = with the "C" underscore).
#if SANDBOX_EXPORTS
#if defined(_WIN64)
#define MAKE_SERVICE_NAME(service, params) "Target" #service "64"
#else
#define MAKE_SERVICE_NAME(service, params) "_Target" #service "@" #params
#endif

#define ADD_NT_INTERCEPTION(service, id, num_params)        \
  AddToPatchedFunctions(kNtdllName, #service,               \
                        sandbox::INTERCEPTION_SERVICE_CALL, \
                        MAKE_SERVICE_NAME(service, num_params), id)

#define INTERCEPT_NT(manager, service, id, num_params)                        \
  ((&Target##service) ? manager->ADD_NT_INTERCEPTION(service, id, num_params) \
                      : false)

// When intercepting the EAT it is important that the patched version of the
// function not call any functions imported from system libraries unless
// |TargetServices::InitCalled()| returns true, because it is only then that
// we are guaranteed that our IAT has been initialized.
#define INTERCEPT_EAT(manager, dll, function, id, num_params)             \
  ((&Target##function) ? manager->AddToPatchedFunctions(                  \
                             dll, #function, sandbox::INTERCEPTION_EAT,   \
                             MAKE_SERVICE_NAME(function, num_params), id) \
                       : false)
#else  // SANDBOX_EXPORTS
#if defined(_WIN64)
#define MAKE_SERVICE_NAME(service) &Target##service##64
#else
#define MAKE_SERVICE_NAME(service) &Target##service
#endif

#define ADD_NT_INTERCEPTION(service, id, num_params)            \
  AddToPatchedFunctions(                                        \
      kNtdllName, #service, sandbox::INTERCEPTION_SERVICE_CALL, \
      reinterpret_cast<void*>(MAKE_SERVICE_NAME(service)), id)

#define INTERCEPT_NT(manager, service, id, num_params) \
  manager->ADD_NT_INTERCEPTION(service, id, num_params)

// When intercepting the EAT it is important that the patched version of the
// function not call any functions imported from system libraries unless
// |TargetServices::InitCalled()| returns true, because it is only then that
// we are guaranteed that our IAT has been initialized.
#define INTERCEPT_EAT(manager, dll, function, id, num_params) \
  manager->AddToPatchedFunctions(                             \
      dll, #function, sandbox::INTERCEPTION_EAT,              \
      reinterpret_cast<void*>(MAKE_SERVICE_NAME(function)), id)
#endif  // SANDBOX_EXPORTS

}  // namespace sandbox

#endif  // SANDBOX_SRC_INTERCEPTION_H_