OLD | NEW |
| (Empty) |
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 <stddef.h> | |
8 | |
9 #include "base/bit_cast.h" | |
10 #include "base/macros.h" | |
11 #include "base/memory/scoped_ptr.h" | |
12 #include "base/win/windows_version.h" | |
13 #include "sandbox/win/src/resolver.h" | |
14 #include "sandbox/win/src/sandbox_utils.h" | |
15 #include "sandbox/win/src/service_resolver.h" | |
16 #include "testing/gtest/include/gtest/gtest.h" | |
17 | |
18 namespace { | |
19 | |
20 // This is the concrete resolver used to perform service-call type functions | |
21 // inside ntdll.dll. | |
22 template<typename T> | |
23 class ResolverThunkTest : public T { | |
24 public: | |
25 // The service resolver needs a child process to write to. | |
26 explicit ResolverThunkTest(bool relaxed) | |
27 : T(::GetCurrentProcess(), relaxed) {} | |
28 | |
29 // Sets the interception target to the desired address. | |
30 void set_target(void* target) { | |
31 fake_target_ = target; | |
32 } | |
33 | |
34 protected: | |
35 // Overrides Resolver::Init | |
36 virtual NTSTATUS Init(const void* target_module, | |
37 const void* interceptor_module, | |
38 const char* target_name, | |
39 const char* interceptor_name, | |
40 const void* interceptor_entry_point, | |
41 void* thunk_storage, | |
42 size_t storage_bytes) { | |
43 NTSTATUS ret = STATUS_SUCCESS; | |
44 ret = T::Init(target_module, interceptor_module, target_name, | |
45 interceptor_name, interceptor_entry_point, thunk_storage, | |
46 storage_bytes); | |
47 EXPECT_EQ(STATUS_SUCCESS, ret); | |
48 | |
49 this->target_ = fake_target_; | |
50 | |
51 return ret; | |
52 }; | |
53 | |
54 private: | |
55 // Holds the address of the fake target. | |
56 void* fake_target_; | |
57 | |
58 DISALLOW_COPY_AND_ASSIGN(ResolverThunkTest); | |
59 }; | |
60 | |
61 typedef ResolverThunkTest<sandbox::ServiceResolverThunk> WinXpResolverTest; | |
62 | |
63 #if !defined(_WIN64) | |
64 typedef ResolverThunkTest<sandbox::Win8ResolverThunk> Win8ResolverTest; | |
65 typedef ResolverThunkTest<sandbox::Wow64ResolverThunk> Wow64ResolverTest; | |
66 typedef ResolverThunkTest<sandbox::Wow64W8ResolverThunk> Wow64W8ResolverTest; | |
67 typedef ResolverThunkTest<sandbox::Wow64W10ResolverThunk> Wow64W10ResolverTest; | |
68 #endif | |
69 | |
70 const BYTE kJump32 = 0xE9; | |
71 | |
72 void CheckJump(void* source, void* target) { | |
73 #pragma pack(push) | |
74 #pragma pack(1) | |
75 struct Code { | |
76 BYTE jump; | |
77 ULONG delta; | |
78 }; | |
79 #pragma pack(pop) | |
80 | |
81 #if defined(_WIN64) | |
82 FAIL() << "Running 32-bit codepath"; | |
83 #else | |
84 Code* patched = reinterpret_cast<Code*>(source); | |
85 EXPECT_EQ(kJump32, patched->jump); | |
86 | |
87 ULONG source_addr = bit_cast<ULONG>(source); | |
88 ULONG target_addr = bit_cast<ULONG>(target); | |
89 EXPECT_EQ(target_addr + 19 - source_addr, patched->delta); | |
90 #endif | |
91 } | |
92 | |
93 NTSTATUS PatchNtdllWithResolver(const char* function, bool relaxed, | |
94 sandbox::ServiceResolverThunk* resolver) { | |
95 HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll"); | |
96 EXPECT_TRUE(NULL != ntdll_base); | |
97 | |
98 void* target = ::GetProcAddress(ntdll_base, function); | |
99 EXPECT_TRUE(NULL != target); | |
100 if (NULL == target) | |
101 return STATUS_UNSUCCESSFUL; | |
102 | |
103 BYTE service[50]; | |
104 memcpy(service, target, sizeof(service)); | |
105 | |
106 static_cast<WinXpResolverTest*>(resolver)->set_target(service); | |
107 | |
108 // Any pointer will do as an interception_entry_point | |
109 void* function_entry = resolver; | |
110 size_t thunk_size = resolver->GetThunkSize(); | |
111 scoped_ptr<char[]> thunk(new char[thunk_size]); | |
112 size_t used; | |
113 | |
114 resolver->AllowLocalPatches(); | |
115 | |
116 NTSTATUS ret = resolver->Setup(ntdll_base, NULL, function, NULL, | |
117 function_entry, thunk.get(), thunk_size, | |
118 &used); | |
119 if (NT_SUCCESS(ret)) { | |
120 EXPECT_EQ(thunk_size, used); | |
121 EXPECT_NE(0, memcmp(service, target, sizeof(service))); | |
122 EXPECT_NE(kJump32, service[0]); | |
123 | |
124 if (relaxed) { | |
125 // It's already patched, let's patch again, and simulate a direct patch. | |
126 service[0] = kJump32; | |
127 ret = resolver->Setup(ntdll_base, NULL, function, NULL, function_entry, | |
128 thunk.get(), thunk_size, &used); | |
129 CheckJump(service, thunk.get()); | |
130 } | |
131 } | |
132 | |
133 return ret; | |
134 } | |
135 | |
136 sandbox::ServiceResolverThunk* GetTestResolver(bool relaxed) { | |
137 #if defined(_WIN64) | |
138 return new WinXpResolverTest(relaxed); | |
139 #else | |
140 base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); | |
141 if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) { | |
142 if (os_info->version() >= base::win::VERSION_WIN10) | |
143 return new Wow64W10ResolverTest(relaxed); | |
144 if (os_info->version() >= base::win::VERSION_WIN8) | |
145 return new Wow64W8ResolverTest(relaxed); | |
146 return new Wow64ResolverTest(relaxed); | |
147 } | |
148 | |
149 if (os_info->version() >= base::win::VERSION_WIN8) | |
150 return new Win8ResolverTest(relaxed); | |
151 | |
152 return new WinXpResolverTest(relaxed); | |
153 #endif | |
154 } | |
155 | |
156 NTSTATUS PatchNtdll(const char* function, bool relaxed) { | |
157 sandbox::ServiceResolverThunk* resolver = GetTestResolver(relaxed); | |
158 | |
159 NTSTATUS ret = PatchNtdllWithResolver(function, relaxed, resolver); | |
160 delete resolver; | |
161 return ret; | |
162 } | |
163 | |
164 TEST(ServiceResolverTest, PatchesServices) { | |
165 NTSTATUS ret = PatchNtdll("NtClose", false); | |
166 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); | |
167 | |
168 ret = PatchNtdll("NtCreateFile", false); | |
169 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << | |
170 ::GetLastError(); | |
171 | |
172 ret = PatchNtdll("NtCreateMutant", false); | |
173 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << | |
174 ::GetLastError(); | |
175 | |
176 ret = PatchNtdll("NtMapViewOfSection", false); | |
177 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << | |
178 ::GetLastError(); | |
179 } | |
180 | |
181 TEST(ServiceResolverTest, FailsIfNotService) { | |
182 #if !defined(_WIN64) | |
183 EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false)); | |
184 #endif | |
185 | |
186 EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false)); | |
187 } | |
188 | |
189 TEST(ServiceResolverTest, PatchesPatchedServices) { | |
190 // We don't support "relaxed mode" for Win64 apps. | |
191 #if !defined(_WIN64) | |
192 NTSTATUS ret = PatchNtdll("NtClose", true); | |
193 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); | |
194 | |
195 ret = PatchNtdll("NtCreateFile", true); | |
196 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << | |
197 ::GetLastError(); | |
198 | |
199 ret = PatchNtdll("NtCreateMutant", true); | |
200 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << | |
201 ::GetLastError(); | |
202 | |
203 ret = PatchNtdll("NtMapViewOfSection", true); | |
204 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << | |
205 ::GetLastError(); | |
206 #endif | |
207 } | |
208 | |
209 TEST(ServiceResolverTest, MultiplePatchedServices) { | |
210 // We don't support "relaxed mode" for Win64 apps. | |
211 #if !defined(_WIN64) | |
212 sandbox::ServiceResolverThunk* resolver = GetTestResolver(true); | |
213 NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, resolver); | |
214 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); | |
215 | |
216 ret = PatchNtdllWithResolver("NtCreateFile", true, resolver); | |
217 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << | |
218 ::GetLastError(); | |
219 | |
220 ret = PatchNtdllWithResolver("NtCreateMutant", true, resolver); | |
221 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << | |
222 ::GetLastError(); | |
223 | |
224 ret = PatchNtdllWithResolver("NtMapViewOfSection", true, resolver); | |
225 EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << | |
226 ::GetLastError(); | |
227 delete resolver; | |
228 #endif | |
229 } | |
230 | |
231 TEST(ServiceResolverTest, LocalPatchesAllowed) { | |
232 sandbox::ServiceResolverThunk* resolver = GetTestResolver(true); | |
233 | |
234 HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll"); | |
235 ASSERT_TRUE(NULL != ntdll_base); | |
236 | |
237 const char kFunctionName[] = "NtClose"; | |
238 | |
239 void* target = ::GetProcAddress(ntdll_base, kFunctionName); | |
240 ASSERT_TRUE(NULL != target); | |
241 | |
242 BYTE service[50]; | |
243 memcpy(service, target, sizeof(service)); | |
244 static_cast<WinXpResolverTest*>(resolver)->set_target(service); | |
245 | |
246 // Any pointer will do as an interception_entry_point | |
247 void* function_entry = resolver; | |
248 size_t thunk_size = resolver->GetThunkSize(); | |
249 scoped_ptr<char[]> thunk(new char[thunk_size]); | |
250 size_t used; | |
251 | |
252 NTSTATUS ret = STATUS_UNSUCCESSFUL; | |
253 | |
254 // First try patching without having allowed local patches. | |
255 ret = resolver->Setup(ntdll_base, NULL, kFunctionName, NULL, | |
256 function_entry, thunk.get(), thunk_size, | |
257 &used); | |
258 EXPECT_FALSE(NT_SUCCESS(ret)); | |
259 | |
260 // Now allow local patches and check that things work. | |
261 resolver->AllowLocalPatches(); | |
262 ret = resolver->Setup(ntdll_base, NULL, kFunctionName, NULL, | |
263 function_entry, thunk.get(), thunk_size, | |
264 &used); | |
265 EXPECT_EQ(STATUS_SUCCESS, ret); | |
266 } | |
267 | |
268 } // namespace | |
OLD | NEW |