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