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 20 namespace sandbox { 21 22 class TargetProcess; 23 enum InterceptorId; 24 25 // Internal structures used for communication between the broker and the target. 26 struct DllPatchInfo; 27 struct 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 // 59 class 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