summaryrefslogtreecommitdiffstats
path: root/sandbox/src/service_resolver.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 22:41:28 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 22:41:28 +0000
commita814a8d55429605fe6d7045045cd25b6bf624580 (patch)
tree58fcd994d4ce41ef021f6406a6fac32d9ca2d265 /sandbox/src/service_resolver.cc
parente6c9e14e0dfec2bb156a1f7a107cda3ebee8d392 (diff)
downloadchromium_src-a814a8d55429605fe6d7045045cd25b6bf624580.zip
chromium_src-a814a8d55429605fe6d7045045cd25b6bf624580.tar.gz
chromium_src-a814a8d55429605fe6d7045045cd25b6bf624580.tar.bz2
Add sandbox to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox/src/service_resolver.cc')
-rw-r--r--sandbox/src/service_resolver.cc391
1 files changed, 391 insertions, 0 deletions
diff --git a/sandbox/src/service_resolver.cc b/sandbox/src/service_resolver.cc
new file mode 100644
index 0000000..7d07adb
--- /dev/null
+++ b/sandbox/src/service_resolver.cc
@@ -0,0 +1,391 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "sandbox/src/service_resolver.h"
+
+#include "base/logging.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 ULONG kCallFs2Ret = 0xC2000000;
+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:
+ // mov eax,25h
+ // mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
+ // call dword ptr [edx]
+ // ret 2Ch
+ // 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 a 32 bit process running on a 64 bit os.
+struct Wow64Entry {
+ // this struct contains roughly the following code:
+ // 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
+ 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
+ ULONG call_fs2_ret; // = 00 00 00 C2
+ USHORT num_params;
+};
+
+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;
+
+ DWORD 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_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;
+}
+
+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;
+
+ *address = ::GetProcAddress(bit_cast<HMODULE>(module), function_name);
+
+ if (NULL == *address)
+ 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;
+ DWORD 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
+ 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
+ DWORD 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;
+ DWORD 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;
+ DWORD 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 ||
+ kCallFs2Ret != function_code.call_fs2_ret)
+ return false;
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const {
+ ServiceEntry function_code;
+ DWORD 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