summaryrefslogtreecommitdiffstats
path: root/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc')
-rw-r--r--security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc278
1 files changed, 278 insertions, 0 deletions
diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc b/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc
new file mode 100644
index 0000000000..63ea26dcbf
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc
@@ -0,0 +1,278 @@
+// 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.
+
+// This file contains unit tests for ServiceResolverThunk.
+
+#include "sandbox/win/src/service_resolver.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/bit_cast.h"
+#include "base/macros.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/resolver.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class ResolverThunkTest {
+ public:
+ virtual ~ResolverThunkTest() {}
+
+ virtual sandbox::ServiceResolverThunk* resolver() = 0;
+
+ // Sets the interception target to the desired address.
+ void set_target(void* target) { fake_target_ = target; }
+
+ protected:
+ // Holds the address of the fake target.
+ void* fake_target_;
+};
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll.
+template <typename T>
+class ResolverThunkTestImpl : public T, public ResolverThunkTest {
+ public:
+ // The service resolver needs a child process to write to.
+ explicit ResolverThunkTestImpl(bool relaxed)
+ : T(::GetCurrentProcess(), relaxed) {}
+
+ sandbox::ServiceResolverThunk* resolver() { return this; }
+
+ protected:
+ // Overrides Resolver::Init
+ virtual NTSTATUS 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) {
+ NTSTATUS ret = STATUS_SUCCESS;
+ ret = T::Init(target_module, interceptor_module, target_name,
+ interceptor_name, interceptor_entry_point, thunk_storage,
+ storage_bytes);
+ EXPECT_EQ(STATUS_SUCCESS, ret);
+
+ this->target_ = fake_target_;
+
+ return ret;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ResolverThunkTestImpl);
+};
+
+typedef ResolverThunkTestImpl<sandbox::ServiceResolverThunk> WinXpResolverTest;
+
+#if !defined(_WIN64)
+typedef ResolverThunkTestImpl<sandbox::Win8ResolverThunk> Win8ResolverTest;
+typedef ResolverThunkTestImpl<sandbox::Wow64ResolverThunk> Wow64ResolverTest;
+typedef ResolverThunkTestImpl<sandbox::Wow64W8ResolverThunk>
+ Wow64W8ResolverTest;
+typedef ResolverThunkTestImpl<sandbox::Wow64W10ResolverThunk>
+ Wow64W10ResolverTest;
+#endif
+
+const BYTE kJump32 = 0xE9;
+
+void CheckJump(void* source, void* target) {
+#pragma pack(push)
+#pragma pack(1)
+ struct Code {
+ BYTE jump;
+ ULONG delta;
+ };
+#pragma pack(pop)
+
+#if defined(_WIN64)
+ FAIL() << "Running 32-bit codepath";
+#else
+ Code* patched = reinterpret_cast<Code*>(source);
+ EXPECT_EQ(kJump32, patched->jump);
+
+ ULONG source_addr = bit_cast<ULONG>(source);
+ ULONG target_addr = bit_cast<ULONG>(target);
+ EXPECT_EQ(target_addr + 19 - source_addr, patched->delta);
+#endif
+}
+
+NTSTATUS PatchNtdllWithResolver(const char* function,
+ bool relaxed,
+ ResolverThunkTest* thunk_test) {
+ HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
+ EXPECT_TRUE(ntdll_base);
+
+ void* target =
+ reinterpret_cast<void*>(::GetProcAddress(ntdll_base, function));
+ EXPECT_TRUE(target);
+ if (!target)
+ return STATUS_UNSUCCESSFUL;
+
+ BYTE service[50];
+ memcpy(service, target, sizeof(service));
+
+ thunk_test->set_target(service);
+
+ sandbox::ServiceResolverThunk* resolver = thunk_test->resolver();
+ // Any pointer will do as an interception_entry_point
+ void* function_entry = resolver;
+ size_t thunk_size = resolver->GetThunkSize();
+ std::unique_ptr<char[]> thunk(new char[thunk_size]);
+ size_t used;
+
+ resolver->AllowLocalPatches();
+
+ NTSTATUS ret =
+ resolver->Setup(ntdll_base, nullptr, function, nullptr, function_entry,
+ thunk.get(), thunk_size, &used);
+ if (NT_SUCCESS(ret)) {
+ EXPECT_EQ(thunk_size, used);
+ EXPECT_NE(0, memcmp(service, target, sizeof(service)));
+ EXPECT_NE(kJump32, service[0]);
+
+ if (relaxed) {
+ // It's already patched, let's patch again, and simulate a direct patch.
+ service[0] = kJump32;
+ ret = resolver->Setup(ntdll_base, nullptr, function, nullptr,
+ function_entry, thunk.get(), thunk_size, &used);
+ CheckJump(service, thunk.get());
+ }
+ }
+
+ return ret;
+}
+
+std::unique_ptr<ResolverThunkTest> GetTestResolver(bool relaxed) {
+#if defined(_WIN64)
+ return std::make_unique<WinXpResolverTest>(relaxed);
+#else
+ base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
+ if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) {
+ if (os_info->version() >= base::win::Version::WIN10)
+ return std::make_unique<Wow64W10ResolverTest>(relaxed);
+ if (os_info->version() >= base::win::Version::WIN8)
+ return std::make_unique<Wow64W8ResolverTest>(relaxed);
+ return std::make_unique<Wow64ResolverTest>(relaxed);
+ }
+
+ if (os_info->version() >= base::win::Version::WIN8)
+ return std::make_unique<Win8ResolverTest>(relaxed);
+
+ return std::make_unique<WinXpResolverTest>(relaxed);
+#endif
+}
+
+NTSTATUS PatchNtdll(const char* function, bool relaxed) {
+ std::unique_ptr<ResolverThunkTest> thunk_test = GetTestResolver(relaxed);
+ return PatchNtdllWithResolver(function, relaxed, thunk_test.get());
+}
+
+TEST(ServiceResolverTest, PatchesServices) {
+ NTSTATUS ret = PatchNtdll("NtClose", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateFile", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateFile, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateMutant", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateMutant, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtMapViewOfSection", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtMapViewOfSection, last error: " << ::GetLastError();
+}
+
+TEST(ServiceResolverTest, FailsIfNotService) {
+#if !defined(_WIN64)
+ EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false));
+#endif
+
+ EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false));
+}
+
+TEST(ServiceResolverTest, PatchesPatchedServices) {
+// We don't support "relaxed mode" for Win64 apps.
+#if !defined(_WIN64)
+ NTSTATUS ret = PatchNtdll("NtClose", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateFile", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateFile, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateMutant", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateMutant, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtMapViewOfSection", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtMapViewOfSection, last error: " << ::GetLastError();
+#endif
+}
+
+TEST(ServiceResolverTest, MultiplePatchedServices) {
+// We don't support "relaxed mode" for Win64 apps.
+#if !defined(_WIN64)
+ std::unique_ptr<ResolverThunkTest> thunk_test = GetTestResolver(true);
+ NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, thunk_test.get());
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
+
+ ret = PatchNtdllWithResolver("NtCreateFile", true, thunk_test.get());
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateFile, last error: " << ::GetLastError();
+
+ ret = PatchNtdllWithResolver("NtCreateMutant", true, thunk_test.get());
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateMutant, last error: " << ::GetLastError();
+
+ ret = PatchNtdllWithResolver("NtMapViewOfSection", true, thunk_test.get());
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtMapViewOfSection, last error: " << ::GetLastError();
+#endif
+}
+
+TEST(ServiceResolverTest, LocalPatchesAllowed) {
+ std::unique_ptr<ResolverThunkTest> thunk_test = GetTestResolver(true);
+
+ HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
+ ASSERT_TRUE(ntdll_base);
+
+ const char kFunctionName[] = "NtClose";
+
+ void* target =
+ reinterpret_cast<void*>(::GetProcAddress(ntdll_base, kFunctionName));
+ ASSERT_TRUE(target);
+
+ BYTE service[50];
+ memcpy(service, target, sizeof(service));
+ thunk_test->set_target(service);
+
+ sandbox::ServiceResolverThunk* resolver = thunk_test->resolver();
+ // Any pointer will do as an interception_entry_point
+ void* function_entry = resolver;
+ size_t thunk_size = resolver->GetThunkSize();
+ std::unique_ptr<char[]> thunk(new char[thunk_size]);
+ size_t used;
+
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+
+ // First try patching without having allowed local patches.
+ ret = resolver->Setup(ntdll_base, nullptr, kFunctionName, nullptr,
+ function_entry, thunk.get(), thunk_size, &used);
+ EXPECT_FALSE(NT_SUCCESS(ret));
+
+ // Now allow local patches and check that things work.
+ resolver->AllowLocalPatches();
+ ret = resolver->Setup(ntdll_base, nullptr, kFunctionName, nullptr,
+ function_entry, thunk.get(), thunk_size, &used);
+ EXPECT_EQ(STATUS_SUCCESS, ret);
+}
+
+} // namespace