security/sandbox/win/src/service_resolver_unittest.cc

changeset 0
6474c204b198
     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

mercurial