1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// This file contains unit tests for ServiceResolverThunk. 6 7#include "base/basictypes.h" 8#include "base/memory/scoped_ptr.h" 9#include "base/win/windows_version.h" 10#include "sandbox/win/src/resolver.h" 11#include "sandbox/win/src/sandbox_utils.h" 12#include "sandbox/win/src/service_resolver.h" 13#include "testing/gtest/include/gtest/gtest.h" 14 15namespace { 16 17// This is the concrete resolver used to perform service-call type functions 18// inside ntdll.dll. 19template<typename T> 20class ResolverThunkTest : public T { 21 public: 22 // The service resolver needs a child process to write to. 23 explicit ResolverThunkTest(bool relaxed) 24 : T(::GetCurrentProcess(), relaxed) {} 25 26 // Sets the interception target to the desired address. 27 void set_target(void* target) { 28 fake_target_ = target; 29 } 30 31 protected: 32 // Overrides Resolver::Init 33 virtual NTSTATUS Init(const void* target_module, 34 const void* interceptor_module, 35 const char* target_name, 36 const char* interceptor_name, 37 const void* interceptor_entry_point, 38 void* thunk_storage, 39 size_t storage_bytes) { 40 NTSTATUS ret = STATUS_SUCCESS; 41 ret = ResolverThunk::Init(target_module, interceptor_module, target_name, 42 interceptor_name, interceptor_entry_point, 43 thunk_storage, storage_bytes); 44 EXPECT_EQ(STATUS_SUCCESS, ret); 45 46 target_ = fake_target_; 47 48 return ret; 49 }; 50 51 private: 52 // Holds the address of the fake target. 53 void* fake_target_; 54 55 DISALLOW_COPY_AND_ASSIGN(ResolverThunkTest); 56}; 57 58typedef ResolverThunkTest<sandbox::ServiceResolverThunk> WinXpResolverTest; 59 60#if !defined(_WIN64) 61typedef ResolverThunkTest<sandbox::Win8ResolverThunk> Win8ResolverTest; 62typedef ResolverThunkTest<sandbox::Wow64ResolverThunk> Wow64ResolverTest; 63typedef ResolverThunkTest<sandbox::Wow64W8ResolverThunk> Wow64W8ResolverTest; 64#endif 65 66const BYTE kJump32 = 0xE9; 67 68void CheckJump(void* source, void* target) { 69#pragma pack(push) 70#pragma pack(1) 71 struct Code { 72 BYTE jump; 73 ULONG delta; 74 }; 75#pragma pack(pop) 76 77#if defined(_WIN64) 78 FAIL() << "Running 32-bit codepath"; 79#else 80 Code* patched = reinterpret_cast<Code*>(source); 81 EXPECT_EQ(kJump32, patched->jump); 82 83 ULONG source_addr = bit_cast<ULONG>(source); 84 ULONG target_addr = bit_cast<ULONG>(target); 85 EXPECT_EQ(target_addr + 19 - source_addr, patched->delta); 86#endif 87} 88 89NTSTATUS PatchNtdllWithResolver(const char* function, bool relaxed, 90 sandbox::ServiceResolverThunk* resolver) { 91 HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll"); 92 EXPECT_TRUE(NULL != ntdll_base); 93 94 void* target = ::GetProcAddress(ntdll_base, function); 95 EXPECT_TRUE(NULL != target); 96 if (NULL == target) 97 return STATUS_UNSUCCESSFUL; 98 99 BYTE service[50]; 100 memcpy(service, target, sizeof(service)); 101 102 static_cast<WinXpResolverTest*>(resolver)->set_target(service); 103 104 // Any pointer will do as an interception_entry_point 105 void* function_entry = resolver; 106 size_t thunk_size = resolver->GetThunkSize(); 107 scoped_ptr<char[]> thunk(new char[thunk_size]); 108 size_t used; 109 110 resolver->AllowLocalPatches(); 111 112 NTSTATUS ret = resolver->Setup(ntdll_base, NULL, function, NULL, 113 function_entry, thunk.get(), thunk_size, 114 &used); 115 if (NT_SUCCESS(ret)) { 116 EXPECT_EQ(thunk_size, used); 117 EXPECT_NE(0, memcmp(service, target, sizeof(service))); 118 EXPECT_NE(kJump32, service[0]); 119 120 if (relaxed) { 121 // It's already patched, let's patch again, and simulate a direct patch. 122 service[0] = kJump32; 123 ret = resolver->Setup(ntdll_base, NULL, function, NULL, function_entry, 124 thunk.get(), thunk_size, &used); 125 CheckJump(service, thunk.get()); 126 } 127 } 128 129 return ret; 130} 131 132sandbox::ServiceResolverThunk* GetTestResolver(bool relaxed) { 133#if defined(_WIN64) 134 return new WinXpResolverTest(relaxed); 135#else 136 base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); 137 if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) { 138 if (os_info->version() >= base::win::VERSION_WIN8) 139 return new Wow64W8ResolverTest(relaxed); 140 return new Wow64ResolverTest(relaxed); 141 } 142 143 if (os_info->version() >= base::win::VERSION_WIN8) 144 return new Win8ResolverTest(relaxed); 145 146 return new WinXpResolverTest(relaxed); 147#endif 148} 149 150NTSTATUS PatchNtdll(const char* function, bool relaxed) { 151 sandbox::ServiceResolverThunk* resolver = GetTestResolver(relaxed); 152 153 NTSTATUS ret = PatchNtdllWithResolver(function, relaxed, resolver); 154 delete resolver; 155 return ret; 156} 157 158TEST(ServiceResolverTest, PatchesServices) { 159 NTSTATUS ret = PatchNtdll("NtClose", false); 160 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); 161 162 ret = PatchNtdll("NtCreateFile", false); 163 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << 164 ::GetLastError(); 165 166 ret = PatchNtdll("NtCreateMutant", false); 167 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << 168 ::GetLastError(); 169 170 ret = PatchNtdll("NtMapViewOfSection", false); 171 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << 172 ::GetLastError(); 173} 174 175TEST(ServiceResolverTest, FailsIfNotService) { 176#if !defined(_WIN64) 177 EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false)); 178#endif 179 180 EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false)); 181} 182 183TEST(ServiceResolverTest, PatchesPatchedServices) { 184// We don't support "relaxed mode" for Win64 apps. 185#if !defined(_WIN64) 186 NTSTATUS ret = PatchNtdll("NtClose", true); 187 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); 188 189 ret = PatchNtdll("NtCreateFile", true); 190 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << 191 ::GetLastError(); 192 193 ret = PatchNtdll("NtCreateMutant", true); 194 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << 195 ::GetLastError(); 196 197 ret = PatchNtdll("NtMapViewOfSection", true); 198 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << 199 ::GetLastError(); 200#endif 201} 202 203TEST(ServiceResolverTest, MultiplePatchedServices) { 204// We don't support "relaxed mode" for Win64 apps. 205#if !defined(_WIN64) 206 sandbox::ServiceResolverThunk* resolver = GetTestResolver(true); 207 NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, resolver); 208 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); 209 210 ret = PatchNtdllWithResolver("NtCreateFile", true, resolver); 211 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << 212 ::GetLastError(); 213 214 ret = PatchNtdllWithResolver("NtCreateMutant", true, resolver); 215 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << 216 ::GetLastError(); 217 218 ret = PatchNtdllWithResolver("NtMapViewOfSection", true, resolver); 219 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << 220 ::GetLastError(); 221 delete resolver; 222#endif 223} 224 225TEST(ServiceResolverTest, LocalPatchesAllowed) { 226 sandbox::ServiceResolverThunk* resolver = GetTestResolver(true); 227 228 HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll"); 229 ASSERT_TRUE(NULL != ntdll_base); 230 231 const char kFunctionName[] = "NtClose"; 232 233 void* target = ::GetProcAddress(ntdll_base, kFunctionName); 234 ASSERT_TRUE(NULL != target); 235 236 BYTE service[50]; 237 memcpy(service, target, sizeof(service)); 238 static_cast<WinXpResolverTest*>(resolver)->set_target(service); 239 240 // Any pointer will do as an interception_entry_point 241 void* function_entry = resolver; 242 size_t thunk_size = resolver->GetThunkSize(); 243 scoped_ptr<char[]> thunk(new char[thunk_size]); 244 size_t used; 245 246 NTSTATUS ret = STATUS_UNSUCCESSFUL; 247 248 // First try patching without having allowed local patches. 249 ret = resolver->Setup(ntdll_base, NULL, kFunctionName, NULL, 250 function_entry, thunk.get(), thunk_size, 251 &used); 252 EXPECT_FALSE(NT_SUCCESS(ret)); 253 254 // Now allow local patches and check that things work. 255 resolver->AllowLocalPatches(); 256 ret = resolver->Setup(ntdll_base, NULL, kFunctionName, NULL, 257 function_entry, thunk.get(), thunk_size, 258 &used); 259 EXPECT_EQ(STATUS_SUCCESS, ret); 260} 261 262} // namespace 263