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