summaryrefslogtreecommitdiffstats
path: root/security/sandbox/chromium/sandbox/win/src/interception_agent.cc
diff options
context:
space:
mode:
Diffstat (limited to 'security/sandbox/chromium/sandbox/win/src/interception_agent.cc')
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interception_agent.cc234
1 files changed, 234 insertions, 0 deletions
diff --git a/security/sandbox/chromium/sandbox/win/src/interception_agent.cc b/security/sandbox/chromium/sandbox/win/src/interception_agent.cc
new file mode 100644
index 0000000000..e095328a71
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interception_agent.cc
@@ -0,0 +1,234 @@
+// Copyright (c) 2006-2010 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.
+
+// For information about interceptions as a whole see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#include "sandbox/win/src/interception_agent.h"
+
+#include <windows.h>
+
+#include <stddef.h>
+
+#include "sandbox/win/src/eat_resolver.h"
+#include "sandbox/win/src/interception_internal.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sidestep_resolver.h"
+
+namespace {
+
+// Returns true if target lies between base and base + range.
+bool IsWithinRange(const void* base, size_t range, const void* target) {
+ const char* end = reinterpret_cast<const char*>(base) + range;
+ return reinterpret_cast<const char*>(target) < end;
+}
+
+} // namespace
+
+namespace sandbox {
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// The list of intercepted functions back-pointers.
+SANDBOX_INTERCEPT OriginalFunctions g_originals;
+
+// Memory buffer mapped from the parent, with the list of interceptions.
+SANDBOX_INTERCEPT SharedMemory* g_interceptions = nullptr;
+
+InterceptionAgent* InterceptionAgent::GetInterceptionAgent() {
+ static InterceptionAgent* s_singleton = nullptr;
+ if (!s_singleton) {
+ if (!g_interceptions)
+ return nullptr;
+
+ size_t array_bytes = g_interceptions->num_intercepted_dlls * sizeof(void*);
+ s_singleton = reinterpret_cast<InterceptionAgent*>(
+ new (NT_ALLOC) char[array_bytes + sizeof(InterceptionAgent)]);
+
+ bool success = s_singleton->Init(g_interceptions);
+ if (!success) {
+ operator delete(s_singleton, NT_ALLOC);
+ s_singleton = nullptr;
+ }
+ }
+ return s_singleton;
+}
+
+bool InterceptionAgent::Init(SharedMemory* shared_memory) {
+ interceptions_ = shared_memory;
+ for (int i = 0; i < shared_memory->num_intercepted_dlls; i++)
+ dlls_[i] = nullptr;
+ return true;
+}
+
+bool InterceptionAgent::DllMatch(const UNICODE_STRING* full_path,
+ const UNICODE_STRING* name,
+ const DllPatchInfo* dll_info) {
+ UNICODE_STRING current_name;
+ current_name.Length =
+ static_cast<USHORT>(g_nt.wcslen(dll_info->dll_name) * sizeof(wchar_t));
+ current_name.MaximumLength = current_name.Length;
+ current_name.Buffer = const_cast<wchar_t*>(dll_info->dll_name);
+
+ BOOLEAN case_insensitive = TRUE;
+ if (full_path &&
+ !g_nt.RtlCompareUnicodeString(&current_name, full_path, case_insensitive))
+ return true;
+
+ if (name &&
+ !g_nt.RtlCompareUnicodeString(&current_name, name, case_insensitive))
+ return true;
+
+ return false;
+}
+
+bool InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path,
+ const UNICODE_STRING* name,
+ void* base_address) {
+ DllPatchInfo* dll_info = interceptions_->dll_list;
+ int i = 0;
+ for (; i < interceptions_->num_intercepted_dlls; i++) {
+ if (DllMatch(full_path, name, dll_info))
+ break;
+
+ dll_info = reinterpret_cast<DllPatchInfo*>(
+ reinterpret_cast<char*>(dll_info) + dll_info->record_bytes);
+ }
+
+ // Return now if the dll is not in our list of interest.
+ if (i == interceptions_->num_intercepted_dlls)
+ return true;
+
+ // The dll must be unloaded.
+ if (dll_info->unload_module)
+ return false;
+
+ // Purify causes this condition to trigger.
+ if (dlls_[i])
+ return true;
+
+ size_t buffer_bytes = offsetof(DllInterceptionData, thunks) +
+ dll_info->num_functions * sizeof(ThunkData);
+ dlls_[i] = reinterpret_cast<DllInterceptionData*>(
+ new (NT_PAGE, base_address) char[buffer_bytes]);
+
+ DCHECK_NT(dlls_[i]);
+ if (!dlls_[i])
+ return true;
+
+ dlls_[i]->data_bytes = buffer_bytes;
+ dlls_[i]->num_thunks = 0;
+ dlls_[i]->base = base_address;
+ dlls_[i]->used_bytes = offsetof(DllInterceptionData, thunks);
+
+ VERIFY(PatchDll(dll_info, dlls_[i]));
+
+ ULONG old_protect;
+ SIZE_T real_size = buffer_bytes;
+ void* to_protect = dlls_[i];
+ VERIFY_SUCCESS(g_nt.ProtectVirtualMemory(NtCurrentProcess, &to_protect,
+ &real_size, PAGE_EXECUTE_READ,
+ &old_protect));
+ return true;
+}
+
+void InterceptionAgent::OnDllUnload(void* base_address) {
+ for (int i = 0; i < interceptions_->num_intercepted_dlls; i++) {
+ if (dlls_[i] && dlls_[i]->base == base_address) {
+ operator delete(dlls_[i], NT_PAGE);
+ dlls_[i] = nullptr;
+ break;
+ }
+ }
+}
+
+// TODO(rvargas): We have to deal with prebinded dlls. I see two options: change
+// the timestamp of the patched dll, or modify the info on the prebinded dll.
+// the first approach messes matching of debug symbols, the second one is more
+// complicated.
+bool InterceptionAgent::PatchDll(const DllPatchInfo* dll_info,
+ DllInterceptionData* thunks) {
+ DCHECK_NT(thunks);
+ DCHECK_NT(dll_info);
+
+ const FunctionInfo* function = reinterpret_cast<const FunctionInfo*>(
+ reinterpret_cast<const char*>(dll_info) + dll_info->offset_to_functions);
+
+ for (int i = 0; i < dll_info->num_functions; i++) {
+ if (!IsWithinRange(dll_info, dll_info->record_bytes, function->function)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ ResolverThunk* resolver = GetResolver(function->type);
+ if (!resolver)
+ return false;
+
+ const char* interceptor =
+ function->function + g_nt.strlen(function->function) + 1;
+
+ if (!IsWithinRange(function, function->record_bytes, interceptor) ||
+ !IsWithinRange(dll_info, dll_info->record_bytes, interceptor)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ NTSTATUS ret = resolver->Setup(
+ thunks->base, interceptions_->interceptor_base, function->function,
+ interceptor, function->interceptor_address, &thunks->thunks[i],
+ sizeof(ThunkData), nullptr);
+ if (!NT_SUCCESS(ret)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ DCHECK_NT(!g_originals[function->id] ||
+ g_originals[function->id] == &thunks->thunks[i]);
+ g_originals[function->id] = &thunks->thunks[i];
+
+ thunks->num_thunks++;
+ thunks->used_bytes += sizeof(ThunkData);
+
+ function = reinterpret_cast<const FunctionInfo*>(
+ reinterpret_cast<const char*>(function) + function->record_bytes);
+ }
+
+ return true;
+}
+
+// This method is called from within the loader lock
+ResolverThunk* InterceptionAgent::GetResolver(InterceptionType type) {
+ static EatResolverThunk* eat_resolver = nullptr;
+ static SidestepResolverThunk* sidestep_resolver = nullptr;
+ static SmartSidestepResolverThunk* smart_sidestep_resolver = nullptr;
+
+ if (!eat_resolver)
+ eat_resolver = new (NT_ALLOC) EatResolverThunk;
+
+#if !defined(_WIN64)
+ // Sidestep is not supported for x64.
+ if (!sidestep_resolver)
+ sidestep_resolver = new (NT_ALLOC) SidestepResolverThunk;
+
+ if (!smart_sidestep_resolver)
+ smart_sidestep_resolver = new (NT_ALLOC) SmartSidestepResolverThunk;
+#endif
+
+ switch (type) {
+ case INTERCEPTION_EAT:
+ return eat_resolver;
+ case INTERCEPTION_SIDESTEP:
+ return sidestep_resolver;
+ case INTERCEPTION_SMART_SIDESTEP:
+ return smart_sidestep_resolver;
+ default:
+ NOTREACHED_NT();
+ }
+
+ return nullptr;
+}
+
+} // namespace sandbox