summaryrefslogtreecommitdiffstats
path: root/sandbox
diff options
context:
space:
mode:
authorrvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-11 22:20:31 +0000
committerrvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-11 22:20:31 +0000
commit0483cf1851e39d333d5fdab34506e1ef1006fc8a (patch)
tree2408fa7306b79dfd241b6a8deca616c21197ef2c /sandbox
parent83f30e9abf82b6e1defa5c6fcc3ed2bc6b2aa067 (diff)
downloadchromium_src-0483cf1851e39d333d5fdab34506e1ef1006fc8a.zip
chromium_src-0483cf1851e39d333d5fdab34506e1ef1006fc8a.tar.gz
chromium_src-0483cf1851e39d333d5fdab34506e1ef1006fc8a.tar.bz2
Sandbox: Some cleanup after the previous changes.
No real code change. BUG=27218 TEST=current tests. Review URL: http://codereview.chromium.org/597050 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@38837 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r--sandbox/sandbox.gyp6
-rw-r--r--sandbox/src/resolver.cc79
-rw-r--r--sandbox/src/resolver_32.cc88
-rw-r--r--sandbox/src/resolver_64.cc51
-rw-r--r--sandbox/src/service_resolver.cc367
-rw-r--r--sandbox/src/service_resolver_32.cc341
-rw-r--r--sandbox/src/service_resolver_64.cc62
-rw-r--r--sandbox/src/win_utils.cc20
-rw-r--r--sandbox/src/win_utils.h6
9 files changed, 465 insertions, 555 deletions
diff --git a/sandbox/sandbox.gyp b/sandbox/sandbox.gyp
index fa2b817..5cc70ee 100644
--- a/sandbox/sandbox.gyp
+++ b/sandbox/sandbox.gyp
@@ -55,6 +55,7 @@
'src/process_thread_policy.h',
'src/registry_policy.cc',
'src/registry_policy.h',
+ 'src/resolver.cc',
'src/resolver.h',
'src/restricted_token_utils.cc',
'src/restricted_token_utils.h',
@@ -73,6 +74,7 @@
'src/sandbox.cc',
'src/sandbox.h',
'src/security_level.h',
+ 'src/service_resolver.cc',
'src/service_resolver.h',
'src/shared_handles.cc',
'src/shared_handles.h',
@@ -207,8 +209,8 @@
'src/registry_dispatcher.cc',
'src/registry_dispatcher.h',
'src/registry_interception.h',
- 'src/resolver.cc',
- 'src/service_resolver.cc',
+ 'src/resolver_32.cc',
+ 'src/service_resolver_32.cc',
'src/sharedmem_ipc_client.cc',
'src/sharedmem_ipc_client.h',
'src/sharedmem_ipc_server.cc',
diff --git a/sandbox/src/resolver.cc b/sandbox/src/resolver.cc
index c2a8136..7ed02e4 100644
--- a/sandbox/src/resolver.cc
+++ b/sandbox/src/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.
@@ -7,54 +7,6 @@
#include "base/pe_image.h"
#include "sandbox/src/sandbox_nt_util.h"
-namespace {
-
-#pragma pack(push, 1)
-struct InternalThunk {
- // This struct contains roughly the following code:
- // sub esp, 8 // Create working space
- // push edx // Save register
- // mov edx, [esp + 0xc] // Get return adddress
- // mov [esp + 8], edx // Store return address
- // mov dword ptr [esp + 0xc], 0x7c401200 // Store extra argument
- // mov dword ptr [esp + 4], 0x40010203 // Store address to jump to
- // pop edx // Restore register
- // ret // Jump to interceptor
- //
- // This code only modifies esp and eip so it must work with to normal calling
- // convention. It is assembled as:
- //
- // 00 83ec08 sub esp,8
- // 03 52 push edx
- // 04 8b54240c mov edx,dword ptr [esp + 0Ch]
- // 08 89542408 mov dword ptr [esp + 8], edx
- // 0c c744240c0012407c mov dword ptr [esp + 0Ch], 7C401200h
- // 14 c744240403020140 mov dword ptr [esp + 4], 40010203h
- // 1c 5a pop edx
- // 1d c3 ret
- InternalThunk() {
- opcodes_1 = 0x5208ec83;
- opcodes_2 = 0x0c24548b;
- opcodes_3 = 0x08245489;
- opcodes_4 = 0x0c2444c7;
- opcodes_5 = 0x042444c7;
- opcodes_6 = 0xc35a;
- extra_argument = 0;
- interceptor_function = 0;
- };
- ULONG opcodes_1; // = 0x5208ec83
- ULONG opcodes_2; // = 0x0c24548b
- ULONG opcodes_3; // = 0x08245489
- ULONG opcodes_4; // = 0x0c2444c7
- ULONG extra_argument;
- ULONG opcodes_5; // = 0x042444c7
- ULONG interceptor_function;
- USHORT opcodes_6; // = 0xc35a
-};
-#pragma pack(pop)
-
-}; // namespace
-
namespace sandbox {
NTSTATUS ResolverThunk::Init(const void* target_module,
@@ -88,28 +40,6 @@ NTSTATUS ResolverThunk::Init(const void* target_module,
return ret;
}
-size_t ResolverThunk::GetInternalThunkSize() const {
- return sizeof(InternalThunk);
-}
-
-bool ResolverThunk::SetInternalThunk(void* storage, size_t storage_bytes,
- const void* original_function,
- const void* interceptor) {
- if (storage_bytes < sizeof(InternalThunk))
- return false;
-
- InternalThunk* thunk = new(storage, NT_PLACE) InternalThunk;
-
-#pragma warning(push)
-#pragma warning(disable: 4311)
- // These casts generate warnings because they are 32 bit specific.
- thunk->interceptor_function = reinterpret_cast<ULONG>(interceptor);
- thunk->extra_argument = reinterpret_cast<ULONG>(original_function);
-#pragma warning(pop)
-
- return true;
-}
-
NTSTATUS ResolverThunk::ResolveInterceptor(const void* interceptor_module,
const char* interceptor_name,
const void** address) {
@@ -129,11 +59,4 @@ NTSTATUS ResolverThunk::ResolveInterceptor(const void* interceptor_module,
return STATUS_SUCCESS;
}
-NTSTATUS ResolverThunk::ResolveTarget(const void* module,
- const char* function_name,
- void** address) {
- const void** casted = const_cast<const void**>(address);
- return ResolverThunk::ResolveInterceptor(module, function_name, casted);
-}
-
} // namespace sandbox
diff --git a/sandbox/src/resolver_32.cc b/sandbox/src/resolver_32.cc
new file mode 100644
index 0000000..ae5c168
--- /dev/null
+++ b/sandbox/src/resolver_32.cc
@@ -0,0 +1,88 @@
+// 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/src/resolver.h"
+
+#include "sandbox/src/sandbox_nt_util.h"
+
+namespace {
+
+#pragma pack(push, 1)
+struct InternalThunk {
+ // This struct contains roughly the following code:
+ // sub esp, 8 // Create working space
+ // push edx // Save register
+ // mov edx, [esp + 0xc] // Get return adddress
+ // mov [esp + 8], edx // Store return address
+ // mov dword ptr [esp + 0xc], 0x7c401200 // Store extra argument
+ // mov dword ptr [esp + 4], 0x40010203 // Store address to jump to
+ // pop edx // Restore register
+ // ret // Jump to interceptor
+ //
+ // This code only modifies esp and eip so it must work with to normal calling
+ // convention. It is assembled as:
+ //
+ // 00 83ec08 sub esp,8
+ // 03 52 push edx
+ // 04 8b54240c mov edx,dword ptr [esp + 0Ch]
+ // 08 89542408 mov dword ptr [esp + 8], edx
+ // 0c c744240c0012407c mov dword ptr [esp + 0Ch], 7C401200h
+ // 14 c744240403020140 mov dword ptr [esp + 4], 40010203h
+ // 1c 5a pop edx
+ // 1d c3 ret
+ InternalThunk() {
+ opcodes_1 = 0x5208ec83;
+ opcodes_2 = 0x0c24548b;
+ opcodes_3 = 0x08245489;
+ opcodes_4 = 0x0c2444c7;
+ opcodes_5 = 0x042444c7;
+ opcodes_6 = 0xc35a;
+ extra_argument = 0;
+ interceptor_function = 0;
+ };
+ ULONG opcodes_1; // = 0x5208ec83
+ ULONG opcodes_2; // = 0x0c24548b
+ ULONG opcodes_3; // = 0x08245489
+ ULONG opcodes_4; // = 0x0c2444c7
+ ULONG extra_argument;
+ ULONG opcodes_5; // = 0x042444c7
+ ULONG interceptor_function;
+ USHORT opcodes_6; // = 0xc35a
+};
+#pragma pack(pop)
+
+}; // namespace
+
+namespace sandbox {
+
+bool ResolverThunk::SetInternalThunk(void* storage, size_t storage_bytes,
+ const void* original_function,
+ const void* interceptor) {
+ if (storage_bytes < sizeof(InternalThunk))
+ return false;
+
+ InternalThunk* thunk = new(storage, NT_PLACE) InternalThunk;
+
+#pragma warning(push)
+#pragma warning(disable: 4311)
+ // These casts generate warnings because they are 32 bit specific.
+ thunk->interceptor_function = reinterpret_cast<ULONG>(interceptor);
+ thunk->extra_argument = reinterpret_cast<ULONG>(original_function);
+#pragma warning(pop)
+
+ return true;
+}
+
+size_t ResolverThunk::GetInternalThunkSize() const {
+ return sizeof(InternalThunk);
+}
+
+NTSTATUS ResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ const void** casted = const_cast<const void**>(address);
+ return ResolverThunk::ResolveInterceptor(module, function_name, casted);
+}
+
+} // namespace sandbox
diff --git a/sandbox/src/resolver_64.cc b/sandbox/src/resolver_64.cc
index 4c736c2..96d039b 100644
--- a/sandbox/src/resolver_64.cc
+++ b/sandbox/src/resolver_64.cc
@@ -4,7 +4,6 @@
#include "sandbox/src/resolver.h"
-#include "base/pe_image.h"
#include "sandbox/src/sandbox_nt_util.h"
namespace {
@@ -44,37 +43,6 @@ struct InternalThunk {
namespace sandbox {
-NTSTATUS ResolverThunk::Init(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) {
- if (NULL == thunk_storage || 0 == storage_bytes ||
- NULL == target_module || NULL == target_name)
- return STATUS_INVALID_PARAMETER;
-
- if (storage_bytes < GetThunkSize())
- return STATUS_BUFFER_TOO_SMALL;
-
- NTSTATUS ret = STATUS_SUCCESS;
- if (NULL == interceptor_entry_point) {
- ret = ResolveInterceptor(interceptor_module, interceptor_name,
- &interceptor_entry_point);
- if (!NT_SUCCESS(ret))
- return ret;
- }
-
- ret = ResolveTarget(target_module, target_name, &target_);
- if (!NT_SUCCESS(ret))
- return ret;
-
- interceptor_ = interceptor_entry_point;
-
- return ret;
-}
-
size_t ResolverThunk::GetInternalThunkSize() const {
return sizeof(InternalThunk);
}
@@ -91,25 +59,6 @@ bool ResolverThunk::SetInternalThunk(void* storage, size_t storage_bytes,
return true;
}
-NTSTATUS ResolverThunk::ResolveInterceptor(const void* interceptor_module,
- const char* interceptor_name,
- const void** address) {
- DCHECK_NT(address);
- if (!interceptor_module)
- return STATUS_INVALID_PARAMETER;
-
- PEImage pe(interceptor_module);
- if (!pe.VerifyMagic())
- return STATUS_INVALID_IMAGE_FORMAT;
-
- *address = pe.GetProcAddress(interceptor_name);
-
- if (!(*address))
- return STATUS_PROCEDURE_NOT_FOUND;
-
- return STATUS_SUCCESS;
-}
-
NTSTATUS ResolverThunk::ResolveTarget(const void* module,
const char* function_name,
void** address) {
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
diff --git a/sandbox/src/service_resolver_32.cc b/sandbox/src/service_resolver_32.cc
new file mode 100644
index 0000000..aaa2c49
--- /dev/null
+++ b/sandbox/src/service_resolver_32.cc
@@ -0,0 +1,341 @@
+// 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/src/service_resolver.h"
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "sandbox/src/sandbox_utils.h"
+#include "sandbox/src/win_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)
+
+}; // 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;
+}
+
+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
diff --git a/sandbox/src/service_resolver_64.cc b/sandbox/src/service_resolver_64.cc
index c6e5b41..7194ee4 100644
--- a/sandbox/src/service_resolver_64.cc
+++ b/sandbox/src/service_resolver_64.cc
@@ -5,8 +5,8 @@
#include "sandbox/src/service_resolver.h"
#include "base/logging.h"
-#include "base/pe_image.h"
#include "base/scoped_ptr.h"
+#include "sandbox/src/win_utils.h"
namespace {
#pragma pack(push, 1)
@@ -41,36 +41,6 @@ struct ServiceFullThunk {
#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 {
@@ -105,36 +75,6 @@ NTSTATUS ServiceResolverThunk::Setup(const void* target_module,
return ret;
}
-NTSTATUS ServiceResolverThunk::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 ServiceResolverThunk::ResolveTarget(const void* module,
- const char* function_name,
- void** address) {
- DCHECK(address);
- if (NULL == module)
- return STATUS_UNSUCCESSFUL;
-
- PEImage module_image(module);
- *address = module_image.GetProcAddress(function_name);
-
- if (NULL == *address) {
- NOTREACHED();
- return STATUS_UNSUCCESSFUL;
- }
-
- return STATUS_SUCCESS;
-}
-
size_t ServiceResolverThunk::GetThunkSize() const {
return sizeof(ServiceFullThunk);
}
diff --git a/sandbox/src/win_utils.cc b/sandbox/src/win_utils.cc
index bf4936b..8be9ce4 100644
--- a/sandbox/src/win_utils.cc
+++ b/sandbox/src/win_utils.cc
@@ -265,6 +265,26 @@ bool GetPathFromHandle(HANDLE handle, std::wstring* path) {
return true;
}
+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 sandbox
// TODO(cpu): This is not the final code we want here but we are yet
diff --git a/sandbox/src/win_utils.h b/sandbox/src/win_utils.h
index c52f3e3..8bc44b7 100644
--- a/sandbox/src/win_utils.h
+++ b/sandbox/src/win_utils.h
@@ -88,6 +88,12 @@ HKEY GetReservedKeyFromName(const std::wstring& name);
// cannot be resolved.
bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name);
+// Writes |length| bytes from the provided |buffer| into the address space of
+// |child_process|, at the specified |address|, preserving the original write
+// protection attributes. Returns true on success.
+bool WriteProtectedChildMemory(HANDLE child_process, void* address,
+ const void* buffer, size_t length);
+
} // namespace sandbox
// Resolves a function name in NTDLL to a function pointer. The second parameter