Don't preload rarely seen large images
[chromium-blink-merge.git] / sandbox / win / src / service_resolver_unittest.cc
blobc7ac7eab16905ea150bd31017a972595672cac15
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.
5 // This file contains unit tests for ServiceResolverThunk.
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"
15 namespace {
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) {}
26 // Sets the interception target to the desired address.
27 void set_target(void* target) {
28 fake_target_ = target;
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);
46 target_ = fake_target_;
48 return ret;
51 private:
52 // Holds the address of the fake target.
53 void* fake_target_;
55 DISALLOW_COPY_AND_ASSIGN(ResolverThunkTest);
58 typedef ResolverThunkTest<sandbox::ServiceResolverThunk> WinXpResolverTest;
60 #if !defined(_WIN64)
61 typedef ResolverThunkTest<sandbox::Win8ResolverThunk> Win8ResolverTest;
62 typedef ResolverThunkTest<sandbox::Wow64ResolverThunk> Wow64ResolverTest;
63 typedef ResolverThunkTest<sandbox::Wow64W8ResolverThunk> Wow64W8ResolverTest;
64 #endif
66 const BYTE kJump32 = 0xE9;
68 void CheckJump(void* source, void* target) {
69 #pragma pack(push)
70 #pragma pack(1)
71 struct Code {
72 BYTE jump;
73 ULONG delta;
75 #pragma pack(pop)
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);
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
89 NTSTATUS PatchNtdllWithResolver(const char* function, bool relaxed,
90 sandbox::ServiceResolverThunk* resolver) {
91 HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
92 EXPECT_TRUE(NULL != ntdll_base);
94 void* target = ::GetProcAddress(ntdll_base, function);
95 EXPECT_TRUE(NULL != target);
96 if (NULL == target)
97 return STATUS_UNSUCCESSFUL;
99 BYTE service[50];
100 memcpy(service, target, sizeof(service));
102 static_cast<WinXpResolverTest*>(resolver)->set_target(service);
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;
110 resolver->AllowLocalPatches();
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]);
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());
129 return ret;
132 sandbox::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);
143 if (os_info->version() >= base::win::VERSION_WIN8)
144 return new Win8ResolverTest(relaxed);
146 return new WinXpResolverTest(relaxed);
147 #endif
150 NTSTATUS PatchNtdll(const char* function, bool relaxed) {
151 sandbox::ServiceResolverThunk* resolver = GetTestResolver(relaxed);
153 NTSTATUS ret = PatchNtdllWithResolver(function, relaxed, resolver);
154 delete resolver;
155 return ret;
158 TEST(ServiceResolverTest, PatchesServices) {
159 NTSTATUS ret = PatchNtdll("NtClose", false);
160 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
162 ret = PatchNtdll("NtCreateFile", false);
163 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " <<
164 ::GetLastError();
166 ret = PatchNtdll("NtCreateMutant", false);
167 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " <<
168 ::GetLastError();
170 ret = PatchNtdll("NtMapViewOfSection", false);
171 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " <<
172 ::GetLastError();
175 TEST(ServiceResolverTest, FailsIfNotService) {
176 #if !defined(_WIN64)
177 EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false));
178 #endif
180 EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false));
183 TEST(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();
189 ret = PatchNtdll("NtCreateFile", true);
190 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " <<
191 ::GetLastError();
193 ret = PatchNtdll("NtCreateMutant", true);
194 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " <<
195 ::GetLastError();
197 ret = PatchNtdll("NtMapViewOfSection", true);
198 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " <<
199 ::GetLastError();
200 #endif
203 TEST(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();
210 ret = PatchNtdllWithResolver("NtCreateFile", true, resolver);
211 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " <<
212 ::GetLastError();
214 ret = PatchNtdllWithResolver("NtCreateMutant", true, resolver);
215 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " <<
216 ::GetLastError();
218 ret = PatchNtdllWithResolver("NtMapViewOfSection", true, resolver);
219 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " <<
220 ::GetLastError();
221 delete resolver;
222 #endif
225 TEST(ServiceResolverTest, LocalPatchesAllowed) {
226 sandbox::ServiceResolverThunk* resolver = GetTestResolver(true);
228 HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
229 ASSERT_TRUE(NULL != ntdll_base);
231 const char kFunctionName[] = "NtClose";
233 void* target = ::GetProcAddress(ntdll_base, kFunctionName);
234 ASSERT_TRUE(NULL != target);
236 BYTE service[50];
237 memcpy(service, target, sizeof(service));
238 static_cast<WinXpResolverTest*>(resolver)->set_target(service);
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;
246 NTSTATUS ret = STATUS_UNSUCCESSFUL;
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));
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);
262 } // namespace