diff options
Diffstat (limited to 'security/sandbox/chromium/sandbox/win/src/interception.h')
-rw-r--r-- | security/sandbox/chromium/sandbox/win/src/interception.h | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/security/sandbox/chromium/sandbox/win/src/interception.h b/security/sandbox/chromium/sandbox/win/src/interception.h new file mode 100644 index 0000000000..6b4612fb6c --- /dev/null +++ b/security/sandbox/chromium/sandbox/win/src/interception.h @@ -0,0 +1,290 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines InterceptionManager, the class in charge of setting up interceptions +// for the sandboxed process. For more details see +// http://dev.chromium.org/developers/design-documents/sandbox . + +#ifndef SANDBOX_SRC_INTERCEPTION_H_ +#define SANDBOX_SRC_INTERCEPTION_H_ + +#include <stddef.h> + +#include <list> +#include <string> + +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "sandbox/win/src/interceptors.h" +#include "sandbox/win/src/sandbox_types.h" + +namespace sandbox { + +class TargetProcess; + +// Internal structures used for communication between the broker and the target. +struct DllPatchInfo; +struct DllInterceptionData; + +// The InterceptionManager executes on the parent application, and it is in +// charge of setting up the desired interceptions, and placing the Interception +// Agent into the child application. +// +// The exposed API consists of two methods: AddToPatchedFunctions to set up a +// particular interception, and InitializeInterceptions to actually go ahead and +// perform all interceptions and transfer data to the child application. +// +// The typical usage is something like this: +// +// InterceptionManager interception_manager(child); +// if (!interception_manager.AddToPatchedFunctions( +// L"ntdll.dll", "NtCreateFile", +// sandbox::INTERCEPTION_SERVICE_CALL, &MyNtCreateFile, MY_ID_1)) +// return false; +// +// if (!interception_manager.AddToPatchedFunctions( +// L"kernel32.dll", "CreateDirectoryW", +// sandbox::INTERCEPTION_EAT, L"MyCreateDirectoryW@12", MY_ID_2)) +// return false; +// +// sandbox::ResultCode rc = interception_manager.InitializeInterceptions(); +// if (rc != sandbox::SBOX_ALL_OK) { +// DWORD error = ::GetLastError(); +// return rc; +// } +// +// Any required syncronization must be performed outside this class. Also, it is +// not possible to perform further interceptions after InitializeInterceptions +// is called. +// +class InterceptionManager { + // The unit test will access private members. + // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes + // do not work with sandbox tests. + FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout1); + FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout2); + + public: + // An interception manager performs interceptions on a given child process. + // If we are allowed to intercept functions that have been patched by somebody + // else, relaxed should be set to true. + // Note: We increase the child's reference count internally. + InterceptionManager(TargetProcess* child_process, bool relaxed); + ~InterceptionManager(); + + // Patches function_name inside dll_name to point to replacement_code_address. + // function_name has to be an exported symbol of dll_name. + // Returns true on success. + // + // The new function should match the prototype and calling convention of the + // function to intercept except for one extra argument (the first one) that + // contains a pointer to the original function, to simplify the development + // of interceptors (for IA32). In x64, there is no extra argument to the + // interceptor, so the provided InterceptorId is used to keep a table of + // intercepted functions so that the interceptor can index that table to get + // the pointer that would have been the first argument (g_originals[id]). + // + // For example, to intercept NtClose, the following code could be used: + // + // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle); + // NTSTATUS WINAPI MyNtCose(IN NtCloseFunction OriginalClose, + // IN HANDLE Handle) { + // // do something + // // call the original function + // return OriginalClose(Handle); + // } + // + // And in x64: + // + // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle); + // NTSTATUS WINAPI MyNtCose64(IN HANDLE Handle) { + // // do something + // // call the original function + // NtCloseFunction OriginalClose = g_originals[NT_CLOSE_ID]; + // return OriginalClose(Handle); + // } + bool AddToPatchedFunctions(const wchar_t* dll_name, + const char* function_name, + InterceptionType interception_type, + const void* replacement_code_address, + InterceptorId id); + + // Patches function_name inside dll_name to point to + // replacement_function_name. + bool AddToPatchedFunctions(const wchar_t* dll_name, + const char* function_name, + InterceptionType interception_type, + const char* replacement_function_name, + InterceptorId id); + + // The interception agent will unload the dll with dll_name. + bool AddToUnloadModules(const wchar_t* dll_name); + + // Initializes all interceptions on the client. + // Returns SBOX_ALL_OK on success, or an appropriate error code. + // + // The child process must be created suspended, and cannot be resumed until + // after this method returns. In addition, no action should be performed on + // the child that may cause it to resume momentarily, such as injecting + // threads or APCs. + // + // This function must be called only once, after all interceptions have been + // set up using AddToPatchedFunctions. + ResultCode InitializeInterceptions(); + + private: + // Used to store the interception information until the actual set-up. + struct InterceptionData { + InterceptionData(); + InterceptionData(const InterceptionData& other); + ~InterceptionData(); + + InterceptionType type; // Interception type. + InterceptorId id; // Interceptor id. + std::wstring dll; // Name of dll to intercept. + std::string function; // Name of function to intercept. + std::string interceptor; // Name of interceptor function. + const void* interceptor_address; // Interceptor's entry point. + }; + + // Calculates the size of the required configuration buffer. + size_t GetBufferSize() const; + + // Rounds up the size of a given buffer, considering alignment (padding). + // value is the current size of the buffer, and alignment is specified in + // bytes. + static inline size_t RoundUpToMultiple(size_t value, size_t alignment) { + return ((value + alignment - 1) / alignment) * alignment; + } + + // Sets up a given buffer with all the information that has to be transfered + // to the child. + // Returns true on success. + // + // The buffer size should be at least the value returned by GetBufferSize + bool SetupConfigBuffer(void* buffer, size_t buffer_bytes); + + // Fills up the part of the transfer buffer that corresponds to information + // about one dll to patch. + // data is the first recorded interception for this dll. + // Returns true on success. + // + // On successful return, buffer will be advanced from it's current position + // to the point where the next block of configuration data should be written + // (the actual interception info), and the current size of the buffer will + // decrease to account the space used by this method. + bool SetupDllInfo(const InterceptionData& data, + void** buffer, + size_t* buffer_bytes) const; + + // Fills up the part of the transfer buffer that corresponds to a single + // function to patch. + // dll_info points to the dll being updated with the interception stored on + // data. The buffer pointer and remaining size are updated by this call. + // Returns true on success. + bool SetupInterceptionInfo(const InterceptionData& data, + void** buffer, + size_t* buffer_bytes, + DllPatchInfo* dll_info) const; + + // Returns true if this interception is to be performed by the child + // as opposed to from the parent. + bool IsInterceptionPerformedByChild(const InterceptionData& data) const; + + // Allocates a buffer on the child's address space (returned on + // remote_buffer), and fills it with the contents of a local buffer. + // Returns SBOX_ALL_OK on success. + ResultCode CopyDataToChild(const void* local_buffer, + size_t buffer_bytes, + void** remote_buffer) const; + + // Performs the cold patch (from the parent) of ntdll. + // Returns SBOX_ALL_OK on success. + // + // This method will insert additional interceptions to launch the interceptor + // agent on the child process, if there are additional interceptions to do. + ResultCode PatchNtdll(bool hot_patch_needed); + + // Peforms the actual interceptions on ntdll. + // thunks is the memory to store all the thunks for this dll (on the child), + // and dll_data is a local buffer to hold global dll interception info. + // Returns SBOX_ALL_OK on success. + ResultCode PatchClientFunctions(DllInterceptionData* thunks, + size_t thunk_bytes, + DllInterceptionData* dll_data); + + // The process to intercept. + TargetProcess* child_; + // Holds all interception info until the call to initialize (perform the + // actual patch). + std::list<InterceptionData> interceptions_; + + // Keep track of patches added by name. + bool names_used_; + + // true if we are allowed to patch already-patched functions. + bool relaxed_; + + DISALLOW_COPY_AND_ASSIGN(InterceptionManager); +}; + +// This macro simply calls interception_manager.AddToPatchedFunctions with +// the given service to intercept (INTERCEPTION_SERVICE_CALL), and assumes that +// the interceptor is called "TargetXXX", where XXX is the name of the service. +// Note that num_params is the number of bytes to pop out of the stack for +// the exported interceptor, following the calling convention of a service call +// (WINAPI = with the "C" underscore). +#if SANDBOX_EXPORTS +#if defined(_WIN64) +#define MAKE_SERVICE_NAME(service, params) "Target" #service "64" +#else +#define MAKE_SERVICE_NAME(service, params) "_Target" #service "@" #params +#endif + +#define ADD_NT_INTERCEPTION(service, id, num_params) \ + AddToPatchedFunctions(kNtdllName, #service, \ + sandbox::INTERCEPTION_SERVICE_CALL, \ + MAKE_SERVICE_NAME(service, num_params), id) + +#define INTERCEPT_NT(manager, service, id, num_params) \ + ((&Target##service) ? manager->ADD_NT_INTERCEPTION(service, id, num_params) \ + : false) + +// When intercepting the EAT it is important that the patched version of the +// function not call any functions imported from system libraries unless +// |TargetServices::InitCalled()| returns true, because it is only then that +// we are guaranteed that our IAT has been initialized. +#define INTERCEPT_EAT(manager, dll, function, id, num_params) \ + ((&Target##function) ? manager->AddToPatchedFunctions( \ + dll, #function, sandbox::INTERCEPTION_EAT, \ + MAKE_SERVICE_NAME(function, num_params), id) \ + : false) +#else // SANDBOX_EXPORTS +#if defined(_WIN64) +#define MAKE_SERVICE_NAME(service) &Target##service##64 +#else +#define MAKE_SERVICE_NAME(service) &Target##service +#endif + +#define ADD_NT_INTERCEPTION(service, id, num_params) \ + AddToPatchedFunctions( \ + kNtdllName, #service, sandbox::INTERCEPTION_SERVICE_CALL, \ + reinterpret_cast<void*>(MAKE_SERVICE_NAME(service)), id) + +#define INTERCEPT_NT(manager, service, id, num_params) \ + manager->ADD_NT_INTERCEPTION(service, id, num_params) + +// When intercepting the EAT it is important that the patched version of the +// function not call any functions imported from system libraries unless +// |TargetServices::InitCalled()| returns true, because it is only then that +// we are guaranteed that our IAT has been initialized. +#define INTERCEPT_EAT(manager, dll, function, id, num_params) \ + manager->AddToPatchedFunctions( \ + dll, #function, sandbox::INTERCEPTION_EAT, \ + reinterpret_cast<void*>(MAKE_SERVICE_NAME(function)), id) +#endif // SANDBOX_EXPORTS + +} // namespace sandbox + +#endif // SANDBOX_SRC_INTERCEPTION_H_ |