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