|
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. |
|
4 |
|
5 // This file contains unit tests for ServiceResolverThunk. |
|
6 |
|
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" |
|
14 |
|
15 namespace { |
|
16 |
|
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) {} |
|
25 |
|
26 // Sets the interception target to the desired address. |
|
27 void set_target(void* target) { |
|
28 fake_target_ = target; |
|
29 } |
|
30 |
|
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); |
|
45 |
|
46 target_ = fake_target_; |
|
47 ntdll_base_ = ::GetModuleHandle(L"ntdll.dll"); |
|
48 return ret; |
|
49 }; |
|
50 |
|
51 private: |
|
52 // Holds the address of the fake target. |
|
53 void* fake_target_; |
|
54 |
|
55 DISALLOW_COPY_AND_ASSIGN(ResolverThunkTest); |
|
56 }; |
|
57 |
|
58 typedef ResolverThunkTest<sandbox::ServiceResolverThunk> WinXpResolverTest; |
|
59 |
|
60 #if !defined(_WIN64) |
|
61 typedef ResolverThunkTest<sandbox::Win2kResolverThunk> Win2kResolverTest; |
|
62 typedef ResolverThunkTest<sandbox::Win8ResolverThunk> Win8ResolverTest; |
|
63 typedef ResolverThunkTest<sandbox::Wow64ResolverThunk> Wow64ResolverTest; |
|
64 typedef ResolverThunkTest<sandbox::Wow64W8ResolverThunk> Wow64W8ResolverTest; |
|
65 #endif |
|
66 |
|
67 const BYTE kJump32 = 0xE9; |
|
68 |
|
69 void CheckJump(void* source, void* target) { |
|
70 #pragma pack(push) |
|
71 #pragma pack(1) |
|
72 struct Code { |
|
73 BYTE jump; |
|
74 ULONG delta; |
|
75 }; |
|
76 #pragma pack(pop) |
|
77 |
|
78 #if defined(_WIN64) |
|
79 FAIL() << "Running 32-bit codepath"; |
|
80 #else |
|
81 Code* patched = reinterpret_cast<Code*>(source); |
|
82 EXPECT_EQ(kJump32, patched->jump); |
|
83 |
|
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); |
|
87 #endif |
|
88 } |
|
89 |
|
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); |
|
94 |
|
95 void* target = ::GetProcAddress(ntdll_base, function); |
|
96 EXPECT_TRUE(NULL != target); |
|
97 if (NULL == target) |
|
98 return STATUS_UNSUCCESSFUL; |
|
99 |
|
100 BYTE service[50]; |
|
101 memcpy(service, target, sizeof(service)); |
|
102 |
|
103 static_cast<WinXpResolverTest*>(resolver)->set_target(service); |
|
104 |
|
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]); |
|
109 size_t used; |
|
110 |
|
111 NTSTATUS ret = resolver->Setup(ntdll_base, NULL, function, NULL, |
|
112 function_entry, thunk.get(), thunk_size, |
|
113 &used); |
|
114 if (NT_SUCCESS(ret)) { |
|
115 EXPECT_EQ(thunk_size, used); |
|
116 EXPECT_NE(0, memcmp(service, target, sizeof(service))); |
|
117 EXPECT_NE(kJump32, service[0]); |
|
118 |
|
119 if (relaxed) { |
|
120 // It's already patched, let's patch again, and simulate a direct patch. |
|
121 service[0] = kJump32; |
|
122 ret = resolver->Setup(ntdll_base, NULL, function, NULL, function_entry, |
|
123 thunk.get(), thunk_size, &used); |
|
124 CheckJump(service, thunk.get()); |
|
125 } |
|
126 } |
|
127 |
|
128 return ret; |
|
129 } |
|
130 |
|
131 sandbox::ServiceResolverThunk* GetTestResolver(bool relaxed) { |
|
132 #if defined(_WIN64) |
|
133 return new WinXpResolverTest(relaxed); |
|
134 #else |
|
135 base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); |
|
136 if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) { |
|
137 if (os_info->version() >= base::win::VERSION_WIN8) |
|
138 return new Wow64W8ResolverTest(relaxed); |
|
139 return new Wow64ResolverTest(relaxed); |
|
140 } |
|
141 |
|
142 if (!sandbox::IsXPSP2OrLater()) |
|
143 return new Win2kResolverTest(relaxed); |
|
144 |
|
145 if (os_info->version() >= base::win::VERSION_WIN8) |
|
146 return new Win8ResolverTest(relaxed); |
|
147 |
|
148 return new WinXpResolverTest(relaxed); |
|
149 #endif |
|
150 } |
|
151 |
|
152 NTSTATUS PatchNtdll(const char* function, bool relaxed) { |
|
153 sandbox::ServiceResolverThunk* resolver = GetTestResolver(relaxed); |
|
154 |
|
155 NTSTATUS ret = PatchNtdllWithResolver(function, relaxed, resolver); |
|
156 delete resolver; |
|
157 return ret; |
|
158 } |
|
159 |
|
160 TEST(ServiceResolverTest, PatchesServices) { |
|
161 NTSTATUS ret = PatchNtdll("NtClose", false); |
|
162 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); |
|
163 |
|
164 ret = PatchNtdll("NtCreateFile", false); |
|
165 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << |
|
166 ::GetLastError(); |
|
167 |
|
168 ret = PatchNtdll("NtCreateMutant", false); |
|
169 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << |
|
170 ::GetLastError(); |
|
171 |
|
172 ret = PatchNtdll("NtMapViewOfSection", false); |
|
173 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << |
|
174 ::GetLastError(); |
|
175 } |
|
176 |
|
177 TEST(ServiceResolverTest, FailsIfNotService) { |
|
178 #if !defined(_WIN64) |
|
179 EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false)); |
|
180 #endif |
|
181 |
|
182 EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false)); |
|
183 } |
|
184 |
|
185 TEST(ServiceResolverTest, PatchesPatchedServices) { |
|
186 // We don't support "relaxed mode" for Win64 apps. |
|
187 #if !defined(_WIN64) |
|
188 NTSTATUS ret = PatchNtdll("NtClose", true); |
|
189 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); |
|
190 |
|
191 ret = PatchNtdll("NtCreateFile", true); |
|
192 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << |
|
193 ::GetLastError(); |
|
194 |
|
195 ret = PatchNtdll("NtCreateMutant", true); |
|
196 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << |
|
197 ::GetLastError(); |
|
198 |
|
199 ret = PatchNtdll("NtMapViewOfSection", true); |
|
200 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << |
|
201 ::GetLastError(); |
|
202 #endif |
|
203 } |
|
204 |
|
205 TEST(ServiceResolverTest, MultiplePatchedServices) { |
|
206 // We don't support "relaxed mode" for Win64 apps. |
|
207 #if !defined(_WIN64) |
|
208 sandbox::ServiceResolverThunk* resolver = GetTestResolver(true); |
|
209 NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, resolver); |
|
210 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); |
|
211 |
|
212 ret = PatchNtdllWithResolver("NtCreateFile", true, resolver); |
|
213 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << |
|
214 ::GetLastError(); |
|
215 |
|
216 ret = PatchNtdllWithResolver("NtCreateMutant", true, resolver); |
|
217 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << |
|
218 ::GetLastError(); |
|
219 |
|
220 ret = PatchNtdllWithResolver("NtMapViewOfSection", true, resolver); |
|
221 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << |
|
222 ::GetLastError(); |
|
223 delete resolver; |
|
224 #endif |
|
225 } |
|
226 |
|
227 } // namespace |