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_ 273