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::Win8ResolverThunk
> Win8ResolverTest
;
62 typedef ResolverThunkTest
<sandbox::Wow64ResolverThunk
> Wow64ResolverTest
;
63 typedef ResolverThunkTest
<sandbox::Wow64W8ResolverThunk
> Wow64W8ResolverTest
;
66 const BYTE kJump32
= 0xE9;
68 void CheckJump(void* source
, void* target
) {
78 FAIL() << "Running 32-bit codepath";
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
);
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
);
97 return STATUS_UNSUCCESSFUL
;
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
]);
110 resolver
->AllowLocalPatches();
112 NTSTATUS ret
= resolver
->Setup(ntdll_base
, NULL
, function
, NULL
,
113 function_entry
, thunk
.get(), thunk_size
,
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]);
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());
132 sandbox::ServiceResolverThunk
* GetTestResolver(bool relaxed
) {
134 return new WinXpResolverTest(relaxed
);
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
);
150 NTSTATUS
PatchNtdll(const char* function
, bool relaxed
) {
151 sandbox::ServiceResolverThunk
* resolver
= GetTestResolver(relaxed
);
153 NTSTATUS ret
= PatchNtdllWithResolver(function
, relaxed
, resolver
);
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: " <<
166 ret
= PatchNtdll("NtCreateMutant", false);
167 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtCreateMutant, last error: " <<
170 ret
= PatchNtdll("NtMapViewOfSection", false);
171 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtMapViewOfSection, last error: " <<
175 TEST(ServiceResolverTest
, FailsIfNotService
) {
177 EXPECT_NE(STATUS_SUCCESS
, PatchNtdll("RtlUlongByteSwap", false));
180 EXPECT_NE(STATUS_SUCCESS
, PatchNtdll("LdrLoadDll", false));
183 TEST(ServiceResolverTest
, PatchesPatchedServices
) {
184 // We don't support "relaxed mode" for Win64 apps.
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: " <<
193 ret
= PatchNtdll("NtCreateMutant", true);
194 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtCreateMutant, last error: " <<
197 ret
= PatchNtdll("NtMapViewOfSection", true);
198 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtMapViewOfSection, last error: " <<
203 TEST(ServiceResolverTest
, MultiplePatchedServices
) {
204 // We don't support "relaxed mode" for Win64 apps.
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: " <<
214 ret
= PatchNtdllWithResolver("NtCreateMutant", true, resolver
);
215 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtCreateMutant, last error: " <<
218 ret
= PatchNtdllWithResolver("NtMapViewOfSection", true, resolver
);
219 EXPECT_EQ(STATUS_SUCCESS
, ret
) << "NtMapViewOfSection, last error: " <<
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
);
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
]);
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
,
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
,
259 EXPECT_EQ(STATUS_SUCCESS
, ret
);