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"
17 // This is the concrete resolver used to perform service-call type functions
20 class ResolverThunkTest
: public T
{
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
;
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
,
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_
;
52 // Holds the address of the fake target.
55 DISALLOW_COPY_AND_ASSIGN(ResolverThunkTest
);
58 typedef ResolverThunkTest
<sandbox::ServiceResolverThunk
> WinXpResolverTest
;
61 typedef ResolverThunkTest
<sandbox::Win2kResolverThunk
> Win2kResolverTest
;
62 typedef ResolverThunkTest
<sandbox::Win8ResolverThunk
> Win8ResolverTest
;
63 typedef ResolverThunkTest
<sandbox::Wow64ResolverThunk
> Wow64ResolverTest
;
64 typedef ResolverThunkTest
<sandbox::Wow64W8ResolverThunk
> Wow64W8ResolverTest
;
67 const BYTE kJump32
= 0xE9;
69 void CheckJump(void* source
, void* target
) {
79 FAIL() << "Running 32-bit codepath";
81 Code
* patched
= reinterpret_cast<Code
*>(source
);
82 EXPECT_EQ(kJump32
, patched
->jump
);
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
);
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
);
95 void* target
= ::GetProcAddress(ntdll_base
, function
);
96 EXPECT_TRUE(NULL
!= target
);
98 return STATUS_UNSUCCESSFUL
;
101 memcpy(service
, target
, sizeof(service
));
103 static_cast<WinXpResolverTest
*>(resolver
)->set_target(service
);
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
]);
111 resolver
->AllowLocalPatches();
113 NTSTATUS ret
= resolver
->Setup(ntdll_base
, NULL
, function
, NULL
,
114 function_entry
, thunk
.get(), thunk_size
,
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]);
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());
133 sandbox::ServiceResolverThunk
* GetTestResolver(bool relaxed
) {
135 return new WinXpResolverTest(relaxed
);
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
);
144 if (!sandbox::IsXPSP2OrLater())
145 return new Win2kResolverTest(relaxed
);
147 if (os_info
->version() >= base::win::VERSION_WIN8
)
148 return new Win8ResolverTest(relaxed
);
150 return new WinXpResolverTest(relaxed
);
154 NTSTATUS
PatchNtdll(const char* function
, bool relaxed
) {
155 sandbox::ServiceResolverThunk
* resolver
= GetTestResolver(relaxed
);
157 NTSTATUS ret
= PatchNtdllWithResolver(function
, relaxed
, resolver
);
162 TEST(ServiceResolverTest
, PatchesServices
) {
163 NTSTATUS ret
= PatchNtdll("NtClose", false);
164 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtClose, last error: " << ::GetLastError();
166 ret
= PatchNtdll("NtCreateFile", false);
167 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtCreateFile, last error: " <<
170 ret
= PatchNtdll("NtCreateMutant", false);
171 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtCreateMutant, last error: " <<
174 ret
= PatchNtdll("NtMapViewOfSection", false);
175 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtMapViewOfSection, last error: " <<
179 TEST(ServiceResolverTest
, FailsIfNotService
) {
181 EXPECT_NE(STATUS_SUCCESS
, PatchNtdll("RtlUlongByteSwap", false));
184 EXPECT_NE(STATUS_SUCCESS
, PatchNtdll("LdrLoadDll", false));
187 TEST(ServiceResolverTest
, PatchesPatchedServices
) {
188 // We don't support "relaxed mode" for Win64 apps.
190 NTSTATUS ret
= PatchNtdll("NtClose", true);
191 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtClose, last error: " << ::GetLastError();
193 ret
= PatchNtdll("NtCreateFile", true);
194 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtCreateFile, last error: " <<
197 ret
= PatchNtdll("NtCreateMutant", true);
198 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtCreateMutant, last error: " <<
201 ret
= PatchNtdll("NtMapViewOfSection", true);
202 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtMapViewOfSection, last error: " <<
207 TEST(ServiceResolverTest
, MultiplePatchedServices
) {
208 // We don't support "relaxed mode" for Win64 apps.
210 sandbox::ServiceResolverThunk
* resolver
= GetTestResolver(true);
211 NTSTATUS ret
= PatchNtdllWithResolver("NtClose", true, resolver
);
212 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtClose, last error: " << ::GetLastError();
214 ret
= PatchNtdllWithResolver("NtCreateFile", true, resolver
);
215 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtCreateFile, last error: " <<
218 ret
= PatchNtdllWithResolver("NtCreateMutant", true, resolver
);
219 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtCreateMutant, last error: " <<
222 ret
= PatchNtdllWithResolver("NtMapViewOfSection", true, resolver
);
223 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtMapViewOfSection, last error: " <<
229 TEST(ServiceResolverTest
, LocalPatchesAllowed
) {
230 sandbox::ServiceResolverThunk
* resolver
= GetTestResolver(true);
232 HMODULE ntdll_base
= ::GetModuleHandle(L
"ntdll.dll");
233 ASSERT_TRUE(NULL
!= ntdll_base
);
235 const char kFunctionName
[] = "NtClose";
237 void* target
= ::GetProcAddress(ntdll_base
, kFunctionName
);
238 ASSERT_TRUE(NULL
!= target
);
241 memcpy(service
, target
, sizeof(service
));
242 static_cast<WinXpResolverTest
*>(resolver
)->set_target(service
);
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
]);
250 NTSTATUS ret
= STATUS_UNSUCCESSFUL
;
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
,
256 EXPECT_FALSE(NT_SUCCESS(ret
));
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
,
263 EXPECT_EQ(STATUS_SUCCESS
, ret
);