summaryrefslogtreecommitdiffstats
path: root/sandbox/src/service_resolver.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/src/service_resolver.cc')
-rw-r--r--sandbox/src/service_resolver.cc367
1 files changed, 4 insertions, 363 deletions
diff --git a/sandbox/src/service_resolver.cc b/sandbox/src/service_resolver.cc
index 069b37f..728555e 100644
--- a/sandbox/src/service_resolver.cc
+++ b/sandbox/src/service_resolver.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// 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.
@@ -6,163 +6,9 @@
#include "base/logging.h"
#include "base/pe_image.h"
-#include "base/scoped_ptr.h"
-#include "sandbox/src/sandbox_types.h"
-#include "sandbox/src/sandbox_utils.h"
-
-
-namespace {
-#pragma pack(push, 1)
-
-const BYTE kMovEax = 0xB8;
-const BYTE kMovEdx = 0xBA;
-const USHORT kCallPtrEdx = 0x12FF;
-const USHORT kCallEdx = 0xD2FF;
-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 USHORT kCallFs2 = 0;
-const BYTE kCallFs3 = 0;
-const BYTE kAddEsp1 = 0x83;
-const USHORT kAddEsp2 = 0x4C4;
-const BYTE kJmp32 = 0xE9;
-
-const int kMaxService = 1000;
-
-// 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;
- ULONG pad1; // Extend the structure to be the same size as the
- ULONG pad2; // 64 version (Wow64Entry)
-};
-
-// 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 b852000000 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 b852000000 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;
-};
-
-// Make sure that relaxed patching works as expected.
-COMPILE_ASSERT(sizeof(ServiceEntry) == sizeof(Wow64Entry), wrong_service_len);
-
-struct ServiceFullThunk {
- union {
- ServiceEntry original;
- Wow64Entry wow_64;
- };
- int internal_thunk; // Dummy member to the beginning of the 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;
-}
-
-}; // 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;
-
- 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) &&
- (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage)))
- return STATUS_UNSUCCESSFUL;
-
- ret = PerformPatch(thunk, thunk_storage);
-
- if (NULL != storage_used)
- *storage_used = thunk_bytes;
-
- return ret;
-}
-
NTSTATUS ServiceResolverThunk::ResolveInterceptor(
const void* interceptor_module,
const char* interceptor_name,
@@ -174,7 +20,7 @@ NTSTATUS ServiceResolverThunk::ResolveInterceptor(
}
// In this case all the work is done from the parent, so resolve is
-// just a simple GetProcAddress
+// just a simple GetProcAddress.
NTSTATUS ServiceResolverThunk::ResolveTarget(const void* module,
const char* function_name,
void** address) {
@@ -185,217 +31,12 @@ NTSTATUS ServiceResolverThunk::ResolveTarget(const void* module,
PEImage module_image(module);
*address = module_image.GetProcAddress(function_name);
- if (NULL == *address)
+ if (NULL == *address) {
+ NOTREACHED();
return STATUS_UNSUCCESSFUL;
-
- return STATUS_SUCCESS;
-}
-
-size_t ServiceResolverThunk::GetThunkSize() const {
- return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize();
-}
-
-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 (!GetModuleHandleHelper(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 (!GetModuleHandleHelper(
- 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;
- if (!win2k_) {
- intercepted_code.ret = kRet;
- intercepted_code.num_params = full_local_thunk->original.num_params;
- intercepted_code.nop = kNop;
- } else {
- bytes_to_write = offsetof(ServiceEntry, ret);
- }
-
- 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 Win2kResolverThunk::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 ||
- function_code.service_id > kMaxService)
- return false;
-
- // Save the verified code
- memcpy(local_thunk, &function_code, sizeof(function_code));
-
- return true;
-}
-
} // namespace sandbox