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 "base/basictypes.h" | |
8 #include "base/memory/scoped_ptr.h" | |
9 #include "base/win/windows_version.h" | |
10 #include "sandbox/src/resolver.h" | |
11 #include "sandbox/src/sandbox_utils.h" | |
12 #include "sandbox/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_array<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 | |
OLD | NEW |