michael@0: // Copyright (c) 2012 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: #include "sandbox/win/src/service_resolver.h" michael@0: michael@0: #include "base/logging.h" michael@0: #include "base/memory/scoped_ptr.h" michael@0: #include "sandbox/win/src/win_utils.h" michael@0: michael@0: namespace { michael@0: #pragma pack(push, 1) michael@0: michael@0: const ULONG kMmovR10EcxMovEax = 0xB8D18B4C; michael@0: const USHORT kSyscall = 0x050F; michael@0: const BYTE kRetNp = 0xC3; michael@0: const ULONG64 kMov1 = 0x54894808244C8948; michael@0: const ULONG64 kMov2 = 0x4C182444894C1024; michael@0: const ULONG kMov3 = 0x20244C89; michael@0: michael@0: // Service code for 64 bit systems. michael@0: struct ServiceEntry { michael@0: // This struct contains roughly the following code: michael@0: // 00 mov r10,rcx michael@0: // 03 mov eax,52h michael@0: // 08 syscall michael@0: // 0a ret michael@0: // 0b xchg ax,ax michael@0: // 0e xchg ax,ax michael@0: michael@0: ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8 michael@0: ULONG service_id; michael@0: USHORT syscall; // = 0F 05 michael@0: BYTE ret; // = C3 michael@0: BYTE pad; // = 66 michael@0: USHORT xchg_ax_ax1; // = 66 90 michael@0: USHORT xchg_ax_ax2; // = 66 90 michael@0: }; michael@0: michael@0: // Service code for 64 bit Windows 8. michael@0: struct ServiceEntryW8 { michael@0: // This struct contains the following code: michael@0: // 00 48894c2408 mov [rsp+8], rcx michael@0: // 05 4889542410 mov [rsp+10], rdx michael@0: // 0a 4c89442418 mov [rsp+18], r8 michael@0: // 0f 4c894c2420 mov [rsp+20], r9 michael@0: // 14 4c8bd1 mov r10,rcx michael@0: // 17 b825000000 mov eax,25h michael@0: // 1c 0f05 syscall michael@0: // 1e c3 ret michael@0: // 1f 90 nop michael@0: michael@0: ULONG64 mov_1; // = 48 89 4C 24 08 48 89 54 michael@0: ULONG64 mov_2; // = 24 10 4C 89 44 24 18 4C michael@0: ULONG mov_3; // = 89 4C 24 20 michael@0: ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8 michael@0: ULONG service_id; michael@0: USHORT syscall; // = 0F 05 michael@0: BYTE ret; // = C2 michael@0: BYTE nop; // = 90 michael@0: }; michael@0: michael@0: // We don't have an internal thunk for x64. michael@0: struct ServiceFullThunk { michael@0: union { michael@0: ServiceEntry original; michael@0: ServiceEntryW8 original_w8; michael@0: }; michael@0: }; michael@0: michael@0: #pragma pack(pop) michael@0: michael@0: bool IsService(const void* source) { michael@0: const ServiceEntry* service = michael@0: reinterpret_cast(source); michael@0: michael@0: return (kMmovR10EcxMovEax == service->mov_r10_rcx_mov_eax && michael@0: kSyscall == service->syscall && kRetNp == service->ret); michael@0: } michael@0: michael@0: }; // namespace michael@0: michael@0: namespace sandbox { michael@0: michael@0: NTSTATUS ServiceResolverThunk::Setup(const void* target_module, michael@0: const void* interceptor_module, michael@0: const char* target_name, michael@0: const char* interceptor_name, michael@0: const void* interceptor_entry_point, michael@0: void* thunk_storage, michael@0: size_t storage_bytes, michael@0: size_t* storage_used) { michael@0: NTSTATUS ret = Init(target_module, interceptor_module, target_name, michael@0: interceptor_name, interceptor_entry_point, michael@0: thunk_storage, storage_bytes); michael@0: if (!NT_SUCCESS(ret)) michael@0: return ret; michael@0: michael@0: size_t thunk_bytes = GetThunkSize(); michael@0: scoped_ptr thunk_buffer(new char[thunk_bytes]); michael@0: ServiceFullThunk* thunk = reinterpret_cast( michael@0: thunk_buffer.get()); michael@0: michael@0: if (!IsFunctionAService(&thunk->original)) michael@0: return STATUS_UNSUCCESSFUL; michael@0: michael@0: ret = PerformPatch(thunk, thunk_storage); michael@0: michael@0: if (NULL != storage_used) michael@0: *storage_used = thunk_bytes; michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: size_t ServiceResolverThunk::GetThunkSize() const { michael@0: return sizeof(ServiceFullThunk); michael@0: } michael@0: michael@0: bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { michael@0: ServiceFullThunk function_code; michael@0: SIZE_T read; michael@0: if (!::ReadProcessMemory(process_, target_, &function_code, michael@0: sizeof(function_code), &read)) michael@0: return false; michael@0: michael@0: if (sizeof(function_code) != read) michael@0: return false; michael@0: michael@0: if (!IsService(&function_code)) { michael@0: // See if it's the Win8 signature. michael@0: ServiceEntryW8* w8_service = &function_code.original_w8; michael@0: if (!IsService(&w8_service->mov_r10_rcx_mov_eax) || michael@0: w8_service->mov_1 != kMov1 || w8_service->mov_1 != kMov1 || michael@0: w8_service->mov_1 != kMov1) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Save the verified code. michael@0: memcpy(local_thunk, &function_code, sizeof(function_code)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, michael@0: void* remote_thunk) { michael@0: ServiceFullThunk* full_local_thunk = reinterpret_cast( michael@0: local_thunk); michael@0: ServiceFullThunk* full_remote_thunk = reinterpret_cast( michael@0: remote_thunk); michael@0: michael@0: // Patch the original code. michael@0: ServiceEntry local_service; michael@0: DCHECK_GE(GetInternalThunkSize(), sizeof(local_service)); michael@0: if (!SetInternalThunk(&local_service, sizeof(local_service), NULL, michael@0: interceptor_)) michael@0: return STATUS_UNSUCCESSFUL; michael@0: michael@0: // Copy the local thunk buffer to the child. michael@0: SIZE_T actual; michael@0: if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, michael@0: sizeof(ServiceFullThunk), &actual)) michael@0: return STATUS_UNSUCCESSFUL; michael@0: michael@0: if (sizeof(ServiceFullThunk) != actual) michael@0: return STATUS_UNSUCCESSFUL; michael@0: michael@0: // And now change the function to intercept, on the child. michael@0: if (NULL != ntdll_base_) { michael@0: // Running a unit test. michael@0: if (!::WriteProcessMemory(process_, target_, &local_service, michael@0: sizeof(local_service), &actual)) michael@0: return STATUS_UNSUCCESSFUL; michael@0: } else { michael@0: if (!WriteProtectedChildMemory(process_, target_, &local_service, michael@0: sizeof(local_service))) michael@0: return STATUS_UNSUCCESSFUL; michael@0: } michael@0: michael@0: return STATUS_SUCCESS; michael@0: } michael@0: michael@0: bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const { michael@0: NOTREACHED(); michael@0: return false; michael@0: } michael@0: michael@0: bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const { michael@0: NOTREACHED(); michael@0: return false; michael@0: } michael@0: michael@0: } // namespace sandbox