1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/sandbox/win/src/interception.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,272 @@ 1.4 +// Copyright (c) 2011 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +// Defines InterceptionManager, the class in charge of setting up interceptions 1.9 +// for the sandboxed process. For more details see 1.10 +// http://dev.chromium.org/developers/design-documents/sandbox . 1.11 + 1.12 +#ifndef SANDBOX_SRC_INTERCEPTION_H_ 1.13 +#define SANDBOX_SRC_INTERCEPTION_H_ 1.14 + 1.15 +#include <list> 1.16 +#include <string> 1.17 + 1.18 +#include "base/basictypes.h" 1.19 +#include "base/gtest_prod_util.h" 1.20 +#include "sandbox/win/src/sandbox_types.h" 1.21 + 1.22 +namespace sandbox { 1.23 + 1.24 +class TargetProcess; 1.25 +enum InterceptorId; 1.26 + 1.27 +// Internal structures used for communication between the broker and the target. 1.28 +struct DllPatchInfo; 1.29 +struct DllInterceptionData; 1.30 + 1.31 +// The InterceptionManager executes on the parent application, and it is in 1.32 +// charge of setting up the desired interceptions, and placing the Interception 1.33 +// Agent into the child application. 1.34 +// 1.35 +// The exposed API consists of two methods: AddToPatchedFunctions to set up a 1.36 +// particular interception, and InitializeInterceptions to actually go ahead and 1.37 +// perform all interceptions and transfer data to the child application. 1.38 +// 1.39 +// The typical usage is something like this: 1.40 +// 1.41 +// InterceptionManager interception_manager(child); 1.42 +// if (!interception_manager.AddToPatchedFunctions( 1.43 +// L"ntdll.dll", "NtCreateFile", 1.44 +// sandbox::INTERCEPTION_SERVICE_CALL, &MyNtCreateFile, MY_ID_1)) 1.45 +// return false; 1.46 +// 1.47 +// if (!interception_manager.AddToPatchedFunctions( 1.48 +// L"kernel32.dll", "CreateDirectoryW", 1.49 +// sandbox::INTERCEPTION_EAT, L"MyCreateDirectoryW@12", MY_ID_2)) 1.50 +// return false; 1.51 +// 1.52 +// if (!interception_manager.InitializeInterceptions()) { 1.53 +// DWORD error = ::GetLastError(); 1.54 +// return false; 1.55 +// } 1.56 +// 1.57 +// Any required syncronization must be performed outside this class. Also, it is 1.58 +// not possible to perform further interceptions after InitializeInterceptions 1.59 +// is called. 1.60 +// 1.61 +class InterceptionManager { 1.62 + // The unit test will access private members. 1.63 + // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes 1.64 + // do not work with sandbox tests. 1.65 + FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout1); 1.66 + FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout2); 1.67 + 1.68 + public: 1.69 + // An interception manager performs interceptions on a given child process. 1.70 + // If we are allowed to intercept functions that have been patched by somebody 1.71 + // else, relaxed should be set to true. 1.72 + // Note: We increase the child's reference count internally. 1.73 + InterceptionManager(TargetProcess* child_process, bool relaxed); 1.74 + ~InterceptionManager(); 1.75 + 1.76 + // Patches function_name inside dll_name to point to replacement_code_address. 1.77 + // function_name has to be an exported symbol of dll_name. 1.78 + // Returns true on success. 1.79 + // 1.80 + // The new function should match the prototype and calling convention of the 1.81 + // function to intercept except for one extra argument (the first one) that 1.82 + // contains a pointer to the original function, to simplify the development 1.83 + // of interceptors (for IA32). In x64, there is no extra argument to the 1.84 + // interceptor, so the provided InterceptorId is used to keep a table of 1.85 + // intercepted functions so that the interceptor can index that table to get 1.86 + // the pointer that would have been the first argument (g_originals[id]). 1.87 + // 1.88 + // For example, to intercept NtClose, the following code could be used: 1.89 + // 1.90 + // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle); 1.91 + // NTSTATUS WINAPI MyNtCose(IN NtCloseFunction OriginalClose, 1.92 + // IN HANDLE Handle) { 1.93 + // // do something 1.94 + // // call the original function 1.95 + // return OriginalClose(Handle); 1.96 + // } 1.97 + // 1.98 + // And in x64: 1.99 + // 1.100 + // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle); 1.101 + // NTSTATUS WINAPI MyNtCose64(IN HANDLE Handle) { 1.102 + // // do something 1.103 + // // call the original function 1.104 + // NtCloseFunction OriginalClose = g_originals[NT_CLOSE_ID]; 1.105 + // return OriginalClose(Handle); 1.106 + // } 1.107 + bool AddToPatchedFunctions(const wchar_t* dll_name, 1.108 + const char* function_name, 1.109 + InterceptionType interception_type, 1.110 + const void* replacement_code_address, 1.111 + InterceptorId id); 1.112 + 1.113 + // Patches function_name inside dll_name to point to 1.114 + // replacement_function_name. 1.115 + bool AddToPatchedFunctions(const wchar_t* dll_name, 1.116 + const char* function_name, 1.117 + InterceptionType interception_type, 1.118 + const char* replacement_function_name, 1.119 + InterceptorId id); 1.120 + 1.121 + // The interception agent will unload the dll with dll_name. 1.122 + bool AddToUnloadModules(const wchar_t* dll_name); 1.123 + 1.124 + // Initializes all interceptions on the client. 1.125 + // Returns true on success. 1.126 + // 1.127 + // The child process must be created suspended, and cannot be resumed until 1.128 + // after this method returns. In addition, no action should be performed on 1.129 + // the child that may cause it to resume momentarily, such as injecting 1.130 + // threads or APCs. 1.131 + // 1.132 + // This function must be called only once, after all interceptions have been 1.133 + // set up using AddToPatchedFunctions. 1.134 + bool InitializeInterceptions(); 1.135 + 1.136 + private: 1.137 + // Used to store the interception information until the actual set-up. 1.138 + struct InterceptionData { 1.139 + InterceptionType type; // Interception type. 1.140 + InterceptorId id; // Interceptor id. 1.141 + std::wstring dll; // Name of dll to intercept. 1.142 + std::string function; // Name of function to intercept. 1.143 + std::string interceptor; // Name of interceptor function. 1.144 + const void* interceptor_address; // Interceptor's entry point. 1.145 + }; 1.146 + 1.147 + // Calculates the size of the required configuration buffer. 1.148 + size_t GetBufferSize() const; 1.149 + 1.150 + // Rounds up the size of a given buffer, considering alignment (padding). 1.151 + // value is the current size of the buffer, and alignment is specified in 1.152 + // bytes. 1.153 + static inline size_t RoundUpToMultiple(size_t value, size_t alignment) { 1.154 + return ((value + alignment -1) / alignment) * alignment; 1.155 + } 1.156 + 1.157 + // Sets up a given buffer with all the information that has to be transfered 1.158 + // to the child. 1.159 + // Returns true on success. 1.160 + // 1.161 + // The buffer size should be at least the value returned by GetBufferSize 1.162 + bool SetupConfigBuffer(void* buffer, size_t buffer_bytes); 1.163 + 1.164 + // Fills up the part of the transfer buffer that corresponds to information 1.165 + // about one dll to patch. 1.166 + // data is the first recorded interception for this dll. 1.167 + // Returns true on success. 1.168 + // 1.169 + // On successful return, buffer will be advanced from it's current position 1.170 + // to the point where the next block of configuration data should be written 1.171 + // (the actual interception info), and the current size of the buffer will 1.172 + // decrease to account the space used by this method. 1.173 + bool SetupDllInfo(const InterceptionData& data, 1.174 + void** buffer, size_t* buffer_bytes) const; 1.175 + 1.176 + // Fills up the part of the transfer buffer that corresponds to a single 1.177 + // function to patch. 1.178 + // dll_info points to the dll being updated with the interception stored on 1.179 + // data. The buffer pointer and remaining size are updated by this call. 1.180 + // Returns true on success. 1.181 + bool SetupInterceptionInfo(const InterceptionData& data, void** buffer, 1.182 + size_t* buffer_bytes, 1.183 + DllPatchInfo* dll_info) const; 1.184 + 1.185 + // Returns true if this interception is to be performed by the child 1.186 + // as opposed to from the parent. 1.187 + bool IsInterceptionPerformedByChild(const InterceptionData& data) const; 1.188 + 1.189 + // Allocates a buffer on the child's address space (returned on 1.190 + // remote_buffer), and fills it with the contents of a local buffer. 1.191 + // Returns true on success. 1.192 + bool CopyDataToChild(const void* local_buffer, size_t buffer_bytes, 1.193 + void** remote_buffer) const; 1.194 + 1.195 + // Performs the cold patch (from the parent) of ntdll. 1.196 + // Returns true on success. 1.197 + // 1.198 + // This method will insert additional interceptions to launch the interceptor 1.199 + // agent on the child process, if there are additional interceptions to do. 1.200 + bool PatchNtdll(bool hot_patch_needed); 1.201 + 1.202 + // Peforms the actual interceptions on ntdll. 1.203 + // thunks is the memory to store all the thunks for this dll (on the child), 1.204 + // and dll_data is a local buffer to hold global dll interception info. 1.205 + // Returns true on success. 1.206 + bool PatchClientFunctions(DllInterceptionData* thunks, 1.207 + size_t thunk_bytes, 1.208 + DllInterceptionData* dll_data); 1.209 + 1.210 + // The process to intercept. 1.211 + TargetProcess* child_; 1.212 + // Holds all interception info until the call to initialize (perform the 1.213 + // actual patch). 1.214 + std::list<InterceptionData> interceptions_; 1.215 + 1.216 + // Keep track of patches added by name. 1.217 + bool names_used_; 1.218 + 1.219 + // true if we are allowed to patch already-patched functions. 1.220 + bool relaxed_; 1.221 + 1.222 + DISALLOW_COPY_AND_ASSIGN(InterceptionManager); 1.223 +}; 1.224 + 1.225 +// This macro simply calls interception_manager.AddToPatchedFunctions with 1.226 +// the given service to intercept (INTERCEPTION_SERVICE_CALL), and assumes that 1.227 +// the interceptor is called "TargetXXX", where XXX is the name of the service. 1.228 +// Note that num_params is the number of bytes to pop out of the stack for 1.229 +// the exported interceptor, following the calling convention of a service call 1.230 +// (WINAPI = with the "C" underscore). 1.231 +#if SANDBOX_EXPORTS 1.232 +#if defined(_WIN64) 1.233 +#define MAKE_SERVICE_NAME(service, params) "Target" # service "64" 1.234 +#else 1.235 +#define MAKE_SERVICE_NAME(service, params) "_Target" # service "@" # params 1.236 +#endif 1.237 + 1.238 +#define ADD_NT_INTERCEPTION(service, id, num_params) \ 1.239 + AddToPatchedFunctions(kNtdllName, #service, \ 1.240 + sandbox::INTERCEPTION_SERVICE_CALL, \ 1.241 + MAKE_SERVICE_NAME(service, num_params), id) 1.242 + 1.243 +#define INTERCEPT_NT(manager, service, id, num_params) \ 1.244 + ((&Target##service) ? \ 1.245 + manager->ADD_NT_INTERCEPTION(service, id, num_params) : false) 1.246 + 1.247 +#define INTERCEPT_EAT(manager, dll, function, id, num_params) \ 1.248 + ((&Target##function) ? \ 1.249 + manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \ 1.250 + MAKE_SERVICE_NAME(function, num_params), \ 1.251 + id) : \ 1.252 + false) 1.253 +#else // SANDBOX_EXPORTS 1.254 +#if defined(_WIN64) 1.255 +#define MAKE_SERVICE_NAME(service) &Target##service##64 1.256 +#else 1.257 +#define MAKE_SERVICE_NAME(service) &Target##service 1.258 +#endif 1.259 + 1.260 +#define ADD_NT_INTERCEPTION(service, id, num_params) \ 1.261 + AddToPatchedFunctions(kNtdllName, #service, \ 1.262 + sandbox::INTERCEPTION_SERVICE_CALL, \ 1.263 + MAKE_SERVICE_NAME(service), id) 1.264 + 1.265 +#define INTERCEPT_NT(manager, service, id, num_params) \ 1.266 + manager->ADD_NT_INTERCEPTION(service, id, num_params) 1.267 + 1.268 +#define INTERCEPT_EAT(manager, dll, function, id, num_params) \ 1.269 + manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \ 1.270 + MAKE_SERVICE_NAME(function), id) 1.271 +#endif // SANDBOX_EXPORTS 1.272 + 1.273 +} // namespace sandbox 1.274 + 1.275 +#endif // SANDBOX_SRC_INTERCEPTION_H_