security/sandbox/win/src/interception.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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.
     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 .
     9 #ifndef SANDBOX_SRC_INTERCEPTION_H_
    10 #define SANDBOX_SRC_INTERCEPTION_H_
    12 #include <list>
    13 #include <string>
    15 #include "base/basictypes.h"
    16 #include "base/gtest_prod_util.h"
    17 #include "sandbox/win/src/sandbox_types.h"
    19 namespace sandbox {
    21 class TargetProcess;
    22 enum InterceptorId;
    24 // Internal structures used for communication between the broker and the target.
    25 struct DllPatchInfo;
    26 struct DllInterceptionData;
    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);
    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();
    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);
   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);
   118   // The interception agent will unload the dll with dll_name.
   119   bool AddToUnloadModules(const wchar_t* dll_name);
   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();
   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   };
   144   // Calculates the size of the required configuration buffer.
   145   size_t GetBufferSize() const;
   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   }
   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);
   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;
   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;
   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;
   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;
   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);
   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);
   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_;
   213   // Keep track of patches added by name.
   214   bool names_used_;
   216   // true if we are allowed to patch already-patched functions.
   217   bool relaxed_;
   219   DISALLOW_COPY_AND_ASSIGN(InterceptionManager);
   220 };
   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
   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)
   240 #define INTERCEPT_NT(manager, service, id, num_params) \
   241   ((&Target##service) ? \
   242     manager->ADD_NT_INTERCEPTION(service, id, num_params) : false)
   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
   257 #define ADD_NT_INTERCEPTION(service, id, num_params) \
   258   AddToPatchedFunctions(kNtdllName, #service, \
   259                         sandbox::INTERCEPTION_SERVICE_CALL, \
   260                         MAKE_SERVICE_NAME(service), id)
   262 #define INTERCEPT_NT(manager, service, id, num_params) \
   263   manager->ADD_NT_INTERCEPTION(service, id, num_params)
   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
   270 }  // namespace sandbox
   272 #endif  // SANDBOX_SRC_INTERCEPTION_H_

mercurial