DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (5350524bb654)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
// 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 "base/strings/string16.h"
#include "sandbox/win/src/sandbox_types.h"

namespace sandbox {

class TargetProcess;
enum InterceptorId;

// 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;
//
// if (!interception_manager.InitializeInterceptions()) {
//   DWORD error = ::GetLastError();
//   return false;
// }
//
// 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 true on success.
  //
  // 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.
  bool InitializeInterceptions();

 private:
  // Used to store the interception information until the actual set-up.
  struct InterceptionData {
    InterceptionData();
    ~InterceptionData();

    InterceptionType type;            // Interception type.
    InterceptorId id;                 // Interceptor id.
    base::string16 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 true on success.
  bool CopyDataToChild(const void* local_buffer, size_t buffer_bytes,
                       void** remote_buffer) const;

  // Performs the cold patch (from the parent) of ntdll.
  // Returns true on success.
  //
  // This method will insert additional interceptions to launch the interceptor
  // agent on the child process, if there are additional interceptions to do.
  bool 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 true on success.
  bool 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, \
                        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, \
                                 MAKE_SERVICE_NAME(function), id)
#endif  // SANDBOX_EXPORTS

}  // namespace sandbox

#endif  // SANDBOX_SRC_INTERCEPTION_H_