1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/sandbox/win/src/service_resolver_unittest.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,227 @@ 1.4 +// Copyright (c) 2012 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +// This file contains unit tests for ServiceResolverThunk. 1.9 + 1.10 +#include "base/basictypes.h" 1.11 +#include "base/memory/scoped_ptr.h" 1.12 +#include "base/win/windows_version.h" 1.13 +#include "sandbox/win/src/resolver.h" 1.14 +#include "sandbox/win/src/sandbox_utils.h" 1.15 +#include "sandbox/win/src/service_resolver.h" 1.16 +#include "testing/gtest/include/gtest/gtest.h" 1.17 + 1.18 +namespace { 1.19 + 1.20 +// This is the concrete resolver used to perform service-call type functions 1.21 +// inside ntdll.dll. 1.22 +template<typename T> 1.23 +class ResolverThunkTest : public T { 1.24 + public: 1.25 + // The service resolver needs a child process to write to. 1.26 + explicit ResolverThunkTest(bool relaxed) 1.27 + : T(::GetCurrentProcess(), relaxed) {} 1.28 + 1.29 + // Sets the interception target to the desired address. 1.30 + void set_target(void* target) { 1.31 + fake_target_ = target; 1.32 + } 1.33 + 1.34 + protected: 1.35 + // Overrides Resolver::Init 1.36 + virtual NTSTATUS Init(const void* target_module, 1.37 + const void* interceptor_module, 1.38 + const char* target_name, 1.39 + const char* interceptor_name, 1.40 + const void* interceptor_entry_point, 1.41 + void* thunk_storage, 1.42 + size_t storage_bytes) { 1.43 + NTSTATUS ret = STATUS_SUCCESS; 1.44 + ret = ResolverThunk::Init(target_module, interceptor_module, target_name, 1.45 + interceptor_name, interceptor_entry_point, 1.46 + thunk_storage, storage_bytes); 1.47 + EXPECT_EQ(STATUS_SUCCESS, ret); 1.48 + 1.49 + target_ = fake_target_; 1.50 + ntdll_base_ = ::GetModuleHandle(L"ntdll.dll"); 1.51 + return ret; 1.52 + }; 1.53 + 1.54 + private: 1.55 + // Holds the address of the fake target. 1.56 + void* fake_target_; 1.57 + 1.58 + DISALLOW_COPY_AND_ASSIGN(ResolverThunkTest); 1.59 +}; 1.60 + 1.61 +typedef ResolverThunkTest<sandbox::ServiceResolverThunk> WinXpResolverTest; 1.62 + 1.63 +#if !defined(_WIN64) 1.64 +typedef ResolverThunkTest<sandbox::Win2kResolverThunk> Win2kResolverTest; 1.65 +typedef ResolverThunkTest<sandbox::Win8ResolverThunk> Win8ResolverTest; 1.66 +typedef ResolverThunkTest<sandbox::Wow64ResolverThunk> Wow64ResolverTest; 1.67 +typedef ResolverThunkTest<sandbox::Wow64W8ResolverThunk> Wow64W8ResolverTest; 1.68 +#endif 1.69 + 1.70 +const BYTE kJump32 = 0xE9; 1.71 + 1.72 +void CheckJump(void* source, void* target) { 1.73 +#pragma pack(push) 1.74 +#pragma pack(1) 1.75 + struct Code { 1.76 + BYTE jump; 1.77 + ULONG delta; 1.78 + }; 1.79 +#pragma pack(pop) 1.80 + 1.81 +#if defined(_WIN64) 1.82 + FAIL() << "Running 32-bit codepath"; 1.83 +#else 1.84 + Code* patched = reinterpret_cast<Code*>(source); 1.85 + EXPECT_EQ(kJump32, patched->jump); 1.86 + 1.87 + ULONG source_addr = bit_cast<ULONG>(source); 1.88 + ULONG target_addr = bit_cast<ULONG>(target); 1.89 + EXPECT_EQ(target_addr + 19 - source_addr, patched->delta); 1.90 +#endif 1.91 +} 1.92 + 1.93 +NTSTATUS PatchNtdllWithResolver(const char* function, bool relaxed, 1.94 + sandbox::ServiceResolverThunk* resolver) { 1.95 + HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll"); 1.96 + EXPECT_TRUE(NULL != ntdll_base); 1.97 + 1.98 + void* target = ::GetProcAddress(ntdll_base, function); 1.99 + EXPECT_TRUE(NULL != target); 1.100 + if (NULL == target) 1.101 + return STATUS_UNSUCCESSFUL; 1.102 + 1.103 + BYTE service[50]; 1.104 + memcpy(service, target, sizeof(service)); 1.105 + 1.106 + static_cast<WinXpResolverTest*>(resolver)->set_target(service); 1.107 + 1.108 + // Any pointer will do as an interception_entry_point 1.109 + void* function_entry = resolver; 1.110 + size_t thunk_size = resolver->GetThunkSize(); 1.111 + scoped_ptr<char[]> thunk(new char[thunk_size]); 1.112 + size_t used; 1.113 + 1.114 + NTSTATUS ret = resolver->Setup(ntdll_base, NULL, function, NULL, 1.115 + function_entry, thunk.get(), thunk_size, 1.116 + &used); 1.117 + if (NT_SUCCESS(ret)) { 1.118 + EXPECT_EQ(thunk_size, used); 1.119 + EXPECT_NE(0, memcmp(service, target, sizeof(service))); 1.120 + EXPECT_NE(kJump32, service[0]); 1.121 + 1.122 + if (relaxed) { 1.123 + // It's already patched, let's patch again, and simulate a direct patch. 1.124 + service[0] = kJump32; 1.125 + ret = resolver->Setup(ntdll_base, NULL, function, NULL, function_entry, 1.126 + thunk.get(), thunk_size, &used); 1.127 + CheckJump(service, thunk.get()); 1.128 + } 1.129 + } 1.130 + 1.131 + return ret; 1.132 +} 1.133 + 1.134 +sandbox::ServiceResolverThunk* GetTestResolver(bool relaxed) { 1.135 +#if defined(_WIN64) 1.136 + return new WinXpResolverTest(relaxed); 1.137 +#else 1.138 + base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); 1.139 + if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) { 1.140 + if (os_info->version() >= base::win::VERSION_WIN8) 1.141 + return new Wow64W8ResolverTest(relaxed); 1.142 + return new Wow64ResolverTest(relaxed); 1.143 + } 1.144 + 1.145 + if (!sandbox::IsXPSP2OrLater()) 1.146 + return new Win2kResolverTest(relaxed); 1.147 + 1.148 + if (os_info->version() >= base::win::VERSION_WIN8) 1.149 + return new Win8ResolverTest(relaxed); 1.150 + 1.151 + return new WinXpResolverTest(relaxed); 1.152 +#endif 1.153 +} 1.154 + 1.155 +NTSTATUS PatchNtdll(const char* function, bool relaxed) { 1.156 + sandbox::ServiceResolverThunk* resolver = GetTestResolver(relaxed); 1.157 + 1.158 + NTSTATUS ret = PatchNtdllWithResolver(function, relaxed, resolver); 1.159 + delete resolver; 1.160 + return ret; 1.161 +} 1.162 + 1.163 +TEST(ServiceResolverTest, PatchesServices) { 1.164 + NTSTATUS ret = PatchNtdll("NtClose", false); 1.165 + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); 1.166 + 1.167 + ret = PatchNtdll("NtCreateFile", false); 1.168 + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << 1.169 + ::GetLastError(); 1.170 + 1.171 + ret = PatchNtdll("NtCreateMutant", false); 1.172 + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << 1.173 + ::GetLastError(); 1.174 + 1.175 + ret = PatchNtdll("NtMapViewOfSection", false); 1.176 + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << 1.177 + ::GetLastError(); 1.178 +} 1.179 + 1.180 +TEST(ServiceResolverTest, FailsIfNotService) { 1.181 +#if !defined(_WIN64) 1.182 + EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false)); 1.183 +#endif 1.184 + 1.185 + EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false)); 1.186 +} 1.187 + 1.188 +TEST(ServiceResolverTest, PatchesPatchedServices) { 1.189 +// We don't support "relaxed mode" for Win64 apps. 1.190 +#if !defined(_WIN64) 1.191 + NTSTATUS ret = PatchNtdll("NtClose", true); 1.192 + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); 1.193 + 1.194 + ret = PatchNtdll("NtCreateFile", true); 1.195 + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << 1.196 + ::GetLastError(); 1.197 + 1.198 + ret = PatchNtdll("NtCreateMutant", true); 1.199 + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << 1.200 + ::GetLastError(); 1.201 + 1.202 + ret = PatchNtdll("NtMapViewOfSection", true); 1.203 + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << 1.204 + ::GetLastError(); 1.205 +#endif 1.206 +} 1.207 + 1.208 +TEST(ServiceResolverTest, MultiplePatchedServices) { 1.209 +// We don't support "relaxed mode" for Win64 apps. 1.210 +#if !defined(_WIN64) 1.211 + sandbox::ServiceResolverThunk* resolver = GetTestResolver(true); 1.212 + NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, resolver); 1.213 + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); 1.214 + 1.215 + ret = PatchNtdllWithResolver("NtCreateFile", true, resolver); 1.216 + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << 1.217 + ::GetLastError(); 1.218 + 1.219 + ret = PatchNtdllWithResolver("NtCreateMutant", true, resolver); 1.220 + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << 1.221 + ::GetLastError(); 1.222 + 1.223 + ret = PatchNtdllWithResolver("NtMapViewOfSection", true, resolver); 1.224 + EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << 1.225 + ::GetLastError(); 1.226 + delete resolver; 1.227 +#endif 1.228 +} 1.229 + 1.230 +} // namespace