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