|
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 "sandbox/win/src/sandbox_types.h" |
|
18 |
|
19 namespace sandbox { |
|
20 |
|
21 class TargetProcess; |
|
22 enum InterceptorId; |
|
23 |
|
24 // Internal structures used for communication between the broker and the target. |
|
25 struct DllPatchInfo; |
|
26 struct DllInterceptionData; |
|
27 |
|
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. |
|
31 // |
|
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. |
|
35 // |
|
36 // The typical usage is something like this: |
|
37 // |
|
38 // InterceptionManager interception_manager(child); |
|
39 // if (!interception_manager.AddToPatchedFunctions( |
|
40 // L"ntdll.dll", "NtCreateFile", |
|
41 // sandbox::INTERCEPTION_SERVICE_CALL, &MyNtCreateFile, MY_ID_1)) |
|
42 // return false; |
|
43 // |
|
44 // if (!interception_manager.AddToPatchedFunctions( |
|
45 // L"kernel32.dll", "CreateDirectoryW", |
|
46 // sandbox::INTERCEPTION_EAT, L"MyCreateDirectoryW@12", MY_ID_2)) |
|
47 // return false; |
|
48 // |
|
49 // if (!interception_manager.InitializeInterceptions()) { |
|
50 // DWORD error = ::GetLastError(); |
|
51 // return false; |
|
52 // } |
|
53 // |
|
54 // Any required syncronization must be performed outside this class. Also, it is |
|
55 // not possible to perform further interceptions after InitializeInterceptions |
|
56 // is called. |
|
57 // |
|
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); |
|
64 |
|
65 public: |
|
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(); |
|
72 |
|
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. |
|
76 // |
|
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]). |
|
84 // |
|
85 // For example, to intercept NtClose, the following code could be used: |
|
86 // |
|
87 // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle); |
|
88 // NTSTATUS WINAPI MyNtCose(IN NtCloseFunction OriginalClose, |
|
89 // IN HANDLE Handle) { |
|
90 // // do something |
|
91 // // call the original function |
|
92 // return OriginalClose(Handle); |
|
93 // } |
|
94 // |
|
95 // And in x64: |
|
96 // |
|
97 // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle); |
|
98 // NTSTATUS WINAPI MyNtCose64(IN HANDLE Handle) { |
|
99 // // do something |
|
100 // // call the original function |
|
101 // NtCloseFunction OriginalClose = g_originals[NT_CLOSE_ID]; |
|
102 // return OriginalClose(Handle); |
|
103 // } |
|
104 bool AddToPatchedFunctions(const wchar_t* dll_name, |
|
105 const char* function_name, |
|
106 InterceptionType interception_type, |
|
107 const void* replacement_code_address, |
|
108 InterceptorId id); |
|
109 |
|
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, |
|
116 InterceptorId id); |
|
117 |
|
118 // The interception agent will unload the dll with dll_name. |
|
119 bool AddToUnloadModules(const wchar_t* dll_name); |
|
120 |
|
121 // Initializes all interceptions on the client. |
|
122 // Returns true on success. |
|
123 // |
|
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 |
|
127 // threads or APCs. |
|
128 // |
|
129 // This function must be called only once, after all interceptions have been |
|
130 // set up using AddToPatchedFunctions. |
|
131 bool InitializeInterceptions(); |
|
132 |
|
133 private: |
|
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. |
|
142 }; |
|
143 |
|
144 // Calculates the size of the required configuration buffer. |
|
145 size_t GetBufferSize() const; |
|
146 |
|
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 |
|
149 // bytes. |
|
150 static inline size_t RoundUpToMultiple(size_t value, size_t alignment) { |
|
151 return ((value + alignment -1) / alignment) * alignment; |
|
152 } |
|
153 |
|
154 // Sets up a given buffer with all the information that has to be transfered |
|
155 // to the child. |
|
156 // Returns true on success. |
|
157 // |
|
158 // The buffer size should be at least the value returned by GetBufferSize |
|
159 bool SetupConfigBuffer(void* buffer, size_t buffer_bytes); |
|
160 |
|
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. |
|
165 // |
|
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; |
|
172 |
|
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; |
|
181 |
|
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; |
|
185 |
|
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; |
|
191 |
|
192 // Performs the cold patch (from the parent) of ntdll. |
|
193 // Returns true on success. |
|
194 // |
|
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); |
|
198 |
|
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, |
|
204 size_t thunk_bytes, |
|
205 DllInterceptionData* dll_data); |
|
206 |
|
207 // The process to intercept. |
|
208 TargetProcess* child_; |
|
209 // Holds all interception info until the call to initialize (perform the |
|
210 // actual patch). |
|
211 std::list<InterceptionData> interceptions_; |
|
212 |
|
213 // Keep track of patches added by name. |
|
214 bool names_used_; |
|
215 |
|
216 // true if we are allowed to patch already-patched functions. |
|
217 bool relaxed_; |
|
218 |
|
219 DISALLOW_COPY_AND_ASSIGN(InterceptionManager); |
|
220 }; |
|
221 |
|
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). |
|
228 #if SANDBOX_EXPORTS |
|
229 #if defined(_WIN64) |
|
230 #define MAKE_SERVICE_NAME(service, params) "Target" # service "64" |
|
231 #else |
|
232 #define MAKE_SERVICE_NAME(service, params) "_Target" # service "@" # params |
|
233 #endif |
|
234 |
|
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) |
|
239 |
|
240 #define INTERCEPT_NT(manager, service, id, num_params) \ |
|
241 ((&Target##service) ? \ |
|
242 manager->ADD_NT_INTERCEPTION(service, id, num_params) : false) |
|
243 |
|
244 #define INTERCEPT_EAT(manager, dll, function, id, num_params) \ |
|
245 ((&Target##function) ? \ |
|
246 manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \ |
|
247 MAKE_SERVICE_NAME(function, num_params), \ |
|
248 id) : \ |
|
249 false) |
|
250 #else // SANDBOX_EXPORTS |
|
251 #if defined(_WIN64) |
|
252 #define MAKE_SERVICE_NAME(service) &Target##service##64 |
|
253 #else |
|
254 #define MAKE_SERVICE_NAME(service) &Target##service |
|
255 #endif |
|
256 |
|
257 #define ADD_NT_INTERCEPTION(service, id, num_params) \ |
|
258 AddToPatchedFunctions(kNtdllName, #service, \ |
|
259 sandbox::INTERCEPTION_SERVICE_CALL, \ |
|
260 MAKE_SERVICE_NAME(service), id) |
|
261 |
|
262 #define INTERCEPT_NT(manager, service, id, num_params) \ |
|
263 manager->ADD_NT_INTERCEPTION(service, id, num_params) |
|
264 |
|
265 #define INTERCEPT_EAT(manager, dll, function, id, num_params) \ |
|
266 manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \ |
|
267 MAKE_SERVICE_NAME(function), id) |
|
268 #endif // SANDBOX_EXPORTS |
|
269 |
|
270 } // namespace sandbox |
|
271 |
|
272 #endif // SANDBOX_SRC_INTERCEPTION_H_ |