tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

interception_agent.cc (6723B)


      1 // Copyright 2006-2010 The Chromium Authors
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // For information about interceptions as a whole see
      6 // http://dev.chromium.org/developers/design-documents/sandbox .
      7 
      8 #include "sandbox/win/src/interception_agent.h"
      9 
     10 #include <windows.h>
     11 
     12 #include <stddef.h>
     13 
     14 #include "sandbox/win/src/eat_resolver.h"
     15 #include "sandbox/win/src/interception_internal.h"
     16 #include "sandbox/win/src/interceptors.h"
     17 #include "sandbox/win/src/sandbox_nt_util.h"
     18 
     19 namespace {
     20 
     21 // Returns true if target lies between base and base + range.
     22 bool IsWithinRange(const void* base, size_t range, const void* target) {
     23  const char* end = reinterpret_cast<const char*>(base) + range;
     24  return reinterpret_cast<const char*>(target) < end;
     25 }
     26 
     27 }  // namespace
     28 
     29 namespace sandbox {
     30 
     31 // The list of intercepted functions back-pointers.
     32 SANDBOX_INTERCEPT OriginalFunctions g_originals;
     33 
     34 // Memory buffer mapped from the parent, with the list of interceptions.
     35 SANDBOX_INTERCEPT SharedMemory* g_interceptions = nullptr;
     36 
     37 InterceptionAgent* InterceptionAgent::GetInterceptionAgent() {
     38  static InterceptionAgent* s_singleton = nullptr;
     39  if (!s_singleton) {
     40    if (!g_interceptions)
     41      return nullptr;
     42 
     43    size_t array_bytes = g_interceptions->num_intercepted_dlls * sizeof(void*);
     44    s_singleton = reinterpret_cast<InterceptionAgent*>(
     45        new (NT_ALLOC) char[array_bytes + sizeof(InterceptionAgent)]);
     46 
     47    bool success = s_singleton->Init(g_interceptions);
     48    if (!success) {
     49      operator delete(s_singleton, NT_ALLOC);
     50      s_singleton = nullptr;
     51    }
     52  }
     53  return s_singleton;
     54 }
     55 
     56 bool InterceptionAgent::Init(SharedMemory* shared_memory) {
     57  interceptions_ = shared_memory;
     58  for (size_t i = 0; i < shared_memory->num_intercepted_dlls; i++)
     59    dlls_[i] = nullptr;
     60  return true;
     61 }
     62 
     63 bool InterceptionAgent::DllMatch(const UNICODE_STRING* full_path,
     64                                 const UNICODE_STRING* name,
     65                                 const DllPatchInfo* dll_info) {
     66  UNICODE_STRING current_name;
     67  current_name.Length = static_cast<USHORT>(
     68      GetNtExports()->wcslen(dll_info->dll_name) * sizeof(wchar_t));
     69  current_name.MaximumLength = current_name.Length;
     70  current_name.Buffer = const_cast<wchar_t*>(dll_info->dll_name);
     71 
     72  BOOLEAN case_insensitive = TRUE;
     73  if (full_path && !GetNtExports()->RtlCompareUnicodeString(
     74                       &current_name, full_path, case_insensitive)) {
     75    return true;
     76  }
     77  return name && !GetNtExports()->RtlCompareUnicodeString(&current_name, name,
     78                                                          case_insensitive);
     79 }
     80 
     81 bool InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path,
     82                                  const UNICODE_STRING* name,
     83                                  void* base_address) {
     84  DllPatchInfo* dll_info = interceptions_->dll_list;
     85  size_t i = 0;
     86  for (; i < interceptions_->num_intercepted_dlls; i++) {
     87    if (DllMatch(full_path, name, dll_info))
     88      break;
     89 
     90    dll_info = reinterpret_cast<DllPatchInfo*>(
     91        reinterpret_cast<char*>(dll_info) + dll_info->record_bytes);
     92  }
     93 
     94  // Return now if the dll is not in our list of interest.
     95  if (i == interceptions_->num_intercepted_dlls)
     96    return true;
     97 
     98  // The dll must be unloaded.
     99  if (dll_info->unload_module)
    100    return false;
    101 
    102  // Purify causes this condition to trigger.
    103  if (dlls_[i])
    104    return true;
    105 
    106  size_t buffer_bytes = offsetof(DllInterceptionData, thunks) +
    107                        dll_info->num_functions * sizeof(ThunkData);
    108  dlls_[i] = reinterpret_cast<DllInterceptionData*>(
    109      new (NT_PAGE, base_address) char[buffer_bytes]);
    110 
    111  DCHECK_NT(dlls_[i]);
    112  if (!dlls_[i])
    113    return true;
    114 
    115  dlls_[i]->data_bytes = buffer_bytes;
    116  dlls_[i]->num_thunks = 0;
    117  dlls_[i]->base = base_address;
    118  dlls_[i]->used_bytes = offsetof(DllInterceptionData, thunks);
    119 
    120  VERIFY(PatchDll(dll_info, dlls_[i]));
    121 
    122  ULONG old_protect;
    123  SIZE_T real_size = buffer_bytes;
    124  void* to_protect = dlls_[i];
    125  VERIFY_SUCCESS(GetNtExports()->ProtectVirtualMemory(
    126      NtCurrentProcess, &to_protect, &real_size, PAGE_EXECUTE_READ,
    127      &old_protect));
    128  return true;
    129 }
    130 
    131 void InterceptionAgent::OnDllUnload(void* base_address) {
    132  for (size_t i = 0; i < interceptions_->num_intercepted_dlls; i++) {
    133    if (dlls_[i] && dlls_[i]->base == base_address) {
    134      operator delete(dlls_[i], NT_PAGE);
    135      dlls_[i] = nullptr;
    136      break;
    137    }
    138  }
    139 }
    140 
    141 // TODO(rvargas): We have to deal with prebinded dlls. I see two options: change
    142 // the timestamp of the patched dll, or modify the info on the prebinded dll.
    143 // the first approach messes matching of debug symbols, the second one is more
    144 // complicated.
    145 bool InterceptionAgent::PatchDll(const DllPatchInfo* dll_info,
    146                                 DllInterceptionData* thunks) {
    147  DCHECK_NT(thunks);
    148  DCHECK_NT(dll_info);
    149 
    150  const FunctionInfo* function = reinterpret_cast<const FunctionInfo*>(
    151      reinterpret_cast<const char*>(dll_info) + dll_info->offset_to_functions);
    152 
    153  for (size_t i = 0; i < dll_info->num_functions; i++) {
    154    if (!IsWithinRange(dll_info, dll_info->record_bytes, function->function)) {
    155      NOTREACHED_NT();
    156      return false;
    157    }
    158 
    159    ResolverThunk* resolver = GetResolver(function->type);
    160    if (!resolver)
    161      return false;
    162 
    163    const char* interceptor =
    164        function->function + GetNtExports()->strlen(function->function) + 1;
    165 
    166    if (!IsWithinRange(function, function->record_bytes, interceptor) ||
    167        !IsWithinRange(dll_info, dll_info->record_bytes, interceptor)) {
    168      NOTREACHED_NT();
    169      return false;
    170    }
    171 
    172    NTSTATUS ret = resolver->Setup(
    173        thunks->base, interceptions_->interceptor_base, function->function,
    174        interceptor, function->interceptor_address, &thunks->thunks[i],
    175        &thunks->thunks[i], sizeof(ThunkData), nullptr);
    176    if (!NT_SUCCESS(ret)) {
    177      NOTREACHED_NT();
    178      return false;
    179    }
    180 
    181    DCHECK_NT(!g_originals[function->id] ||
    182              g_originals[function->id] == &thunks->thunks[i]);
    183    g_originals[function->id] = &thunks->thunks[i];
    184 
    185    thunks->num_thunks++;
    186    thunks->used_bytes += sizeof(ThunkData);
    187 
    188    function = reinterpret_cast<const FunctionInfo*>(
    189        reinterpret_cast<const char*>(function) + function->record_bytes);
    190  }
    191 
    192  return true;
    193 }
    194 
    195 // This method is called from within the loader lock
    196 ResolverThunk* InterceptionAgent::GetResolver(InterceptionType type) {
    197  static EatResolverThunk* eat_resolver = nullptr;
    198 
    199  if (!eat_resolver)
    200    eat_resolver = new (NT_ALLOC) EatResolverThunk;
    201  if (type == INTERCEPTION_EAT)
    202    return eat_resolver;
    203  NOTREACHED_NT();
    204  return nullptr;
    205 }
    206 
    207 }  // namespace sandbox