1ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Use of this source code is governed by a BSD-style license that can be 3ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// found in the LICENSE file. 4ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 5ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// This file contains unit tests for ServiceResolverThunk. 6ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 7ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/basictypes.h" 8ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/memory/scoped_ptr.h" 9ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/win/windows_version.h" 10ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "sandbox/win/src/resolver.h" 11ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "sandbox/win/src/sandbox_utils.h" 12ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "sandbox/win/src/service_resolver.h" 13ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "testing/gtest/include/gtest/gtest.h" 14ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 15ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovnamespace { 16ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 17ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// This is the concrete resolver used to perform service-call type functions 18ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// inside ntdll.dll. 19ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovtemplate<typename T> 20ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovclass ResolverThunkTest : public T { 21ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov public: 22ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // The service resolver needs a child process to write to. 23ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov explicit ResolverThunkTest(bool relaxed) 24ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov : T(::GetCurrentProcess(), relaxed) {} 25ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 26ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Sets the interception target to the desired address. 27ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov void set_target(void* target) { 28ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov fake_target_ = target; 29ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 30ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 31ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov protected: 32ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Overrides Resolver::Init 33ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov virtual NTSTATUS Init(const void* target_module, 34ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const void* interceptor_module, 35ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const char* target_name, 36ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const char* interceptor_name, 37ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const void* interceptor_entry_point, 38ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov void* thunk_storage, 39ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov size_t storage_bytes) { 40ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov NTSTATUS ret = STATUS_SUCCESS; 41ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ret = ResolverThunk::Init(target_module, interceptor_module, target_name, 42ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov interceptor_name, interceptor_entry_point, 43ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov thunk_storage, storage_bytes); 44ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov EXPECT_EQ(STATUS_SUCCESS, ret); 45ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 46ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov target_ = fake_target_; 47ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 48ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return ret; 49ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov }; 50ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 51ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov private: 52ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Holds the address of the fake target. 53ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov void* fake_target_; 54ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 55ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov DISALLOW_COPY_AND_ASSIGN(ResolverThunkTest); 56ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}; 57ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 58ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovtypedef ResolverThunkTest<sandbox::ServiceResolverThunk> WinXpResolverTest; 59ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 60ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#if !defined(_WIN64) 61ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovtypedef ResolverThunkTest<sandbox::Win8ResolverThunk> Win8ResolverTest; 62ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovtypedef ResolverThunkTest<sandbox::Wow64ResolverThunk> Wow64ResolverTest; 63ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovtypedef ResolverThunkTest<sandbox::Wow64W8ResolverThunk> Wow64W8ResolverTest; 64ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#endif 65ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 66ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovconst BYTE kJump32 = 0xE9; 67ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 68ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovvoid CheckJump(void* source, void* target) { 69ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#pragma pack(push) 70ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#pragma pack(1) 71ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov struct Code { 72ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov BYTE jump; 73ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ULONG delta; 74ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov }; 75ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#pragma pack(pop) 76ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 77ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#if defined(_WIN64) 78ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FAIL() << "Running 32-bit codepath"; 79ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#else 80ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Code* patched = reinterpret_cast<Code*>(source); 81ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov EXPECT_EQ(kJump32, patched->jump); 82ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 83ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ULONG source_addr = bit_cast<ULONG>(source); 84ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ULONG target_addr = bit_cast<ULONG>(target); 85ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov EXPECT_EQ(target_addr + 19 - source_addr, patched->delta); 86ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#endif 87ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 88ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 89ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovNTSTATUS PatchNtdllWithResolver(const char* function, bool relaxed, 90ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov sandbox::ServiceResolverThunk* resolver) { 91ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll"); 92ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov EXPECT_TRUE(NULL != ntdll_base); 93ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 94ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov void* target = ::GetProcAddress(ntdll_base, function); 95ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov EXPECT_TRUE(NULL != target); 96ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (NULL == target) 97ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return STATUS_UNSUCCESSFUL; 98ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 99ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov BYTE service[50]; 100ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov memcpy(service, target, sizeof(service)); 101ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 102ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static_cast<WinXpResolverTest*>(resolver)->set_target(service); 103ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 104ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Any pointer will do as an interception_entry_point 105ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov void* function_entry = resolver; 106ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov size_t thunk_size = resolver->GetThunkSize(); 107ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov scoped_ptr<char[]> thunk(new char[thunk_size]); 108ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov size_t used; 109ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 110ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov resolver->AllowLocalPatches(); 111ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 112ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov NTSTATUS ret = resolver->Setup(ntdll_base, NULL, function, NULL, 113ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov function_entry, thunk.get(), thunk_size, 114ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &used); 115ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (NT_SUCCESS(ret)) { 116ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov EXPECT_EQ(thunk_size, used); 117ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov EXPECT_NE(0, memcmp(service, target, sizeof(service))); 118ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov EXPECT_NE(kJump32, service[0]); 119ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 120ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (relaxed) { 121ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // It's already patched, let's patch again, and simulate a direct patch. 122ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov service[0] = kJump32; 123ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ret = resolver->Setup(ntdll_base, NULL, function, NULL, function_entry, 124ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov thunk.get(), thunk_size, &used); 125ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CheckJump(service, thunk.get()); 126ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 127ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 128ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 129ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return ret; 130ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 131ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 132ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovsandbox::ServiceResolverThunk* GetTestResolver(bool relaxed) { 133ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#if defined(_WIN64) 134ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return new WinXpResolverTest(relaxed); 135ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#else 136ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); 137ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) { 138ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (os_info->version() >= base::win::VERSION_WIN8) 139ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return new Wow64W8ResolverTest(relaxed); 140ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return new Wow64ResolverTest(relaxed); 141ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 142ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 143ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (os_info->version() >= base::win::VERSION_WIN8) 144ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return new Win8ResolverTest(relaxed); 145ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 146ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return new WinXpResolverTest(relaxed); 147ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#endif 148ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 149ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 150ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovNTSTATUS PatchNtdll(const char* function, bool relaxed) { 151ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov sandbox::ServiceResolverThunk* resolver = GetTestResolver(relaxed); 152ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 153ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov NTSTATUS ret = PatchNtdllWithResolver(function, relaxed, resolver); 154ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov delete resolver; 155ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return ret; 156ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 157ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 158ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovTEST(ServiceResolverTest, PatchesServices) { 159ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov NTSTATUS ret = PatchNtdll("NtClose", false); 160ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); 161ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 162ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ret = PatchNtdll("NtCreateFile", false); 163ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << 164ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ::GetLastError(); 165ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 166ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ret = PatchNtdll("NtCreateMutant", false); 167ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << 168ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ::GetLastError(); 169ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 170ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ret = PatchNtdll("NtMapViewOfSection", false); 171ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << 172ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ::GetLastError(); 173ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 174ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 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