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 "sandbox/win/src/sandbox_types.h"
     18 
     19 namespace sandbox {
     20 
     21 class TargetProcess;
     22 enum InterceptorId;
     23 
     24 // Internal structures used for communication between the broker and the target.
     25 struct DllPatchInfo;
     26 struct DllInterceptionData;
     27 
     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);
     64 
     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();
     72 
     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);
    109 
    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);
    117 
    118   // The interception agent will unload the dll with dll_name.
    119   bool AddToUnloadModules(const wchar_t* dll_name);
    120 
    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();
    132 
    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   };
    143 
    144   // Calculates the size of the required configuration buffer.
    145   size_t GetBufferSize() const;
    146 
    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   }
    153 
    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);
    160 
    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;
    172 
    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;
    181 
    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;
    185 
    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;
    191 
    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);
    198 
    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);
    206 
    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_;
    212 
    213   // Keep track of patches added by name.
    214   bool names_used_;
    215 
    216   // true if we are allowed to patch already-patched functions.
    217   bool relaxed_;
    218 
    219   DISALLOW_COPY_AND_ASSIGN(InterceptionManager);
    220 };
    221 
    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
    234 
    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)
    239 
    240 #define INTERCEPT_NT(manager, service, id, num_params) \
    241   ((&Target##service) ? \
    242     manager->ADD_NT_INTERCEPTION(service, id, num_params) : false)
    243 
    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
    256 
    257 #define ADD_NT_INTERCEPTION(service, id, num_params) \
    258   AddToPatchedFunctions(kNtdllName, #service, \
    259                         sandbox::INTERCEPTION_SERVICE_CALL, \
    260                         MAKE_SERVICE_NAME(service), id)
    261 
    262 #define INTERCEPT_NT(manager, service, id, num_params) \
    263   manager->ADD_NT_INTERCEPTION(service, id, num_params)
    264 
    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
    269 
    270 }  // namespace sandbox
    271 
    272 #endif  // SANDBOX_SRC_INTERCEPTION_H_
    273