1// Copyright (c) 2011 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// Defines InterceptionManager, the class in charge of setting up interceptions
6// for the sandboxed process. For more details see
7// http://dev.chromium.org/developers/design-documents/sandbox .
8
9#ifndef SANDBOX_SRC_INTERCEPTION_H_
10#define SANDBOX_SRC_INTERCEPTION_H_
11
12#include <list>
13#include <string>
14
15#include "base/basictypes.h"
16#include "base/gtest_prod_util.h"
17#include "base/strings/string16.h"
18#include "sandbox/win/src/sandbox_types.h"
19
20namespace sandbox {
21
22class TargetProcess;
23enum InterceptorId;
24
25// Internal structures used for communication between the broker and the target.
26struct DllPatchInfo;
27struct DllInterceptionData;
28
29// The InterceptionManager executes on the parent application, and it is in
30// charge of setting up the desired interceptions, and placing the Interception
31// Agent into the child application.
32//
33// The exposed API consists of two methods: AddToPatchedFunctions to set up a
34// particular interception, and InitializeInterceptions to actually go ahead and
35// perform all interceptions and transfer data to the child application.
36//
37// The typical usage is something like this:
38//
39// InterceptionManager interception_manager(child);
40// if (!interception_manager.AddToPatchedFunctions(
41//         L"ntdll.dll", "NtCreateFile",
42//         sandbox::INTERCEPTION_SERVICE_CALL, &MyNtCreateFile, MY_ID_1))
43//   return false;
44//
45// if (!interception_manager.AddToPatchedFunctions(
46//         L"kernel32.dll", "CreateDirectoryW",
47//         sandbox::INTERCEPTION_EAT, L"MyCreateDirectoryW@12", MY_ID_2))
48//   return false;
49//
50// if (!interception_manager.InitializeInterceptions()) {
51//   DWORD error = ::GetLastError();
52//   return false;
53// }
54//
55// Any required syncronization must be performed outside this class. Also, it is
56// not possible to perform further interceptions after InitializeInterceptions
57// is called.
58//
59class InterceptionManager {
60  // The unit test will access private members.
61  // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes
62  // do not work with sandbox tests.
63  FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout1);
64  FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout2);
65
66 public:
67  // An interception manager performs interceptions on a given child process.
68  // If we are allowed to intercept functions that have been patched by somebody
69  // else, relaxed should be set to true.
70  // Note: We increase the child's reference count internally.
71  InterceptionManager(TargetProcess* child_process, bool relaxed);
72  ~InterceptionManager();
73
74  // Patches function_name inside dll_name to point to replacement_code_address.
75  // function_name has to be an exported symbol of dll_name.
76  // Returns true on success.
77  //
78  // The new function should match the prototype and calling convention of the
79  // function to intercept except for one extra argument (the first one) that
80  // contains a pointer to the original function, to simplify the development
81  // of interceptors (for IA32). In x64, there is no extra argument to the
82  // interceptor, so the provided InterceptorId is used to keep a table of
83  // intercepted functions so that the interceptor can index that table to get
84  // the pointer that would have been the first argument (g_originals[id]).
85  //
86  // For example, to intercept NtClose, the following code could be used:
87  //
88  // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle);
89  // NTSTATUS WINAPI MyNtCose(IN NtCloseFunction OriginalClose,
90  //                          IN HANDLE Handle) {
91  //   // do something
92  //   // call the original function
93  //   return OriginalClose(Handle);
94  // }
95  //
96  // And in x64:
97  //
98  // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle);
99  // NTSTATUS WINAPI MyNtCose64(IN HANDLE Handle) {
100  //   // do something
101  //   // call the original function
102  //   NtCloseFunction OriginalClose = g_originals[NT_CLOSE_ID];
103  //   return OriginalClose(Handle);
104  // }
105  bool AddToPatchedFunctions(const wchar_t* dll_name,
106                             const char* function_name,
107                             InterceptionType interception_type,
108                             const void* replacement_code_address,
109                             InterceptorId id);
110
111  // Patches function_name inside dll_name to point to
112  // replacement_function_name.
113  bool AddToPatchedFunctions(const wchar_t* dll_name,
114                             const char* function_name,
115                             InterceptionType interception_type,
116                             const char* replacement_function_name,
117                             InterceptorId id);
118
119  // The interception agent will unload the dll with dll_name.
120  bool AddToUnloadModules(const wchar_t* dll_name);
121
122  // Initializes all interceptions on the client.
123  // Returns true on success.
124  //
125  // The child process must be created suspended, and cannot be resumed until
126  // after this method returns. In addition, no action should be performed on
127  // the child that may cause it to resume momentarily, such as injecting
128  // threads or APCs.
129  //
130  // This function must be called only once, after all interceptions have been
131  // set up using AddToPatchedFunctions.
132  bool InitializeInterceptions();
133
134 private:
135  // Used to store the interception information until the actual set-up.
136  struct InterceptionData {
137    InterceptionType type;            // Interception type.
138    InterceptorId id;                 // Interceptor id.
139    base::string16 dll;               // Name of dll to intercept.
140    std::string function;             // Name of function to intercept.
141    std::string interceptor;          // Name of interceptor function.
142    const void* interceptor_address;  // Interceptor's entry point.
143  };
144
145  // Calculates the size of the required configuration buffer.
146  size_t GetBufferSize() const;
147
148  // Rounds up the size of a given buffer, considering alignment (padding).
149  // value is the current size of the buffer, and alignment is specified in
150  // bytes.
151  static inline size_t RoundUpToMultiple(size_t value, size_t alignment) {
152    return ((value + alignment -1) / alignment) * alignment;
153  }
154
155  // Sets up a given buffer with all the information that has to be transfered
156  // to the child.
157  // Returns true on success.
158  //
159  // The buffer size should be at least the value returned by GetBufferSize
160  bool SetupConfigBuffer(void* buffer, size_t buffer_bytes);
161
162  // Fills up the part of the transfer buffer that corresponds to information
163  // about one dll to patch.
164  // data is the first recorded interception for this dll.
165  // Returns true on success.
166  //
167  // On successful return, buffer will be advanced from it's current position
168  // to the point where the next block of configuration data should be written
169  // (the actual interception info), and the current size of the buffer will
170  // decrease to account the space used by this method.
171  bool SetupDllInfo(const InterceptionData& data,
172                    void** buffer, size_t* buffer_bytes) const;
173
174  // Fills up the part of the transfer buffer that corresponds to a single
175  // function to patch.
176  // dll_info points to the dll being updated with the interception stored on
177  // data. The buffer pointer and remaining size are updated by this call.
178  // Returns true on success.
179  bool SetupInterceptionInfo(const InterceptionData& data, void** buffer,
180                             size_t* buffer_bytes,
181                             DllPatchInfo* dll_info) const;
182
183  // Returns true if this interception is to be performed by the child
184  // as opposed to from the parent.
185  bool IsInterceptionPerformedByChild(const InterceptionData& data) const;
186
187  // Allocates a buffer on the child's address space (returned on
188  // remote_buffer), and fills it with the contents of a local buffer.
189  // Returns true on success.
190  bool CopyDataToChild(const void* local_buffer, size_t buffer_bytes,
191                       void** remote_buffer) const;
192
193  // Performs the cold patch (from the parent) of ntdll.
194  // Returns true on success.
195  //
196  // This method will insert additional interceptions to launch the interceptor
197  // agent on the child process, if there are additional interceptions to do.
198  bool PatchNtdll(bool hot_patch_needed);
199
200  // Peforms the actual interceptions on ntdll.
201  // thunks is the memory to store all the thunks for this dll (on the child),
202  // and dll_data is a local buffer to hold global dll interception info.
203  // Returns true on success.
204  bool PatchClientFunctions(DllInterceptionData* thunks,
205                            size_t thunk_bytes,
206                            DllInterceptionData* dll_data);
207
208  // The process to intercept.
209  TargetProcess* child_;
210  // Holds all interception info until the call to initialize (perform the
211  // actual patch).
212  std::list<InterceptionData> interceptions_;
213
214  // Keep track of patches added by name.
215  bool names_used_;
216
217  // true if we are allowed to patch already-patched functions.
218  bool relaxed_;
219
220  DISALLOW_COPY_AND_ASSIGN(InterceptionManager);
221};
222
223// This macro simply calls interception_manager.AddToPatchedFunctions with
224// the given service to intercept (INTERCEPTION_SERVICE_CALL), and assumes that
225// the interceptor is called "TargetXXX", where XXX is the name of the service.
226// Note that num_params is the number of bytes to pop out of the stack for
227// the exported interceptor, following the calling convention of a service call
228// (WINAPI = with the "C" underscore).
229#if SANDBOX_EXPORTS
230#if defined(_WIN64)
231#define MAKE_SERVICE_NAME(service, params) "Target" # service "64"
232#else
233#define MAKE_SERVICE_NAME(service, params) "_Target" # service "@" # params
234#endif
235
236#define ADD_NT_INTERCEPTION(service, id, num_params) \
237  AddToPatchedFunctions(kNtdllName, #service, \
238                        sandbox::INTERCEPTION_SERVICE_CALL, \
239                        MAKE_SERVICE_NAME(service, num_params), id)
240
241#define INTERCEPT_NT(manager, service, id, num_params) \
242  ((&Target##service) ? \
243    manager->ADD_NT_INTERCEPTION(service, id, num_params) : false)
244
245// When intercepting the EAT it is important that the patched version of the
246// function not call any functions imported from system libraries unless
247// |TargetServices::InitCalled()| returns true, because it is only then that
248// we are guaranteed that our IAT has been initialized.
249#define INTERCEPT_EAT(manager, dll, function, id, num_params) \
250  ((&Target##function) ? \
251    manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \
252                                   MAKE_SERVICE_NAME(function, num_params), \
253                                   id) : \
254    false)
255#else  // SANDBOX_EXPORTS
256#if defined(_WIN64)
257#define MAKE_SERVICE_NAME(service) &Target##service##64
258#else
259#define MAKE_SERVICE_NAME(service) &Target##service
260#endif
261
262#define ADD_NT_INTERCEPTION(service, id, num_params) \
263  AddToPatchedFunctions(kNtdllName, #service, \
264                        sandbox::INTERCEPTION_SERVICE_CALL, \
265                        MAKE_SERVICE_NAME(service), id)
266
267#define INTERCEPT_NT(manager, service, id, num_params) \
268  manager->ADD_NT_INTERCEPTION(service, id, num_params)
269
270// When intercepting the EAT it is important that the patched version of the
271// function not call any functions imported from system libraries unless
272// |TargetServices::InitCalled()| returns true, because it is only then that
273// we are guaranteed that our IAT has been initialized.
274#define INTERCEPT_EAT(manager, dll, function, id, num_params) \
275  manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \
276                                 MAKE_SERVICE_NAME(function), id)
277#endif  // SANDBOX_EXPORTS
278
279}  // namespace sandbox
280
281#endif  // SANDBOX_SRC_INTERCEPTION_H_
282