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