security/sandbox/win/src/interception.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/sandbox/win/src/interception.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,272 @@
     1.4 +// Copyright (c) 2011 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +// Defines InterceptionManager, the class in charge of setting up interceptions
     1.9 +// for the sandboxed process. For more details see
    1.10 +// http://dev.chromium.org/developers/design-documents/sandbox .
    1.11 +
    1.12 +#ifndef SANDBOX_SRC_INTERCEPTION_H_
    1.13 +#define SANDBOX_SRC_INTERCEPTION_H_
    1.14 +
    1.15 +#include <list>
    1.16 +#include <string>
    1.17 +
    1.18 +#include "base/basictypes.h"
    1.19 +#include "base/gtest_prod_util.h"
    1.20 +#include "sandbox/win/src/sandbox_types.h"
    1.21 +
    1.22 +namespace sandbox {
    1.23 +
    1.24 +class TargetProcess;
    1.25 +enum InterceptorId;
    1.26 +
    1.27 +// Internal structures used for communication between the broker and the target.
    1.28 +struct DllPatchInfo;
    1.29 +struct DllInterceptionData;
    1.30 +
    1.31 +// The InterceptionManager executes on the parent application, and it is in
    1.32 +// charge of setting up the desired interceptions, and placing the Interception
    1.33 +// Agent into the child application.
    1.34 +//
    1.35 +// The exposed API consists of two methods: AddToPatchedFunctions to set up a
    1.36 +// particular interception, and InitializeInterceptions to actually go ahead and
    1.37 +// perform all interceptions and transfer data to the child application.
    1.38 +//
    1.39 +// The typical usage is something like this:
    1.40 +//
    1.41 +// InterceptionManager interception_manager(child);
    1.42 +// if (!interception_manager.AddToPatchedFunctions(
    1.43 +//         L"ntdll.dll", "NtCreateFile",
    1.44 +//         sandbox::INTERCEPTION_SERVICE_CALL, &MyNtCreateFile, MY_ID_1))
    1.45 +//   return false;
    1.46 +//
    1.47 +// if (!interception_manager.AddToPatchedFunctions(
    1.48 +//         L"kernel32.dll", "CreateDirectoryW",
    1.49 +//         sandbox::INTERCEPTION_EAT, L"MyCreateDirectoryW@12", MY_ID_2))
    1.50 +//   return false;
    1.51 +//
    1.52 +// if (!interception_manager.InitializeInterceptions()) {
    1.53 +//   DWORD error = ::GetLastError();
    1.54 +//   return false;
    1.55 +// }
    1.56 +//
    1.57 +// Any required syncronization must be performed outside this class. Also, it is
    1.58 +// not possible to perform further interceptions after InitializeInterceptions
    1.59 +// is called.
    1.60 +//
    1.61 +class InterceptionManager {
    1.62 +  // The unit test will access private members.
    1.63 +  // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes
    1.64 +  // do not work with sandbox tests.
    1.65 +  FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout1);
    1.66 +  FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout2);
    1.67 +
    1.68 + public:
    1.69 +  // An interception manager performs interceptions on a given child process.
    1.70 +  // If we are allowed to intercept functions that have been patched by somebody
    1.71 +  // else, relaxed should be set to true.
    1.72 +  // Note: We increase the child's reference count internally.
    1.73 +  InterceptionManager(TargetProcess* child_process, bool relaxed);
    1.74 +  ~InterceptionManager();
    1.75 +
    1.76 +  // Patches function_name inside dll_name to point to replacement_code_address.
    1.77 +  // function_name has to be an exported symbol of dll_name.
    1.78 +  // Returns true on success.
    1.79 +  //
    1.80 +  // The new function should match the prototype and calling convention of the
    1.81 +  // function to intercept except for one extra argument (the first one) that
    1.82 +  // contains a pointer to the original function, to simplify the development
    1.83 +  // of interceptors (for IA32). In x64, there is no extra argument to the
    1.84 +  // interceptor, so the provided InterceptorId is used to keep a table of
    1.85 +  // intercepted functions so that the interceptor can index that table to get
    1.86 +  // the pointer that would have been the first argument (g_originals[id]).
    1.87 +  //
    1.88 +  // For example, to intercept NtClose, the following code could be used:
    1.89 +  //
    1.90 +  // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle);
    1.91 +  // NTSTATUS WINAPI MyNtCose(IN NtCloseFunction OriginalClose,
    1.92 +  //                          IN HANDLE Handle) {
    1.93 +  //   // do something
    1.94 +  //   // call the original function
    1.95 +  //   return OriginalClose(Handle);
    1.96 +  // }
    1.97 +  //
    1.98 +  // And in x64:
    1.99 +  //
   1.100 +  // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle);
   1.101 +  // NTSTATUS WINAPI MyNtCose64(IN HANDLE Handle) {
   1.102 +  //   // do something
   1.103 +  //   // call the original function
   1.104 +  //   NtCloseFunction OriginalClose = g_originals[NT_CLOSE_ID];
   1.105 +  //   return OriginalClose(Handle);
   1.106 +  // }
   1.107 +  bool AddToPatchedFunctions(const wchar_t* dll_name,
   1.108 +                             const char* function_name,
   1.109 +                             InterceptionType interception_type,
   1.110 +                             const void* replacement_code_address,
   1.111 +                             InterceptorId id);
   1.112 +
   1.113 +  // Patches function_name inside dll_name to point to
   1.114 +  // replacement_function_name.
   1.115 +  bool AddToPatchedFunctions(const wchar_t* dll_name,
   1.116 +                             const char* function_name,
   1.117 +                             InterceptionType interception_type,
   1.118 +                             const char* replacement_function_name,
   1.119 +                             InterceptorId id);
   1.120 +
   1.121 +  // The interception agent will unload the dll with dll_name.
   1.122 +  bool AddToUnloadModules(const wchar_t* dll_name);
   1.123 +
   1.124 +  // Initializes all interceptions on the client.
   1.125 +  // Returns true on success.
   1.126 +  //
   1.127 +  // The child process must be created suspended, and cannot be resumed until
   1.128 +  // after this method returns. In addition, no action should be performed on
   1.129 +  // the child that may cause it to resume momentarily, such as injecting
   1.130 +  // threads or APCs.
   1.131 +  //
   1.132 +  // This function must be called only once, after all interceptions have been
   1.133 +  // set up using AddToPatchedFunctions.
   1.134 +  bool InitializeInterceptions();
   1.135 +
   1.136 + private:
   1.137 +  // Used to store the interception information until the actual set-up.
   1.138 +  struct InterceptionData {
   1.139 +    InterceptionType type;            // Interception type.
   1.140 +    InterceptorId id;                 // Interceptor id.
   1.141 +    std::wstring dll;                 // Name of dll to intercept.
   1.142 +    std::string function;             // Name of function to intercept.
   1.143 +    std::string interceptor;          // Name of interceptor function.
   1.144 +    const void* interceptor_address;  // Interceptor's entry point.
   1.145 +  };
   1.146 +
   1.147 +  // Calculates the size of the required configuration buffer.
   1.148 +  size_t GetBufferSize() const;
   1.149 +
   1.150 +  // Rounds up the size of a given buffer, considering alignment (padding).
   1.151 +  // value is the current size of the buffer, and alignment is specified in
   1.152 +  // bytes.
   1.153 +  static inline size_t RoundUpToMultiple(size_t value, size_t alignment) {
   1.154 +    return ((value + alignment -1) / alignment) * alignment;
   1.155 +  }
   1.156 +
   1.157 +  // Sets up a given buffer with all the information that has to be transfered
   1.158 +  // to the child.
   1.159 +  // Returns true on success.
   1.160 +  //
   1.161 +  // The buffer size should be at least the value returned by GetBufferSize
   1.162 +  bool SetupConfigBuffer(void* buffer, size_t buffer_bytes);
   1.163 +
   1.164 +  // Fills up the part of the transfer buffer that corresponds to information
   1.165 +  // about one dll to patch.
   1.166 +  // data is the first recorded interception for this dll.
   1.167 +  // Returns true on success.
   1.168 +  //
   1.169 +  // On successful return, buffer will be advanced from it's current position
   1.170 +  // to the point where the next block of configuration data should be written
   1.171 +  // (the actual interception info), and the current size of the buffer will
   1.172 +  // decrease to account the space used by this method.
   1.173 +  bool SetupDllInfo(const InterceptionData& data,
   1.174 +                    void** buffer, size_t* buffer_bytes) const;
   1.175 +
   1.176 +  // Fills up the part of the transfer buffer that corresponds to a single
   1.177 +  // function to patch.
   1.178 +  // dll_info points to the dll being updated with the interception stored on
   1.179 +  // data. The buffer pointer and remaining size are updated by this call.
   1.180 +  // Returns true on success.
   1.181 +  bool SetupInterceptionInfo(const InterceptionData& data, void** buffer,
   1.182 +                             size_t* buffer_bytes,
   1.183 +                             DllPatchInfo* dll_info) const;
   1.184 +
   1.185 +  // Returns true if this interception is to be performed by the child
   1.186 +  // as opposed to from the parent.
   1.187 +  bool IsInterceptionPerformedByChild(const InterceptionData& data) const;
   1.188 +
   1.189 +  // Allocates a buffer on the child's address space (returned on
   1.190 +  // remote_buffer), and fills it with the contents of a local buffer.
   1.191 +  // Returns true on success.
   1.192 +  bool CopyDataToChild(const void* local_buffer, size_t buffer_bytes,
   1.193 +                       void** remote_buffer) const;
   1.194 +
   1.195 +  // Performs the cold patch (from the parent) of ntdll.
   1.196 +  // Returns true on success.
   1.197 +  //
   1.198 +  // This method will insert additional interceptions to launch the interceptor
   1.199 +  // agent on the child process, if there are additional interceptions to do.
   1.200 +  bool PatchNtdll(bool hot_patch_needed);
   1.201 +
   1.202 +  // Peforms the actual interceptions on ntdll.
   1.203 +  // thunks is the memory to store all the thunks for this dll (on the child),
   1.204 +  // and dll_data is a local buffer to hold global dll interception info.
   1.205 +  // Returns true on success.
   1.206 +  bool PatchClientFunctions(DllInterceptionData* thunks,
   1.207 +                            size_t thunk_bytes,
   1.208 +                            DllInterceptionData* dll_data);
   1.209 +
   1.210 +  // The process to intercept.
   1.211 +  TargetProcess* child_;
   1.212 +  // Holds all interception info until the call to initialize (perform the
   1.213 +  // actual patch).
   1.214 +  std::list<InterceptionData> interceptions_;
   1.215 +
   1.216 +  // Keep track of patches added by name.
   1.217 +  bool names_used_;
   1.218 +
   1.219 +  // true if we are allowed to patch already-patched functions.
   1.220 +  bool relaxed_;
   1.221 +
   1.222 +  DISALLOW_COPY_AND_ASSIGN(InterceptionManager);
   1.223 +};
   1.224 +
   1.225 +// This macro simply calls interception_manager.AddToPatchedFunctions with
   1.226 +// the given service to intercept (INTERCEPTION_SERVICE_CALL), and assumes that
   1.227 +// the interceptor is called "TargetXXX", where XXX is the name of the service.
   1.228 +// Note that num_params is the number of bytes to pop out of the stack for
   1.229 +// the exported interceptor, following the calling convention of a service call
   1.230 +// (WINAPI = with the "C" underscore).
   1.231 +#if SANDBOX_EXPORTS
   1.232 +#if defined(_WIN64)
   1.233 +#define MAKE_SERVICE_NAME(service, params) "Target" # service "64"
   1.234 +#else
   1.235 +#define MAKE_SERVICE_NAME(service, params) "_Target" # service "@" # params
   1.236 +#endif
   1.237 +
   1.238 +#define ADD_NT_INTERCEPTION(service, id, num_params) \
   1.239 +  AddToPatchedFunctions(kNtdllName, #service, \
   1.240 +                        sandbox::INTERCEPTION_SERVICE_CALL, \
   1.241 +                        MAKE_SERVICE_NAME(service, num_params), id)
   1.242 +
   1.243 +#define INTERCEPT_NT(manager, service, id, num_params) \
   1.244 +  ((&Target##service) ? \
   1.245 +    manager->ADD_NT_INTERCEPTION(service, id, num_params) : false)
   1.246 +
   1.247 +#define INTERCEPT_EAT(manager, dll, function, id, num_params) \
   1.248 +  ((&Target##function) ? \
   1.249 +    manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \
   1.250 +                                   MAKE_SERVICE_NAME(function, num_params), \
   1.251 +                                   id) : \
   1.252 +    false)
   1.253 +#else  // SANDBOX_EXPORTS
   1.254 +#if defined(_WIN64)
   1.255 +#define MAKE_SERVICE_NAME(service) &Target##service##64
   1.256 +#else
   1.257 +#define MAKE_SERVICE_NAME(service) &Target##service
   1.258 +#endif
   1.259 +
   1.260 +#define ADD_NT_INTERCEPTION(service, id, num_params) \
   1.261 +  AddToPatchedFunctions(kNtdllName, #service, \
   1.262 +                        sandbox::INTERCEPTION_SERVICE_CALL, \
   1.263 +                        MAKE_SERVICE_NAME(service), id)
   1.264 +
   1.265 +#define INTERCEPT_NT(manager, service, id, num_params) \
   1.266 +  manager->ADD_NT_INTERCEPTION(service, id, num_params)
   1.267 +
   1.268 +#define INTERCEPT_EAT(manager, dll, function, id, num_params) \
   1.269 +  manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \
   1.270 +                                 MAKE_SERVICE_NAME(function), id)
   1.271 +#endif  // SANDBOX_EXPORTS
   1.272 +
   1.273 +}  // namespace sandbox
   1.274 +
   1.275 +#endif  // SANDBOX_SRC_INTERCEPTION_H_

mercurial