Home | History | Annotate | Download | only in src
      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 
     15 namespace {
     16 
     17 // This is the concrete resolver used to perform service-call type functions
     18 // inside ntdll.dll.
     19 template<typename T>
     20 class 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 
     58 typedef ResolverThunkTest<sandbox::ServiceResolverThunk> WinXpResolverTest;
     59 
     60 #if !defined(_WIN64)
     61 typedef ResolverThunkTest<sandbox::Win2kResolverThunk> Win2kResolverTest;
     62 typedef ResolverThunkTest<sandbox::Win8ResolverThunk> Win8ResolverTest;
     63 typedef ResolverThunkTest<sandbox::Wow64ResolverThunk> Wow64ResolverTest;
     64 typedef ResolverThunkTest<sandbox::Wow64W8ResolverThunk> Wow64W8ResolverTest;
     65 #endif
     66 
     67 const BYTE kJump32 = 0xE9;
     68 
     69 void CheckJump(void* source, void* target) {
     70 #pragma pack(push)
     71 #pragma pack(1)
     72   struct Code {
     73     BYTE jump;
     74     ULONG delta;
     75   };
     76 #pragma pack(pop)
     77 
     78 #if defined(_WIN64)
     79   FAIL() << "Running 32-bit codepath";
     80 #else
     81   Code* patched = reinterpret_cast<Code*>(source);
     82   EXPECT_EQ(kJump32, patched->jump);
     83 
     84   ULONG source_addr = bit_cast<ULONG>(source);
     85   ULONG target_addr = bit_cast<ULONG>(target);
     86   EXPECT_EQ(target_addr + 19 - source_addr, patched->delta);
     87 #endif
     88 }
     89 
     90 NTSTATUS PatchNtdllWithResolver(const char* function, bool relaxed,
     91                                 sandbox::ServiceResolverThunk* resolver) {
     92   HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
     93   EXPECT_TRUE(NULL != ntdll_base);
     94 
     95   void* target = ::GetProcAddress(ntdll_base, function);
     96   EXPECT_TRUE(NULL != target);
     97   if (NULL == target)
     98     return STATUS_UNSUCCESSFUL;
     99 
    100   BYTE service[50];
    101   memcpy(service, target, sizeof(service));
    102 
    103   static_cast<WinXpResolverTest*>(resolver)->set_target(service);
    104 
    105   // Any pointer will do as an interception_entry_point
    106   void* function_entry = resolver;
    107   size_t thunk_size = resolver->GetThunkSize();
    108   scoped_ptr<char[]> thunk(new char[thunk_size]);
    109   size_t used;
    110 
    111   resolver->AllowLocalPatches();
    112 
    113   NTSTATUS ret = resolver->Setup(ntdll_base, NULL, function, NULL,
    114                                  function_entry, thunk.get(), thunk_size,
    115                                  &used);
    116   if (NT_SUCCESS(ret)) {
    117     EXPECT_EQ(thunk_size, used);
    118     EXPECT_NE(0, memcmp(service, target, sizeof(service)));
    119     EXPECT_NE(kJump32, service[0]);
    120 
    121     if (relaxed) {
    122       // It's already patched, let's patch again, and simulate a direct patch.
    123       service[0] = kJump32;
    124       ret = resolver->Setup(ntdll_base, NULL, function, NULL, function_entry,
    125                             thunk.get(), thunk_size, &used);
    126       CheckJump(service, thunk.get());
    127     }
    128   }
    129 
    130   return ret;
    131 }
    132 
    133 sandbox::ServiceResolverThunk* GetTestResolver(bool relaxed) {
    134 #if defined(_WIN64)
    135   return new WinXpResolverTest(relaxed);
    136 #else
    137   base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
    138   if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) {
    139     if (os_info->version() >= base::win::VERSION_WIN8)
    140       return new Wow64W8ResolverTest(relaxed);
    141     return new Wow64ResolverTest(relaxed);
    142   }
    143 
    144   if (!sandbox::IsXPSP2OrLater())
    145     return new Win2kResolverTest(relaxed);
    146 
    147   if (os_info->version() >= base::win::VERSION_WIN8)
    148     return new Win8ResolverTest(relaxed);
    149 
    150   return new WinXpResolverTest(relaxed);
    151 #endif
    152 }
    153 
    154 NTSTATUS PatchNtdll(const char* function, bool relaxed) {
    155   sandbox::ServiceResolverThunk* resolver = GetTestResolver(relaxed);
    156 
    157   NTSTATUS ret = PatchNtdllWithResolver(function, relaxed, resolver);
    158   delete resolver;
    159   return ret;
    160 }
    161 
    162 TEST(ServiceResolverTest, PatchesServices) {
    163   NTSTATUS ret = PatchNtdll("NtClose", false);
    164   EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
    165 
    166   ret = PatchNtdll("NtCreateFile", false);
    167   EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " <<
    168     ::GetLastError();
    169 
    170   ret = PatchNtdll("NtCreateMutant", false);
    171   EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " <<
    172     ::GetLastError();
    173 
    174   ret = PatchNtdll("NtMapViewOfSection", false);
    175   EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " <<
    176     ::GetLastError();
    177 }
    178 
    179 TEST(ServiceResolverTest, FailsIfNotService) {
    180 #if !defined(_WIN64)
    181   EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false));
    182 #endif
    183 
    184   EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false));
    185 }
    186 
    187 TEST(ServiceResolverTest, PatchesPatchedServices) {
    188 // We don't support "relaxed mode" for Win64 apps.
    189 #if !defined(_WIN64)
    190   NTSTATUS ret = PatchNtdll("NtClose", true);
    191   EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
    192 
    193   ret = PatchNtdll("NtCreateFile", true);
    194   EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " <<
    195     ::GetLastError();
    196 
    197   ret = PatchNtdll("NtCreateMutant", true);
    198   EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " <<
    199     ::GetLastError();
    200 
    201   ret = PatchNtdll("NtMapViewOfSection", true);
    202   EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " <<
    203     ::GetLastError();
    204 #endif
    205 }
    206 
    207 TEST(ServiceResolverTest, MultiplePatchedServices) {
    208 // We don't support "relaxed mode" for Win64 apps.
    209 #if !defined(_WIN64)
    210   sandbox::ServiceResolverThunk* resolver = GetTestResolver(true);
    211   NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, resolver);
    212   EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
    213 
    214   ret = PatchNtdllWithResolver("NtCreateFile", true, resolver);
    215   EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " <<
    216     ::GetLastError();
    217 
    218   ret = PatchNtdllWithResolver("NtCreateMutant", true, resolver);
    219   EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " <<
    220     ::GetLastError();
    221 
    222   ret = PatchNtdllWithResolver("NtMapViewOfSection", true, resolver);
    223   EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " <<
    224     ::GetLastError();
    225   delete resolver;
    226 #endif
    227 }
    228 
    229 TEST(ServiceResolverTest, LocalPatchesAllowed) {
    230   sandbox::ServiceResolverThunk* resolver = GetTestResolver(true);
    231 
    232   HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
    233   ASSERT_TRUE(NULL != ntdll_base);
    234 
    235   const char kFunctionName[] = "NtClose";
    236 
    237   void* target = ::GetProcAddress(ntdll_base, kFunctionName);
    238   ASSERT_TRUE(NULL != target);
    239 
    240   BYTE service[50];
    241   memcpy(service, target, sizeof(service));
    242   static_cast<WinXpResolverTest*>(resolver)->set_target(service);
    243 
    244   // Any pointer will do as an interception_entry_point
    245   void* function_entry = resolver;
    246   size_t thunk_size = resolver->GetThunkSize();
    247   scoped_ptr<char[]> thunk(new char[thunk_size]);
    248   size_t used;
    249 
    250   NTSTATUS ret = STATUS_UNSUCCESSFUL;
    251 
    252   // First try patching without having allowed local patches.
    253   ret = resolver->Setup(ntdll_base, NULL, kFunctionName, NULL,
    254                         function_entry, thunk.get(), thunk_size,
    255                         &used);
    256   EXPECT_FALSE(NT_SUCCESS(ret));
    257 
    258   // Now allow local patches and check that things work.
    259   resolver->AllowLocalPatches();
    260   ret = resolver->Setup(ntdll_base, NULL, kFunctionName, NULL,
    261                         function_entry, thunk.get(), thunk_size,
    262                         &used);
    263   EXPECT_EQ(STATUS_SUCCESS, ret);
    264 }
    265 
    266 }  // namespace
    267