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