michael@0: // Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: // This file contains unit tests for ServiceResolverThunk. michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "base/memory/scoped_ptr.h" michael@0: #include "base/win/windows_version.h" michael@0: #include "sandbox/win/src/resolver.h" michael@0: #include "sandbox/win/src/sandbox_utils.h" michael@0: #include "sandbox/win/src/service_resolver.h" michael@0: #include "testing/gtest/include/gtest/gtest.h" michael@0: michael@0: namespace { michael@0: michael@0: // This is the concrete resolver used to perform service-call type functions michael@0: // inside ntdll.dll. michael@0: template michael@0: class ResolverThunkTest : public T { michael@0: public: michael@0: // The service resolver needs a child process to write to. michael@0: explicit ResolverThunkTest(bool relaxed) michael@0: : T(::GetCurrentProcess(), relaxed) {} michael@0: michael@0: // Sets the interception target to the desired address. michael@0: void set_target(void* target) { michael@0: fake_target_ = target; michael@0: } michael@0: michael@0: protected: michael@0: // Overrides Resolver::Init michael@0: virtual NTSTATUS Init(const void* target_module, michael@0: const void* interceptor_module, michael@0: const char* target_name, michael@0: const char* interceptor_name, michael@0: const void* interceptor_entry_point, michael@0: void* thunk_storage, michael@0: size_t storage_bytes) { michael@0: NTSTATUS ret = STATUS_SUCCESS; michael@0: ret = ResolverThunk::Init(target_module, interceptor_module, target_name, michael@0: interceptor_name, interceptor_entry_point, michael@0: thunk_storage, storage_bytes); michael@0: EXPECT_EQ(STATUS_SUCCESS, ret); michael@0: michael@0: target_ = fake_target_; michael@0: ntdll_base_ = ::GetModuleHandle(L"ntdll.dll"); michael@0: return ret; michael@0: }; michael@0: michael@0: private: michael@0: // Holds the address of the fake target. michael@0: void* fake_target_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(ResolverThunkTest); michael@0: }; michael@0: michael@0: typedef ResolverThunkTest WinXpResolverTest; michael@0: michael@0: #if !defined(_WIN64) michael@0: typedef ResolverThunkTest Win2kResolverTest; michael@0: typedef ResolverThunkTest Win8ResolverTest; michael@0: typedef ResolverThunkTest Wow64ResolverTest; michael@0: typedef ResolverThunkTest Wow64W8ResolverTest; michael@0: #endif michael@0: michael@0: const BYTE kJump32 = 0xE9; michael@0: michael@0: void CheckJump(void* source, void* target) { michael@0: #pragma pack(push) michael@0: #pragma pack(1) michael@0: struct Code { michael@0: BYTE jump; michael@0: ULONG delta; michael@0: }; michael@0: #pragma pack(pop) michael@0: michael@0: #if defined(_WIN64) michael@0: FAIL() << "Running 32-bit codepath"; michael@0: #else michael@0: Code* patched = reinterpret_cast(source); michael@0: EXPECT_EQ(kJump32, patched->jump); michael@0: michael@0: ULONG source_addr = bit_cast(source); michael@0: ULONG target_addr = bit_cast(target); michael@0: EXPECT_EQ(target_addr + 19 - source_addr, patched->delta); michael@0: #endif michael@0: } michael@0: michael@0: NTSTATUS PatchNtdllWithResolver(const char* function, bool relaxed, michael@0: sandbox::ServiceResolverThunk* resolver) { michael@0: HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll"); michael@0: EXPECT_TRUE(NULL != ntdll_base); michael@0: michael@0: void* target = ::GetProcAddress(ntdll_base, function); michael@0: EXPECT_TRUE(NULL != target); michael@0: if (NULL == target) michael@0: return STATUS_UNSUCCESSFUL; michael@0: michael@0: BYTE service[50]; michael@0: memcpy(service, target, sizeof(service)); michael@0: michael@0: static_cast(resolver)->set_target(service); michael@0: michael@0: // Any pointer will do as an interception_entry_point michael@0: void* function_entry = resolver; michael@0: size_t thunk_size = resolver->GetThunkSize(); michael@0: scoped_ptr thunk(new char[thunk_size]); michael@0: size_t used; michael@0: michael@0: NTSTATUS ret = resolver->Setup(ntdll_base, NULL, function, NULL, michael@0: function_entry, thunk.get(), thunk_size, michael@0: &used); michael@0: if (NT_SUCCESS(ret)) { michael@0: EXPECT_EQ(thunk_size, used); michael@0: EXPECT_NE(0, memcmp(service, target, sizeof(service))); michael@0: EXPECT_NE(kJump32, service[0]); michael@0: michael@0: if (relaxed) { michael@0: // It's already patched, let's patch again, and simulate a direct patch. michael@0: service[0] = kJump32; michael@0: ret = resolver->Setup(ntdll_base, NULL, function, NULL, function_entry, michael@0: thunk.get(), thunk_size, &used); michael@0: CheckJump(service, thunk.get()); michael@0: } michael@0: } michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: sandbox::ServiceResolverThunk* GetTestResolver(bool relaxed) { michael@0: #if defined(_WIN64) michael@0: return new WinXpResolverTest(relaxed); michael@0: #else michael@0: base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); michael@0: if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) { michael@0: if (os_info->version() >= base::win::VERSION_WIN8) michael@0: return new Wow64W8ResolverTest(relaxed); michael@0: return new Wow64ResolverTest(relaxed); michael@0: } michael@0: michael@0: if (!sandbox::IsXPSP2OrLater()) michael@0: return new Win2kResolverTest(relaxed); michael@0: michael@0: if (os_info->version() >= base::win::VERSION_WIN8) michael@0: return new Win8ResolverTest(relaxed); michael@0: michael@0: return new WinXpResolverTest(relaxed); michael@0: #endif michael@0: } michael@0: michael@0: NTSTATUS PatchNtdll(const char* function, bool relaxed) { michael@0: sandbox::ServiceResolverThunk* resolver = GetTestResolver(relaxed); michael@0: michael@0: NTSTATUS ret = PatchNtdllWithResolver(function, relaxed, resolver); michael@0: delete resolver; michael@0: return ret; michael@0: } michael@0: michael@0: TEST(ServiceResolverTest, PatchesServices) { michael@0: NTSTATUS ret = PatchNtdll("NtClose", false); michael@0: EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); michael@0: michael@0: ret = PatchNtdll("NtCreateFile", false); michael@0: EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << michael@0: ::GetLastError(); michael@0: michael@0: ret = PatchNtdll("NtCreateMutant", false); michael@0: EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << michael@0: ::GetLastError(); michael@0: michael@0: ret = PatchNtdll("NtMapViewOfSection", false); michael@0: EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << michael@0: ::GetLastError(); michael@0: } michael@0: michael@0: TEST(ServiceResolverTest, FailsIfNotService) { michael@0: #if !defined(_WIN64) michael@0: EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false)); michael@0: #endif michael@0: michael@0: EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false)); michael@0: } michael@0: michael@0: TEST(ServiceResolverTest, PatchesPatchedServices) { michael@0: // We don't support "relaxed mode" for Win64 apps. michael@0: #if !defined(_WIN64) michael@0: NTSTATUS ret = PatchNtdll("NtClose", true); michael@0: EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); michael@0: michael@0: ret = PatchNtdll("NtCreateFile", true); michael@0: EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << michael@0: ::GetLastError(); michael@0: michael@0: ret = PatchNtdll("NtCreateMutant", true); michael@0: EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << michael@0: ::GetLastError(); michael@0: michael@0: ret = PatchNtdll("NtMapViewOfSection", true); michael@0: EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << michael@0: ::GetLastError(); michael@0: #endif michael@0: } michael@0: michael@0: TEST(ServiceResolverTest, MultiplePatchedServices) { michael@0: // We don't support "relaxed mode" for Win64 apps. michael@0: #if !defined(_WIN64) michael@0: sandbox::ServiceResolverThunk* resolver = GetTestResolver(true); michael@0: NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, resolver); michael@0: EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); michael@0: michael@0: ret = PatchNtdllWithResolver("NtCreateFile", true, resolver); michael@0: EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << michael@0: ::GetLastError(); michael@0: michael@0: ret = PatchNtdllWithResolver("NtCreateMutant", true, resolver); michael@0: EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << michael@0: ::GetLastError(); michael@0: michael@0: ret = PatchNtdllWithResolver("NtMapViewOfSection", true, resolver); michael@0: EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << michael@0: ::GetLastError(); michael@0: delete resolver; michael@0: #endif michael@0: } michael@0: michael@0: } // namespace