Index: sandbox/win/src/interception.cc |
diff --git a/sandbox/win/src/interception.cc b/sandbox/win/src/interception.cc |
deleted file mode 100644 |
index 0243c245f5934eba86363206b3abe601abb2dcd0..0000000000000000000000000000000000000000 |
--- a/sandbox/win/src/interception.cc |
+++ /dev/null |
@@ -1,553 +0,0 @@ |
-// Copyright (c) 2012 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 <stddef.h> |
- |
-#include <set> |
- |
-#include "sandbox/win/src/interception.h" |
- |
-#include "base/logging.h" |
-#include "base/memory/scoped_ptr.h" |
-#include "base/strings/string16.h" |
-#include "base/win/pe_image.h" |
-#include "base/win/windows_version.h" |
-#include "sandbox/win/src/interception_internal.h" |
-#include "sandbox/win/src/interceptors.h" |
-#include "sandbox/win/src/sandbox.h" |
-#include "sandbox/win/src/sandbox_rand.h" |
-#include "sandbox/win/src/service_resolver.h" |
-#include "sandbox/win/src/target_interceptions.h" |
-#include "sandbox/win/src/target_process.h" |
- |
-namespace sandbox { |
- |
-namespace { |
- |
-// Standard allocation granularity and page size for Windows. |
-const size_t kAllocGranularity = 65536; |
-const size_t kPageSize = 4096; |
- |
-} // namespace |
- |
-namespace internal { |
- |
-// Find a random offset within 64k and aligned to ceil(log2(size)). |
-size_t GetGranularAlignedRandomOffset(size_t size) { |
- CHECK_LE(size, kAllocGranularity); |
- unsigned int offset; |
- |
- do { |
- GetRandom(&offset); |
- offset &= (kAllocGranularity - 1); |
- } while (offset > (kAllocGranularity - size)); |
- |
- // Find an alignment between 64 and the page size (4096). |
- size_t align_size = kPageSize; |
- for (size_t new_size = align_size / 2; new_size >= size; new_size /= 2) { |
- align_size = new_size; |
- } |
- return offset & ~(align_size - 1); |
-} |
- |
-} // namespace internal |
- |
-SANDBOX_INTERCEPT SharedMemory* g_interceptions; |
- |
-// Table of the unpatched functions that we intercept. Mapped from the parent. |
-SANDBOX_INTERCEPT OriginalFunctions g_originals = { NULL }; |
- |
-// Magic constant that identifies that this function is not to be patched. |
-const char kUnloadDLLDummyFunction[] = "@"; |
- |
-InterceptionManager::InterceptionData::InterceptionData() { |
-} |
- |
-InterceptionManager::InterceptionData::~InterceptionData() { |
-} |
- |
-InterceptionManager::InterceptionManager(TargetProcess* child_process, |
- bool relaxed) |
- : child_(child_process), names_used_(false), relaxed_(relaxed) { |
- child_->AddRef(); |
-} |
-InterceptionManager::~InterceptionManager() { |
- child_->Release(); |
-} |
- |
-bool InterceptionManager::AddToPatchedFunctions( |
- const wchar_t* dll_name, const char* function_name, |
- InterceptionType interception_type, const void* replacement_code_address, |
- InterceptorId id) { |
- InterceptionData function; |
- function.type = interception_type; |
- function.id = id; |
- function.dll = dll_name; |
- function.function = function_name; |
- function.interceptor_address = replacement_code_address; |
- |
- interceptions_.push_back(function); |
- return true; |
-} |
- |
-bool InterceptionManager::AddToPatchedFunctions( |
- const wchar_t* dll_name, const char* function_name, |
- InterceptionType interception_type, const char* replacement_function_name, |
- InterceptorId id) { |
- InterceptionData function; |
- function.type = interception_type; |
- function.id = id; |
- function.dll = dll_name; |
- function.function = function_name; |
- function.interceptor = replacement_function_name; |
- function.interceptor_address = NULL; |
- |
- interceptions_.push_back(function); |
- names_used_ = true; |
- return true; |
-} |
- |
-bool InterceptionManager::AddToUnloadModules(const wchar_t* dll_name) { |
- InterceptionData module_to_unload; |
- module_to_unload.type = INTERCEPTION_UNLOAD_MODULE; |
- module_to_unload.dll = dll_name; |
- // The next two are dummy values that make the structures regular, instead |
- // of having special cases. They should not be used. |
- module_to_unload.function = kUnloadDLLDummyFunction; |
- module_to_unload.interceptor_address = reinterpret_cast<void*>(1); |
- |
- interceptions_.push_back(module_to_unload); |
- return true; |
-} |
- |
-bool InterceptionManager::InitializeInterceptions() { |
- if (interceptions_.empty()) |
- return true; // Nothing to do here |
- |
- size_t buffer_bytes = GetBufferSize(); |
- scoped_ptr<char[]> local_buffer(new char[buffer_bytes]); |
- |
- if (!SetupConfigBuffer(local_buffer.get(), buffer_bytes)) |
- return false; |
- |
- void* remote_buffer; |
- if (!CopyDataToChild(local_buffer.get(), buffer_bytes, &remote_buffer)) |
- return false; |
- |
- bool hot_patch_needed = (0 != buffer_bytes); |
- if (!PatchNtdll(hot_patch_needed)) |
- return false; |
- |
- g_interceptions = reinterpret_cast<SharedMemory*>(remote_buffer); |
- ResultCode rc = child_->TransferVariable("g_interceptions", |
- &g_interceptions, |
- sizeof(g_interceptions)); |
- return (SBOX_ALL_OK == rc); |
-} |
- |
-size_t InterceptionManager::GetBufferSize() const { |
- std::set<base::string16> dlls; |
- size_t buffer_bytes = 0; |
- |
- std::list<InterceptionData>::const_iterator it = interceptions_.begin(); |
- for (; it != interceptions_.end(); ++it) { |
- // skip interceptions that are performed from the parent |
- if (!IsInterceptionPerformedByChild(*it)) |
- continue; |
- |
- if (!dlls.count(it->dll)) { |
- // NULL terminate the dll name on the structure |
- size_t dll_name_bytes = (it->dll.size() + 1) * sizeof(wchar_t); |
- |
- // include the dll related size |
- buffer_bytes += RoundUpToMultiple(offsetof(DllPatchInfo, dll_name) + |
- dll_name_bytes, sizeof(size_t)); |
- dlls.insert(it->dll); |
- } |
- |
- // we have to NULL terminate the strings on the structure |
- size_t strings_chars = it->function.size() + it->interceptor.size() + 2; |
- |
- // a new FunctionInfo is required per function |
- size_t record_bytes = offsetof(FunctionInfo, function) + strings_chars; |
- record_bytes = RoundUpToMultiple(record_bytes, sizeof(size_t)); |
- buffer_bytes += record_bytes; |
- } |
- |
- if (0 != buffer_bytes) |
- // add the part of SharedMemory that we have not counted yet |
- buffer_bytes += offsetof(SharedMemory, dll_list); |
- |
- return buffer_bytes; |
-} |
- |
-// Basically, walk the list of interceptions moving them to the config buffer, |
-// but keeping together all interceptions that belong to the same dll. |
-// The config buffer is a local buffer, not the one allocated on the child. |
-bool InterceptionManager::SetupConfigBuffer(void* buffer, size_t buffer_bytes) { |
- if (0 == buffer_bytes) |
- return true; |
- |
- DCHECK(buffer_bytes > sizeof(SharedMemory)); |
- |
- SharedMemory* shared_memory = reinterpret_cast<SharedMemory*>(buffer); |
- DllPatchInfo* dll_info = shared_memory->dll_list; |
- int num_dlls = 0; |
- |
- shared_memory->interceptor_base = names_used_ ? child_->MainModule() : NULL; |
- |
- buffer_bytes -= offsetof(SharedMemory, dll_list); |
- buffer = dll_info; |
- |
- std::list<InterceptionData>::iterator it = interceptions_.begin(); |
- for (; it != interceptions_.end();) { |
- // skip interceptions that are performed from the parent |
- if (!IsInterceptionPerformedByChild(*it)) { |
- ++it; |
- continue; |
- } |
- |
- const base::string16 dll = it->dll; |
- if (!SetupDllInfo(*it, &buffer, &buffer_bytes)) |
- return false; |
- |
- // walk the interceptions from this point, saving the ones that are |
- // performed on this dll, and removing the entry from the list. |
- // advance the iterator before removing the element from the list |
- std::list<InterceptionData>::iterator rest = it; |
- for (; rest != interceptions_.end();) { |
- if (rest->dll == dll) { |
- if (!SetupInterceptionInfo(*rest, &buffer, &buffer_bytes, dll_info)) |
- return false; |
- if (it == rest) |
- ++it; |
- rest = interceptions_.erase(rest); |
- } else { |
- ++rest; |
- } |
- } |
- dll_info = reinterpret_cast<DllPatchInfo*>(buffer); |
- ++num_dlls; |
- } |
- |
- shared_memory->num_intercepted_dlls = num_dlls; |
- return true; |
-} |
- |
-// Fills up just the part that depends on the dll, not the info that depends on |
-// the actual interception. |
-bool InterceptionManager::SetupDllInfo(const InterceptionData& data, |
- void** buffer, |
- size_t* buffer_bytes) const { |
- DCHECK(buffer_bytes); |
- DCHECK(buffer); |
- DCHECK(*buffer); |
- |
- DllPatchInfo* dll_info = reinterpret_cast<DllPatchInfo*>(*buffer); |
- |
- // the strings have to be zero terminated |
- size_t required = offsetof(DllPatchInfo, dll_name) + |
- (data.dll.size() + 1) * sizeof(wchar_t); |
- required = RoundUpToMultiple(required, sizeof(size_t)); |
- if (*buffer_bytes < required) |
- return false; |
- |
- *buffer_bytes -= required; |
- *buffer = reinterpret_cast<char*>(*buffer) + required; |
- |
- // set up the dll info to be what we know about it at this time |
- dll_info->unload_module = (data.type == INTERCEPTION_UNLOAD_MODULE); |
- dll_info->record_bytes = required; |
- dll_info->offset_to_functions = required; |
- dll_info->num_functions = 0; |
- data.dll._Copy_s(dll_info->dll_name, data.dll.size(), data.dll.size()); |
- dll_info->dll_name[data.dll.size()] = L'\0'; |
- |
- return true; |
-} |
- |
-bool InterceptionManager::SetupInterceptionInfo(const InterceptionData& data, |
- void** buffer, |
- size_t* buffer_bytes, |
- DllPatchInfo* dll_info) const { |
- DCHECK(buffer_bytes); |
- DCHECK(buffer); |
- DCHECK(*buffer); |
- |
- if ((dll_info->unload_module) && |
- (data.function != kUnloadDLLDummyFunction)) { |
- // Can't specify a dll for both patch and unload. |
- NOTREACHED(); |
- } |
- |
- FunctionInfo* function = reinterpret_cast<FunctionInfo*>(*buffer); |
- |
- size_t name_bytes = data.function.size(); |
- size_t interceptor_bytes = data.interceptor.size(); |
- |
- // the strings at the end of the structure are zero terminated |
- size_t required = offsetof(FunctionInfo, function) + |
- name_bytes + interceptor_bytes + 2; |
- required = RoundUpToMultiple(required, sizeof(size_t)); |
- if (*buffer_bytes < required) |
- return false; |
- |
- // update the caller's values |
- *buffer_bytes -= required; |
- *buffer = reinterpret_cast<char*>(*buffer) + required; |
- |
- function->record_bytes = required; |
- function->type = data.type; |
- function->id = data.id; |
- function->interceptor_address = data.interceptor_address; |
- char* names = function->function; |
- |
- data.function._Copy_s(names, name_bytes, name_bytes); |
- names += name_bytes; |
- *names++ = '\0'; |
- |
- // interceptor follows the function_name |
- data.interceptor._Copy_s(names, interceptor_bytes, interceptor_bytes); |
- names += interceptor_bytes; |
- *names++ = '\0'; |
- |
- // update the dll table |
- dll_info->num_functions++; |
- dll_info->record_bytes += required; |
- |
- return true; |
-} |
- |
-bool InterceptionManager::CopyDataToChild(const void* local_buffer, |
- size_t buffer_bytes, |
- void** remote_buffer) const { |
- DCHECK(NULL != remote_buffer); |
- if (0 == buffer_bytes) { |
- *remote_buffer = NULL; |
- return true; |
- } |
- |
- HANDLE child = child_->Process(); |
- |
- // Allocate memory on the target process without specifying the address |
- void* remote_data = ::VirtualAllocEx(child, NULL, buffer_bytes, |
- MEM_COMMIT, PAGE_READWRITE); |
- if (NULL == remote_data) |
- return false; |
- |
- SIZE_T bytes_written; |
- BOOL success = ::WriteProcessMemory(child, remote_data, local_buffer, |
- buffer_bytes, &bytes_written); |
- if (FALSE == success || bytes_written != buffer_bytes) { |
- ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE); |
- return false; |
- } |
- |
- *remote_buffer = remote_data; |
- |
- return true; |
-} |
- |
-// Only return true if the child should be able to perform this interception. |
-bool InterceptionManager::IsInterceptionPerformedByChild( |
- const InterceptionData& data) const { |
- if (INTERCEPTION_INVALID == data.type) |
- return false; |
- |
- if (INTERCEPTION_SERVICE_CALL == data.type) |
- return false; |
- |
- if (data.type >= INTERCEPTION_LAST) |
- return false; |
- |
- base::string16 ntdll(kNtdllName); |
- if (ntdll == data.dll) |
- return false; // ntdll has to be intercepted from the parent |
- |
- return true; |
-} |
- |
-bool InterceptionManager::PatchNtdll(bool hot_patch_needed) { |
- // Maybe there is nothing to do |
- if (!hot_patch_needed && interceptions_.empty()) |
- return true; |
- |
- if (hot_patch_needed) { |
-#if SANDBOX_EXPORTS |
- // Make sure the functions are not excluded by the linker. |
-#if defined(_WIN64) |
- #pragma comment(linker, "/include:TargetNtMapViewOfSection64") |
- #pragma comment(linker, "/include:TargetNtUnmapViewOfSection64") |
-#else |
- #pragma comment(linker, "/include:_TargetNtMapViewOfSection@44") |
- #pragma comment(linker, "/include:_TargetNtUnmapViewOfSection@12") |
-#endif |
-#endif |
- ADD_NT_INTERCEPTION(NtMapViewOfSection, MAP_VIEW_OF_SECTION_ID, 44); |
- ADD_NT_INTERCEPTION(NtUnmapViewOfSection, UNMAP_VIEW_OF_SECTION_ID, 12); |
- } |
- |
- // Reserve a full 64k memory range in the child process. |
- HANDLE child = child_->Process(); |
- BYTE* thunk_base = reinterpret_cast<BYTE*>( |
- ::VirtualAllocEx(child, NULL, kAllocGranularity, |
- MEM_RESERVE, PAGE_NOACCESS)); |
- |
- // Find an aligned, random location within the reserved range. |
- size_t thunk_bytes = interceptions_.size() * sizeof(ThunkData) + |
- sizeof(DllInterceptionData); |
- size_t thunk_offset = internal::GetGranularAlignedRandomOffset(thunk_bytes); |
- |
- // Split the base and offset along page boundaries. |
- thunk_base += thunk_offset & ~(kPageSize - 1); |
- thunk_offset &= kPageSize - 1; |
- |
- // Make an aligned, padded allocation, and move the pointer to our chunk. |
- size_t thunk_bytes_padded = (thunk_bytes + kPageSize - 1) & ~(kPageSize - 1); |
- thunk_base = reinterpret_cast<BYTE*>( |
- ::VirtualAllocEx(child, thunk_base, thunk_bytes_padded, |
- MEM_COMMIT, PAGE_EXECUTE_READWRITE)); |
- CHECK(thunk_base); // If this fails we'd crash anyway on an invalid access. |
- DllInterceptionData* thunks = reinterpret_cast<DllInterceptionData*>( |
- thunk_base + thunk_offset); |
- |
- DllInterceptionData dll_data; |
- dll_data.data_bytes = thunk_bytes; |
- dll_data.num_thunks = 0; |
- dll_data.used_bytes = offsetof(DllInterceptionData, thunks); |
- |
- // Reset all helpers for a new child. |
- memset(g_originals, 0, sizeof(g_originals)); |
- |
- // this should write all the individual thunks to the child's memory |
- if (!PatchClientFunctions(thunks, thunk_bytes, &dll_data)) |
- return false; |
- |
- // and now write the first part of the table to the child's memory |
- SIZE_T written; |
- bool ok = FALSE != ::WriteProcessMemory(child, thunks, &dll_data, |
- offsetof(DllInterceptionData, thunks), |
- &written); |
- |
- if (!ok || (offsetof(DllInterceptionData, thunks) != written)) |
- return false; |
- |
- // Attempt to protect all the thunks, but ignore failure |
- DWORD old_protection; |
- ::VirtualProtectEx(child, thunks, thunk_bytes, |
- PAGE_EXECUTE_READ, &old_protection); |
- |
- ResultCode ret = child_->TransferVariable("g_originals", g_originals, |
- sizeof(g_originals)); |
- return (SBOX_ALL_OK == ret); |
-} |
- |
-bool InterceptionManager::PatchClientFunctions(DllInterceptionData* thunks, |
- size_t thunk_bytes, |
- DllInterceptionData* dll_data) { |
- DCHECK(NULL != thunks); |
- DCHECK(NULL != dll_data); |
- |
- HMODULE ntdll_base = ::GetModuleHandle(kNtdllName); |
- if (!ntdll_base) |
- return false; |
- |
- base::win::PEImage ntdll_image(ntdll_base); |
- |
- // Bypass purify's interception. |
- wchar_t* loader_get = reinterpret_cast<wchar_t*>( |
- ntdll_image.GetProcAddress("LdrGetDllHandle")); |
- if (loader_get) { |
- if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | |
- GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, |
- loader_get, &ntdll_base)) |
- return false; |
- } |
- |
- char* interceptor_base = NULL; |
- |
-#if SANDBOX_EXPORTS |
- interceptor_base = reinterpret_cast<char*>(child_->MainModule()); |
- HMODULE local_interceptor = ::LoadLibrary(child_->Name()); |
-#endif |
- |
- ServiceResolverThunk* thunk; |
-#if defined(_WIN64) |
- thunk = new ServiceResolverThunk(child_->Process(), relaxed_); |
-#else |
- base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); |
- if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) { |
- if (os_info->version() >= base::win::VERSION_WIN10) |
- thunk = new Wow64W10ResolverThunk(child_->Process(), relaxed_); |
- else if (os_info->version() >= base::win::VERSION_WIN8) |
- thunk = new Wow64W8ResolverThunk(child_->Process(), relaxed_); |
- else |
- thunk = new Wow64ResolverThunk(child_->Process(), relaxed_); |
- } else if (os_info->version() >= base::win::VERSION_WIN8) { |
- thunk = new Win8ResolverThunk(child_->Process(), relaxed_); |
- } else { |
- thunk = new ServiceResolverThunk(child_->Process(), relaxed_); |
- } |
-#endif |
- |
- std::list<InterceptionData>::iterator it = interceptions_.begin(); |
- for (; it != interceptions_.end(); ++it) { |
- const base::string16 ntdll(kNtdllName); |
- if (it->dll != ntdll) |
- break; |
- |
- if (INTERCEPTION_SERVICE_CALL != it->type) |
- break; |
- |
-#if SANDBOX_EXPORTS |
- // We may be trying to patch by function name. |
- if (NULL == it->interceptor_address) { |
- const char* address; |
- NTSTATUS ret = thunk->ResolveInterceptor(local_interceptor, |
- it->interceptor.c_str(), |
- reinterpret_cast<const void**>( |
- &address)); |
- if (!NT_SUCCESS(ret)) |
- break; |
- |
- // Translate the local address to an address on the child. |
- it->interceptor_address = interceptor_base + (address - |
- reinterpret_cast<char*>(local_interceptor)); |
- } |
-#endif |
- NTSTATUS ret = thunk->Setup(ntdll_base, |
- interceptor_base, |
- it->function.c_str(), |
- it->interceptor.c_str(), |
- it->interceptor_address, |
- &thunks->thunks[dll_data->num_thunks], |
- thunk_bytes - dll_data->used_bytes, |
- NULL); |
- if (!NT_SUCCESS(ret)) |
- break; |
- |
- DCHECK(!g_originals[it->id]); |
- g_originals[it->id] = &thunks->thunks[dll_data->num_thunks]; |
- |
- dll_data->num_thunks++; |
- dll_data->used_bytes += sizeof(ThunkData); |
- } |
- |
- delete(thunk); |
- |
-#if SANDBOX_EXPORTS |
- if (NULL != local_interceptor) |
- ::FreeLibrary(local_interceptor); |
-#endif |
- |
- if (it != interceptions_.end()) |
- return false; |
- |
- return true; |
-} |
- |
-} // namespace sandbox |