diff options
author | rvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-29 00:24:26 +0000 |
---|---|---|
committer | rvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-29 00:24:26 +0000 |
commit | ddcd5e3b2cd813bc21befe70f8faf6eb65dd3829 (patch) | |
tree | ab310abd6ace427cf08c315ec549d4e64f429059 /sandbox | |
parent | aac2c87d5100f23b869435387d55938e70ae8d34 (diff) | |
download | chromium_src-ddcd5e3b2cd813bc21befe70f8faf6eb65dd3829.zip chromium_src-ddcd5e3b2cd813bc21befe70f8faf6eb65dd3829.tar.gz chromium_src-ddcd5e3b2cd813bc21befe70f8faf6eb65dd3829.tar.bz2 |
Sandbox: Add the base code for the 46-bit service resolver.
BUG=27218
TEST=none
Review URL: http://codereview.chromium.org/552223
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@37468 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r-- | sandbox/sandbox.gyp | 6 | ||||
-rw-r--r-- | sandbox/src/service_resolver_64.cc | 299 |
2 files changed, 304 insertions, 1 deletions
diff --git a/sandbox/sandbox.gyp b/sandbox/sandbox.gyp index 58f0059..85bd6b3 100644 --- a/sandbox/sandbox.gyp +++ b/sandbox/sandbox.gyp @@ -76,6 +76,7 @@ 'src/sandbox.cc', 'src/sandbox.h', 'src/security_level.h', + 'src/service_resolver.h', 'src/shared_handles.cc', 'src/shared_handles.h', 'src/sid.cc', @@ -207,7 +208,6 @@ 'src/registry_interception.cc', 'src/registry_interception.h', 'src/service_resolver.cc', - 'src/service_resolver.h', 'src/sharedmem_ipc_client.cc', 'src/sharedmem_ipc_client.h', 'src/sharedmem_ipc_server.cc', @@ -263,6 +263,10 @@ }, }, 'msvs_guid': 'BE3468E6-B314-4310-B449-6FC0C52EE155', + 'sources': [ + # Files that are used by the 64-bit version of Windows sandbox only. + 'src/service_resolver_64.cc', + ], 'include_dirs': [ '..', ], diff --git a/sandbox/src/service_resolver_64.cc b/sandbox/src/service_resolver_64.cc new file mode 100644 index 0000000..3a46d73 --- /dev/null +++ b/sandbox/src/service_resolver_64.cc @@ -0,0 +1,299 @@ +// 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. + +#include "sandbox/wow_helper/service64_resolver.h" + +#include "base/scoped_ptr.h" +#include "sandbox/wow_helper/target_code.h" + +namespace { +#pragma pack(push, 1) + +const BYTE kMovEax = 0xB8; +const BYTE kMovEdx = 0xBA; +const USHORT kCallPtrEdx = 0x12FF; +const BYTE kRet = 0xC2; +const BYTE kNop = 0x90; +const USHORT kJmpEdx = 0xE2FF; +const USHORT kXorEcx = 0xC933; +const ULONG kLeaEdx = 0x0424548D; +const ULONG kCallFs1 = 0xC015FF64; +const ULONG kCallFs2Ret = 0xC2000000; +const BYTE kPopEdx = 0x5A; +const BYTE kPushEdx = 0x52; +const BYTE kPush32 = 0x68; + +const ULONG kMmovR10EcxMovEax = 0xB8D18B4C; +const USHORT kSyscall = 0x050F; +const BYTE kRetNp = 0xC3; +const BYTE kPad = 0x66; +const USHORT kNop16 = 0x9066; +const BYTE kRelJmp = 0xE9; + +const ULONG kXorRaxMovEax = 0xB8C03148; +const ULONG kSaveRcx = 0x10488948; +const ULONG kMovRcxRaxJmp = 0xE9C88B48; + +// Service code for 64 bit systems. +struct ServiceEntry { + // this struct contains roughly the following code: + // mov r10,rcx + // mov eax,52h + // syscall + // ret + // xchg ax,ax + // xchg ax,ax + + ULONG mov_r10_ecx_mov_eax; // = 4C 8B D1 B8 + ULONG service_id; + USHORT syscall; // = 0F 05 + BYTE ret; // = C3 + BYTE pad; // = 66 + USHORT xchg_ax_ax1; // = 66 90 + USHORT xchg_ax_ax2; // = 66 90 +}; + +struct Redirected { + // this struct contains roughly the following code: + // jmp relative_32 + // xchg ax,ax // 3 byte nop + + Redirected() { + jmp = kRelJmp; + relative = 0; + pad = kPad; + xchg_ax_ax = kNop16; + }; + BYTE jmp; // = E9 + ULONG relative; + BYTE pad; // = 66 + USHORT xchg_ax_ax; // = 66 90 +}; + +struct InternalThunk { + // this struct contains roughly the following code: + // xor rax,rax + // mov eax, 0x00080000 // Thunk storage. + // mov [rax]PatchInfo.service, rcx // Save first argument. + // mov rcx, rax + // jmp relative_to_interceptor + + InternalThunk() { + xor_rax_mov_eax = kXorRaxMovEax; + patch_info = 0; + save_rcx = kSaveRcx; + mov_rcx_rax_jmp = kMovRcxRaxJmp; + relative = 0; + }; + ULONG xor_rax_mov_eax; // = 48 31 C0 B8 + ULONG patch_info; + ULONG save_rcx; // = 48 89 48 10 + ULONG mov_rcx_rax_jmp; // = 48 8b c8 e9 + ULONG relative; +}; + +struct ServiceFullThunk { + sandbox::PatchInfo patch_info; + ServiceEntry original; + InternalThunk internal_thunk; +}; + +#pragma pack(pop) + +// Simple utility function to write to a buffer on the child, if the memery has +// write protection attributes. +// Arguments: +// child_process (in): process to write to. +// address (out): memory position on the child to write to. +// buffer (in): local buffer with the data to write . +// length (in): number of bytes to write. +// Returns true on success. +bool WriteProtectedChildMemory(HANDLE child_process, + void* address, + const void* buffer, + size_t length) { + // first, remove the protections + DWORD old_protection; + if (!::VirtualProtectEx(child_process, address, length, + PAGE_WRITECOPY, &old_protection)) + return false; + + SIZE_T written; + bool ok = ::WriteProcessMemory(child_process, address, buffer, length, + &written) && (length == written); + + // always attempt to restore the original protection + if (!::VirtualProtectEx(child_process, address, length, + old_protection, &old_protection)) + return false; + + return ok; +} + +// Get pointers to the functions that we need from ntdll.dll. +NTSTATUS ResolveNtdll(sandbox::PatchInfo* patch_info) { + wchar_t* ntdll_name = L"ntdll.dll"; + HMODULE ntdll = ::GetModuleHandle(ntdll_name); + if (!ntdll) + return STATUS_PROCEDURE_NOT_FOUND; + + void* signal = ::GetProcAddress(ntdll, "NtSignalAndWaitForSingleObject"); + if (!signal) + return STATUS_PROCEDURE_NOT_FOUND; + + patch_info->signal_and_wait = + reinterpret_cast<NtSignalAndWaitForSingleObjectFunction>(signal); + + return STATUS_SUCCESS; +} + +}; // namespace + +namespace sandbox { + +NTSTATUS Service64ResolverThunk::Setup(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes, + size_t* storage_used) { + NTSTATUS ret = Init(target_module, interceptor_module, target_name, + interceptor_name, interceptor_entry_point, + thunk_storage, storage_bytes); + if (!NT_SUCCESS(ret)) + return ret; + + size_t thunk_bytes = GetThunkSize(); + scoped_array<char> thunk_buffer(new char[thunk_bytes]); + ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>( + thunk_buffer.get()); + + if (!IsFunctionAService(&thunk->original)) + return STATUS_UNSUCCESSFUL; + + ret = PerformPatch(thunk, thunk_storage); + + if (NULL != storage_used) + *storage_used = thunk_bytes; + + return ret; +} + +NTSTATUS Service64ResolverThunk::ResolveInterceptor( + const void* interceptor_module, + const char* interceptor_name, + const void** address) { + // After all, we are using a locally mapped version of the exe, so the + // action is the same as for a target function. + return ResolveTarget(interceptor_module, interceptor_name, + const_cast<void**>(address)); +} + +// In this case all the work is done from the parent, so resolve is +// just a simple GetProcAddress. +NTSTATUS Service64ResolverThunk::ResolveTarget(const void* module, + const char* function_name, + void** address) { + if (NULL == module) + return STATUS_UNSUCCESSFUL; + + *address = ::GetProcAddress(bit_cast<HMODULE>(module), function_name); + + if (NULL == *address) + return STATUS_UNSUCCESSFUL; + + return STATUS_SUCCESS; +} + +size_t Service64ResolverThunk::GetThunkSize() const { + return sizeof(ServiceFullThunk); +} + +bool Service64ResolverThunk::IsFunctionAService(void* local_thunk) const { + ServiceEntry function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) + return false; + + if (sizeof(function_code) != read) + return false; + + if (kMmovR10EcxMovEax != function_code.mov_r10_ecx_mov_eax || + kSyscall != function_code.syscall || kRetNp != function_code.ret) + return false; + + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + + return true; +} + +NTSTATUS Service64ResolverThunk::PerformPatch(void* local_thunk, + void* remote_thunk) { + ServiceFullThunk* full_local_thunk = reinterpret_cast<ServiceFullThunk*>( + local_thunk); + ServiceFullThunk* full_remote_thunk = reinterpret_cast<ServiceFullThunk*>( + remote_thunk); + + // If the source or target are above 4GB we cannot do this relative jump. + if (reinterpret_cast<ULONG_PTR>(full_remote_thunk) > + static_cast<ULONG_PTR>(ULONG_MAX)) + return STATUS_CONFLICTING_ADDRESSES; + + if (reinterpret_cast<ULONG_PTR>(target_) > static_cast<ULONG_PTR>(ULONG_MAX)) + return STATUS_CONFLICTING_ADDRESSES; + + // Patch the original code. + Redirected local_service; + Redirected* remote_service = reinterpret_cast<Redirected*>(target_); + ULONG_PTR diff = reinterpret_cast<BYTE*>(&full_remote_thunk->internal_thunk) - + &remote_service->pad; + local_service.relative = static_cast<ULONG>(diff); + + // Setup the PatchInfo structure. + SIZE_T actual; + if (!::ReadProcessMemory(process_, remote_thunk, local_thunk, + sizeof(PatchInfo), &actual)) + return STATUS_UNSUCCESSFUL; + if (sizeof(PatchInfo) != actual) + return STATUS_UNSUCCESSFUL; + + full_local_thunk->patch_info.orig_MapViewOfSection = reinterpret_cast< + NtMapViewOfSectionFunction>(&full_remote_thunk->original); + full_local_thunk->patch_info.patch_location = target_; + NTSTATUS ret = ResolveNtdll(&full_local_thunk->patch_info); + if (!NT_SUCCESS(ret)) + return ret; + + // Setup the thunk. The jump out is performed from right after the end of the + // thunk (full_remote_thunk + 1). + InternalThunk my_thunk; + ULONG_PTR patch_info = reinterpret_cast<ULONG_PTR>(remote_thunk); + my_thunk.patch_info = static_cast<ULONG>(patch_info); + diff = reinterpret_cast<const BYTE*>(interceptor_) - + reinterpret_cast<BYTE*>(full_remote_thunk + 1); + my_thunk.relative = static_cast<ULONG>(diff); + + memcpy(&full_local_thunk->internal_thunk, &my_thunk, sizeof(my_thunk)); + + // copy the local thunk buffer to the child + if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, + sizeof(ServiceFullThunk), &actual)) + return STATUS_UNSUCCESSFUL; + + if (sizeof(ServiceFullThunk) != actual) + return STATUS_UNSUCCESSFUL; + + // and now change the function to intercept, on the child + if (!::WriteProtectedChildMemory(process_, target_, &local_service, + sizeof(local_service))) + return STATUS_UNSUCCESSFUL; + + return STATUS_SUCCESS; +} + +} // namespace sandbox |