diff options
Diffstat (limited to 'sandbox/src/sandbox_nt_util.cc')
-rw-r--r-- | sandbox/src/sandbox_nt_util.cc | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/sandbox/src/sandbox_nt_util.cc b/sandbox/src/sandbox_nt_util.cc new file mode 100644 index 0000000..7f55f1c --- /dev/null +++ b/sandbox/src/sandbox_nt_util.cc @@ -0,0 +1,475 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "sandbox/src/sandbox_nt_util.h" + +#include "sandbox/src/pe_image.h" +#include "sandbox/src/sandbox_factory.h" +#include "sandbox/src/target_services.h" + +namespace sandbox { + +// Handle for our private heap. +void* g_heap = NULL; + +// This is the list of all imported symbols from ntdll.dll. +SANDBOX_INTERCEPT NtExports g_nt = { NULL }; + +SANDBOX_INTERCEPT HANDLE g_shared_section; +SANDBOX_INTERCEPT size_t g_shared_IPC_size = 0; +SANDBOX_INTERCEPT size_t g_shared_policy_size = 0; + +void* volatile g_shared_policy_memory = NULL; +void* volatile g_shared_IPC_memory = NULL; + +// Both the IPC and the policy share a single region of memory in which the IPC +// memory is first and the policy memory is last. +bool MapGlobalMemory() { + if (NULL == g_shared_IPC_memory) { + void* memory = NULL; + SIZE_T size = 0; + // Map the entire shared section from the start. + NTSTATUS ret = g_nt.MapViewOfSection(g_shared_section, NtCurrentProcess, + &memory, 0, 0, NULL, &size, ViewUnmap, + 0, PAGE_READWRITE); + + if (!NT_SUCCESS(ret) || NULL == memory) { + NOTREACHED_NT(); + return false; + } + + if (NULL != _InterlockedCompareExchangePointer(&g_shared_IPC_memory, + memory, NULL)) { + // Somebody beat us to the memory setup. + ret = g_nt.UnmapViewOfSection(NtCurrentProcess, memory); + VERIFY_SUCCESS(ret); + } + DCHECK_NT(g_shared_IPC_size > 0); + g_shared_policy_memory = reinterpret_cast<char*>(g_shared_IPC_memory) + + g_shared_IPC_size; + } + DCHECK_NT(g_shared_policy_memory); + DCHECK_NT(g_shared_policy_size > 0); + return true; +} + +void* GetGlobalIPCMemory() { + if (!MapGlobalMemory()) + return NULL; + return g_shared_IPC_memory; +} + +void* GetGlobalPolicyMemory() { + if (!MapGlobalMemory()) + return NULL; + return g_shared_policy_memory; +} + +bool InitHeap() { + if (!g_heap) { + // Create a new heap using default values for everything. + void* heap = g_nt.RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL); + if (!heap) + return false; + + if (NULL != _InterlockedCompareExchangePointer(&g_heap, heap, NULL)) { + // Somebody beat us to the memory setup. + g_nt.RtlDestroyHeap(heap); + } + } + return (g_heap) ? true : false; +} + +// Physically reads or writes from memory to verify that (at this time), it is +// valid. Returns a dummy value. +int TouchMemory(void* buffer, size_t size_bytes, RequiredAccess intent) { + const int kPageSize = 4096; + int dummy = 0; + char* start = reinterpret_cast<char*>(buffer); + char* end = start + size_bytes - 1; + + if (WRITE == intent) { + for (; start < end; start += kPageSize) { + *start = 0; + } + *end = 0; + } else { + for (; start < end; start += kPageSize) { + dummy += *start; + } + dummy += *end; + } + + return dummy; +} + +bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) { + DCHECK_NT(size); + __try { + TouchMemory(buffer, size, intent); + } __except(EXCEPTION_EXECUTE_HANDLER) { + return false; + } + return true; +} + +NTSTATUS CopyData(void* destination, const void* source, size_t bytes) { + NTSTATUS ret = STATUS_SUCCESS; + __try { + if (SandboxFactory::GetTargetServices()->GetState()->InitCalled()) { + memcpy(destination, source, bytes); + } else { + const char* from = reinterpret_cast<const char*>(source); + char* to = reinterpret_cast<char*>(destination); + for (size_t i = 0; i < bytes; i++) { + to[i] = from[i]; + } + } + } __except(EXCEPTION_EXECUTE_HANDLER) { + ret = GetExceptionCode(); + } + return ret; +} + +// Hacky code... replace with AllocAndCopyObjectAttributes. +NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object, + wchar_t** out_name, uint32* attributes, + HANDLE* root) { + if (!InitHeap()) + return STATUS_NO_MEMORY; + + DCHECK_NT(out_name); + *out_name = NULL; + NTSTATUS ret = STATUS_UNSUCCESSFUL; + __try { + do { + if (in_object->RootDirectory != static_cast<HANDLE>(0) && !root) + break; + if (NULL == in_object->ObjectName) + break; + if (NULL == in_object->ObjectName->Buffer) + break; + + size_t size = in_object->ObjectName->Length + sizeof(wchar_t); + *out_name = new(NT_ALLOC) wchar_t[size/sizeof(wchar_t)]; + if (NULL == *out_name) + break; + + ret = CopyData(*out_name, in_object->ObjectName->Buffer, + size - sizeof(wchar_t)); + if (!NT_SUCCESS(ret)) + break; + + (*out_name)[size / sizeof(wchar_t) - 1] = L'\0'; + + if (attributes) + *attributes = in_object->Attributes; + + if (root) + *root = in_object->RootDirectory; + ret = STATUS_SUCCESS; + } while (false); + } __except(EXCEPTION_EXECUTE_HANDLER) { + ret = GetExceptionCode(); + } + + if (!NT_SUCCESS(ret) && *out_name) { + operator delete(*out_name, NT_ALLOC); + *out_name = NULL; + } + + return ret; +} + +NTSTATUS GetProcessId(HANDLE process, ULONG *process_id) { + PROCESS_BASIC_INFORMATION proc_info; + ULONG bytes_returned; + + NTSTATUS ret = g_nt.QueryInformationProcess(process, ProcessBasicInformation, + &proc_info, sizeof(proc_info), + &bytes_returned); + if (!NT_SUCCESS(ret) || sizeof(proc_info) != bytes_returned) + return ret; + + *process_id = proc_info.UniqueProcessId; + return STATUS_SUCCESS; +} + +bool IsSameProcess(HANDLE process) { + if (NtCurrentProcess == process) + return true; + + static ULONG s_process_id = 0; + + if (!s_process_id) { + NTSTATUS ret = GetProcessId(NtCurrentProcess, &s_process_id); + if (!NT_SUCCESS(ret)) + return false; + } + + ULONG process_id; + NTSTATUS ret = GetProcessId(process, &process_id); + if (!NT_SUCCESS(ret)) + return false; + + return (process_id == s_process_id); +} + +bool IsValidImageSection(HANDLE section, PVOID *base, PLARGE_INTEGER offset, + PULONG view_size) { + if (!section || !base || !view_size || offset) + return false; + + HANDLE query_section; + + NTSTATUS ret = g_nt.DuplicateObject(NtCurrentProcess, section, + NtCurrentProcess, &query_section, + SECTION_QUERY, 0, 0); + if (!NT_SUCCESS(ret)) + return false; + + SECTION_BASIC_INFORMATION basic_info; + ULONG bytes_returned; + ret = g_nt.QuerySection(query_section, SectionBasicInformation, &basic_info, + sizeof(basic_info), &bytes_returned); + + VERIFY_SUCCESS(g_nt.Close(query_section)); + + if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned) + return false; + + if (!(basic_info.Attributes & SEC_IMAGE)) + return false; + + return true; +} + +UNICODE_STRING* AnsiToUnicode(const char* string) { + ANSI_STRING ansi_string; + ansi_string.Length = static_cast<USHORT>(g_nt.strlen(string)); + ansi_string.MaximumLength = ansi_string.Length + 1; + ansi_string.Buffer = const_cast<char*>(string); + + if (ansi_string.Length > ansi_string.MaximumLength) + return NULL; + + size_t name_bytes = ansi_string.MaximumLength * sizeof(wchar_t) + + sizeof(UNICODE_STRING); + + UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>( + new(NT_ALLOC) char[name_bytes]); + if (!out_string) + return NULL; + + out_string->MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t); + out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]); + + BOOLEAN alloc_destination = FALSE; + NTSTATUS ret = g_nt.RtlAnsiStringToUnicodeString(out_string, &ansi_string, + alloc_destination); + DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret); + if (!NT_SUCCESS(ret)) { + operator delete(out_string, NT_ALLOC); + return NULL; + } + + return out_string; +} + +UNICODE_STRING* GetImageNameFromModule(HMODULE module) { + UNICODE_STRING* out_name = NULL; + __try { + do { + PEImage pe(module); + + if (!pe.VerifyMagic()) + break; + + PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory(); + if (!exports) + break; + + char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name)); + + out_name = AnsiToUnicode(name); + } while (false); + } __except(EXCEPTION_EXECUTE_HANDLER) { + } + + return out_name; +} + +UNICODE_STRING* GetBackingFilePath(PVOID address) { + // We'll start with something close to max_path charactes for the name. + ULONG buffer_bytes = MAX_PATH * 2; + + for (;;) { + MEMORY_SECTION_NAME* section_name = reinterpret_cast<MEMORY_SECTION_NAME*>( + new(NT_ALLOC) char[buffer_bytes]); + + if (!section_name) + return NULL; + + ULONG returned_bytes; + NTSTATUS ret = g_nt.QueryVirtualMemory(NtCurrentProcess, address, + MemorySectionName, section_name, + buffer_bytes, &returned_bytes); + + if (STATUS_BUFFER_OVERFLOW == ret) { + // Retry the call with the given buffer size. + operator delete(section_name, NT_ALLOC); + section_name = NULL; + buffer_bytes = returned_bytes; + continue; + } + if (!NT_SUCCESS(ret)) { + operator delete(section_name, NT_ALLOC); + return NULL; + } + + return reinterpret_cast<UNICODE_STRING*>(section_name); + } +} + +NTSTATUS AutoProtectMemory::ChangeProtection(void* address, size_t bytes, + ULONG protect) { + DCHECK_NT(!changed_); + SIZE_T new_bytes = bytes; + NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address, + &new_bytes, protect, &old_protect_); + if (NT_SUCCESS(ret)) { + changed_ = true; + address_ = address; + bytes_ = bytes; + } + + return ret; +} + +NTSTATUS AutoProtectMemory::RevertProtection() { + if (!changed_) + return STATUS_SUCCESS; + + DCHECK_NT(address_); + DCHECK_NT(bytes_); + + SIZE_T new_bytes = bytes_; + NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address_, + &new_bytes, old_protect_, + &old_protect_); + DCHECK_NT(NT_SUCCESS(ret)); + + changed_ = false; + address_ = NULL; + bytes_ = 0; + old_protect_ = 0; + + return ret; +} + +bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length, + uint32 file_info_class) { + if (FileRenameInformation != file_info_class) + return false; + + if (length < sizeof(FILE_RENAME_INFORMATION)) + return false; + + // We don't support a root directory. + if (file_info->RootDirectory) + return false; + + // Check if it starts with \\??\\. We don't support relative paths. + if (file_info->FileNameLength < 4 || file_info->FileNameLength > kuint16max) + return false; + + if (file_info->FileName[0] != L'\\' || + file_info->FileName[1] != L'?' || + file_info->FileName[2] != L'?' || + file_info->FileName[3] != L'\\') + return false; + + return true; +} + +} // namespace sandbox + +void* operator new(size_t size, sandbox::AllocationType type) { + using namespace sandbox; + + if (NT_ALLOC == type) { + if (!InitHeap()) + return false; + + // Use default flags for the allocation. + return g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size); + } else if (NT_PAGE == type) { + void* base = 0; + SIZE_T actual_size = size; + ULONG_PTR zero_bits = 0; + NTSTATUS ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, + zero_bits, &actual_size, + MEM_COMMIT, PAGE_READWRITE); + if (!NT_SUCCESS(ret)) + return NULL; + return base; + } + NOTREACHED_NT(); + return NULL; +} + +void operator delete(void* memory, sandbox::AllocationType type) { + using namespace sandbox; + + if (NT_ALLOC == type) { + // Use default flags. + VERIFY(g_nt.RtlFreeHeap(sandbox::g_heap, 0, memory)); + } else if (NT_PAGE == type) { + void* base = memory; + SIZE_T size = 0; + VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, + MEM_RELEASE)); + } else { + NOTREACHED_NT(); + } +} + +void* __cdecl operator new(size_t size, void* buffer, + sandbox::AllocationType type) { + UNREFERENCED_PARAMETER(size); + UNREFERENCED_PARAMETER(type); + return buffer; +} + +void __cdecl operator delete(void* memory, void* buffer, + sandbox::AllocationType type) { + UNREFERENCED_PARAMETER(memory); + UNREFERENCED_PARAMETER(buffer); + UNREFERENCED_PARAMETER(type); +} |