| Index: sandbox/win/src/service_resolver_32.cc
|
| diff --git a/sandbox/win/src/service_resolver_32.cc b/sandbox/win/src/service_resolver_32.cc
|
| deleted file mode 100644
|
| index f809227e9c786a06c95b736fa11a2272eb2c3c21..0000000000000000000000000000000000000000
|
| --- a/sandbox/win/src/service_resolver_32.cc
|
| +++ /dev/null
|
| @@ -1,478 +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.
|
| -
|
| -#include "sandbox/win/src/service_resolver.h"
|
| -
|
| -#include <stddef.h>
|
| -
|
| -#include "base/bit_cast.h"
|
| -#include "base/memory/scoped_ptr.h"
|
| -#include "sandbox/win/src/win_utils.h"
|
| -
|
| -namespace {
|
| -#pragma pack(push, 1)
|
| -
|
| -const BYTE kMovEax = 0xB8;
|
| -const BYTE kMovEdx = 0xBA;
|
| -const USHORT kMovEdxEsp = 0xD48B;
|
| -const USHORT kCallPtrEdx = 0x12FF;
|
| -const USHORT kCallEdx = 0xD2FF;
|
| -const BYTE kCallEip = 0xE8;
|
| -const BYTE kRet = 0xC2;
|
| -const BYTE kRet2 = 0xC3;
|
| -const USHORT kJmpEdx = 0xE2FF;
|
| -const USHORT kXorEcx = 0xC933;
|
| -const ULONG kLeaEdx = 0x0424548D;
|
| -const ULONG kCallFs1 = 0xC015FF64;
|
| -const USHORT kCallFs2 = 0;
|
| -const BYTE kCallFs3 = 0;
|
| -const BYTE kAddEsp1 = 0x83;
|
| -const USHORT kAddEsp2 = 0x4C4;
|
| -const BYTE kJmp32 = 0xE9;
|
| -const USHORT kSysenter = 0x340F;
|
| -
|
| -// Service code for 32 bit systems.
|
| -// NOTE: on win2003 "call dword ptr [edx]" is "call edx".
|
| -struct ServiceEntry {
|
| - // This struct contains roughly the following code:
|
| - // 00 mov eax,25h
|
| - // 05 mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
|
| - // 0a call dword ptr [edx]
|
| - // 0c ret 2Ch
|
| - // 0f nop
|
| - BYTE mov_eax; // = B8
|
| - ULONG service_id;
|
| - BYTE mov_edx; // = BA
|
| - ULONG stub;
|
| - USHORT call_ptr_edx; // = FF 12
|
| - BYTE ret; // = C2
|
| - USHORT num_params;
|
| - BYTE nop;
|
| -};
|
| -
|
| -// Service code for 32 bit Windows 8.
|
| -struct ServiceEntryW8 {
|
| - // This struct contains the following code:
|
| - // 00 b825000000 mov eax,25h
|
| - // 05 e803000000 call eip+3
|
| - // 0a c22c00 ret 2Ch
|
| - // 0d 8bd4 mov edx,esp
|
| - // 0f 0f34 sysenter
|
| - // 11 c3 ret
|
| - // 12 8bff mov edi,edi
|
| - BYTE mov_eax; // = B8
|
| - ULONG service_id;
|
| - BYTE call_eip; // = E8
|
| - ULONG call_offset;
|
| - BYTE ret_p; // = C2
|
| - USHORT num_params;
|
| - USHORT mov_edx_esp; // = BD D4
|
| - USHORT sysenter; // = 0F 34
|
| - BYTE ret; // = C3
|
| - USHORT nop;
|
| -};
|
| -
|
| -// Service code for a 32 bit process running on a 64 bit os.
|
| -struct Wow64Entry {
|
| - // This struct may contain one of two versions of code:
|
| - // 1. For XP, Vista and 2K3:
|
| - // 00 b825000000 mov eax, 25h
|
| - // 05 33c9 xor ecx, ecx
|
| - // 07 8d542404 lea edx, [esp + 4]
|
| - // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
|
| - // 12 c22c00 ret 2Ch
|
| - //
|
| - // 2. For Windows 7:
|
| - // 00 b825000000 mov eax, 25h
|
| - // 05 33c9 xor ecx, ecx
|
| - // 07 8d542404 lea edx, [esp + 4]
|
| - // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
|
| - // 12 83c404 add esp, 4
|
| - // 15 c22c00 ret 2Ch
|
| - //
|
| - // So we base the structure on the bigger one:
|
| - BYTE mov_eax; // = B8
|
| - ULONG service_id;
|
| - USHORT xor_ecx; // = 33 C9
|
| - ULONG lea_edx; // = 8D 54 24 04
|
| - ULONG call_fs1; // = 64 FF 15 C0
|
| - USHORT call_fs2; // = 00 00
|
| - BYTE call_fs3; // = 00
|
| - BYTE add_esp1; // = 83 or ret
|
| - USHORT add_esp2; // = C4 04 or num_params
|
| - BYTE ret; // = C2
|
| - USHORT num_params;
|
| -};
|
| -
|
| -// Service code for a 32 bit process running on 64 bit Windows 8.
|
| -struct Wow64EntryW8 {
|
| - // 00 b825000000 mov eax, 25h
|
| - // 05 64ff15c0000000 call dword ptr fs:[0C0h]
|
| - // 0b c22c00 ret 2Ch
|
| - // 0f 90 nop
|
| - BYTE mov_eax; // = B8
|
| - ULONG service_id;
|
| - ULONG call_fs1; // = 64 FF 15 C0
|
| - USHORT call_fs2; // = 00 00
|
| - BYTE call_fs3; // = 00
|
| - BYTE ret; // = C2
|
| - USHORT num_params;
|
| - BYTE nop;
|
| -};
|
| -
|
| -// Service code for a 32 bit process running on 64 bit Windows 10.
|
| -struct Wow64EntryW10 {
|
| - // 00 b828000000 mov eax, 28h
|
| - // 05 bab0d54877 mov edx, 7748D5B0h
|
| - // 09 ffd2 call edx
|
| - // 0b c22800 ret 28h
|
| - BYTE mov_eax; // = B8
|
| - ULONG service_id;
|
| - BYTE mov_edx; // = BA
|
| - ULONG mov_edx_param;
|
| - USHORT call_edx; // = FF D2
|
| - BYTE ret; // = C2
|
| - USHORT num_params;
|
| -};
|
| -
|
| -// Make sure that relaxed patching works as expected.
|
| -const size_t kMinServiceSize = offsetof(ServiceEntry, ret);
|
| -static_assert(sizeof(ServiceEntryW8) >= kMinServiceSize,
|
| - "wrong service length");
|
| -static_assert(sizeof(Wow64Entry) >= kMinServiceSize, "wrong service length");
|
| -static_assert(sizeof(Wow64EntryW8) >= kMinServiceSize, "wrong service length");
|
| -
|
| -struct ServiceFullThunk {
|
| - union {
|
| - ServiceEntry original;
|
| - ServiceEntryW8 original_w8;
|
| - Wow64Entry wow_64;
|
| - Wow64EntryW8 wow_64_w8;
|
| - };
|
| - int internal_thunk; // Dummy member to the beginning of the internal thunk.
|
| -};
|
| -
|
| -#pragma pack(pop)
|
| -
|
| -}; // namespace
|
| -
|
| -namespace sandbox {
|
| -
|
| -NTSTATUS ServiceResolverThunk::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;
|
| -
|
| - relative_jump_ = 0;
|
| - size_t thunk_bytes = GetThunkSize();
|
| - scoped_ptr<char[]> thunk_buffer(new char[thunk_bytes]);
|
| - ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(
|
| - thunk_buffer.get());
|
| -
|
| - if (!IsFunctionAService(&thunk->original) &&
|
| - (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) {
|
| - return STATUS_UNSUCCESSFUL;
|
| - }
|
| -
|
| - ret = PerformPatch(thunk, thunk_storage);
|
| -
|
| - if (NULL != storage_used)
|
| - *storage_used = thunk_bytes;
|
| -
|
| - return ret;
|
| -}
|
| -
|
| -size_t ServiceResolverThunk::GetThunkSize() const {
|
| - return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize();
|
| -}
|
| -
|
| -NTSTATUS ServiceResolverThunk::CopyThunk(const void* target_module,
|
| - const char* target_name,
|
| - BYTE* thunk_storage,
|
| - size_t storage_bytes,
|
| - size_t* storage_used) {
|
| - NTSTATUS ret = ResolveTarget(target_module, target_name, &target_);
|
| - if (!NT_SUCCESS(ret))
|
| - return ret;
|
| -
|
| - size_t thunk_bytes = GetThunkSize();
|
| - if (storage_bytes < thunk_bytes)
|
| - return STATUS_UNSUCCESSFUL;
|
| -
|
| - ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(thunk_storage);
|
| -
|
| - if (!IsFunctionAService(&thunk->original) &&
|
| - (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) {
|
| - return STATUS_UNSUCCESSFUL;
|
| - }
|
| -
|
| - if (NULL != storage_used)
|
| - *storage_used = thunk_bytes;
|
| -
|
| - return ret;
|
| -}
|
| -
|
| -bool ServiceResolverThunk::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 (kMovEax != function_code.mov_eax ||
|
| - kMovEdx != function_code.mov_edx ||
|
| - (kCallPtrEdx != function_code.call_ptr_edx &&
|
| - kCallEdx != function_code.call_ptr_edx) ||
|
| - kRet != function_code.ret) {
|
| - return false;
|
| - }
|
| -
|
| - // Find the system call pointer if we don't already have it.
|
| - if (kCallEdx != function_code.call_ptr_edx) {
|
| - DWORD ki_system_call;
|
| - if (!::ReadProcessMemory(process_,
|
| - bit_cast<const void*>(function_code.stub),
|
| - &ki_system_call, sizeof(ki_system_call), &read)) {
|
| - return false;
|
| - }
|
| -
|
| - if (sizeof(ki_system_call) != read)
|
| - return false;
|
| -
|
| - HMODULE module_1, module_2;
|
| - // last check, call_stub should point to a KiXXSystemCall function on ntdll
|
| - if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
| - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
| - bit_cast<const wchar_t*>(ki_system_call),
|
| - &module_1)) {
|
| - return false;
|
| - }
|
| -
|
| - if (NULL != ntdll_base_) {
|
| - // This path is only taken when running the unit tests. We want to be
|
| - // able to patch a buffer in memory, so target_ is not inside ntdll.
|
| - module_2 = ntdll_base_;
|
| - } else {
|
| - if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
| - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
| - reinterpret_cast<const wchar_t*>(target_),
|
| - &module_2))
|
| - return false;
|
| - }
|
| -
|
| - if (module_1 != module_2)
|
| - return false;
|
| - }
|
| -
|
| - // Save the verified code
|
| - memcpy(local_thunk, &function_code, sizeof(function_code));
|
| -
|
| - return true;
|
| -}
|
| -
|
| -NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk,
|
| - void* remote_thunk) {
|
| - ServiceEntry intercepted_code;
|
| - size_t bytes_to_write = sizeof(intercepted_code);
|
| - ServiceFullThunk *full_local_thunk = reinterpret_cast<ServiceFullThunk*>(
|
| - local_thunk);
|
| - ServiceFullThunk *full_remote_thunk = reinterpret_cast<ServiceFullThunk*>(
|
| - remote_thunk);
|
| -
|
| - // patch the original code
|
| - memcpy(&intercepted_code, &full_local_thunk->original,
|
| - sizeof(intercepted_code));
|
| - intercepted_code.mov_eax = kMovEax;
|
| - intercepted_code.service_id = full_local_thunk->original.service_id;
|
| - intercepted_code.mov_edx = kMovEdx;
|
| - intercepted_code.stub = bit_cast<ULONG>(&full_remote_thunk->internal_thunk);
|
| - intercepted_code.call_ptr_edx = kJmpEdx;
|
| - bytes_to_write = kMinServiceSize;
|
| -
|
| - if (relative_jump_) {
|
| - intercepted_code.mov_eax = kJmp32;
|
| - intercepted_code.service_id = relative_jump_;
|
| - bytes_to_write = offsetof(ServiceEntry, mov_edx);
|
| - }
|
| -
|
| - // setup the thunk
|
| - SetInternalThunk(&full_local_thunk->internal_thunk, GetInternalThunkSize(),
|
| - remote_thunk, interceptor_);
|
| -
|
| - size_t thunk_size = GetThunkSize();
|
| -
|
| - // copy the local thunk buffer to the child
|
| - SIZE_T written;
|
| - if (!::WriteProcessMemory(process_, remote_thunk, local_thunk,
|
| - thunk_size, &written)) {
|
| - return STATUS_UNSUCCESSFUL;
|
| - }
|
| -
|
| - if (thunk_size != written)
|
| - return STATUS_UNSUCCESSFUL;
|
| -
|
| - // and now change the function to intercept, on the child
|
| - if (NULL != ntdll_base_) {
|
| - // running a unit test
|
| - if (!::WriteProcessMemory(process_, target_, &intercepted_code,
|
| - bytes_to_write, &written))
|
| - return STATUS_UNSUCCESSFUL;
|
| - } else {
|
| - if (!WriteProtectedChildMemory(process_, target_, &intercepted_code,
|
| - bytes_to_write))
|
| - return STATUS_UNSUCCESSFUL;
|
| - }
|
| -
|
| - return STATUS_SUCCESS;
|
| -}
|
| -
|
| -bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk,
|
| - void* remote_thunk) {
|
| - 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 (kJmp32 == function_code.mov_eax) {
|
| - // Plain old entry point patch. The relative jump address follows it.
|
| - ULONG relative = function_code.service_id;
|
| -
|
| - // First, fix our copy of their patch.
|
| - relative += bit_cast<ULONG>(target_) - bit_cast<ULONG>(remote_thunk);
|
| -
|
| - function_code.service_id = relative;
|
| -
|
| - // And now, remember how to re-patch it.
|
| - ServiceFullThunk *full_thunk =
|
| - reinterpret_cast<ServiceFullThunk*>(remote_thunk);
|
| -
|
| - const ULONG kJmp32Size = 5;
|
| -
|
| - relative_jump_ = bit_cast<ULONG>(&full_thunk->internal_thunk) -
|
| - bit_cast<ULONG>(target_) - kJmp32Size;
|
| - }
|
| -
|
| - // Save the verified code
|
| - memcpy(local_thunk, &function_code, sizeof(function_code));
|
| -
|
| - return true;
|
| -}
|
| -
|
| -bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const {
|
| - Wow64Entry 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 (kMovEax != function_code.mov_eax || kXorEcx != function_code.xor_ecx ||
|
| - kLeaEdx != function_code.lea_edx || kCallFs1 != function_code.call_fs1 ||
|
| - kCallFs2 != function_code.call_fs2 ||
|
| - kCallFs3 != function_code.call_fs3) {
|
| - return false;
|
| - }
|
| -
|
| - if ((kAddEsp1 == function_code.add_esp1 &&
|
| - kAddEsp2 == function_code.add_esp2 &&
|
| - kRet == function_code.ret) || kRet == function_code.add_esp1) {
|
| - // Save the verified code
|
| - memcpy(local_thunk, &function_code, sizeof(function_code));
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| -bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const {
|
| - Wow64EntryW8 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 (kMovEax != function_code.mov_eax || kCallFs1 != function_code.call_fs1 ||
|
| - kCallFs2 != function_code.call_fs2 ||
|
| - kCallFs3 != function_code.call_fs3 || kRet != function_code.ret) {
|
| - return false;
|
| - }
|
| -
|
| - // Save the verified code
|
| - memcpy(local_thunk, &function_code, sizeof(function_code));
|
| - return true;
|
| -}
|
| -
|
| -bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const {
|
| - ServiceEntryW8 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 (kMovEax != function_code.mov_eax || kCallEip != function_code.call_eip ||
|
| - function_code.call_offset != 3 || kRet != function_code.ret_p ||
|
| - kMovEdxEsp != function_code.mov_edx_esp ||
|
| - kSysenter != function_code.sysenter || kRet2 != function_code.ret) {
|
| - return false;
|
| - }
|
| -
|
| - // Save the verified code
|
| - memcpy(local_thunk, &function_code, sizeof(function_code));
|
| -
|
| - return true;
|
| -}
|
| -
|
| -bool Wow64W10ResolverThunk::IsFunctionAService(void* local_thunk) const {
|
| - Wow64EntryW10 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 (kMovEax != function_code.mov_eax ||
|
| - kMovEdx != function_code.mov_edx ||
|
| - kCallEdx != function_code.call_edx ||
|
| - kRet != function_code.ret) {
|
| - return false;
|
| - }
|
| -
|
| - // Save the verified code
|
| - memcpy(local_thunk, &function_code, sizeof(function_code));
|
| - return true;
|
| -}
|
| -
|
| -} // namespace sandbox
|
|
|