OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 #include "chrome_elf/create_file/chrome_create_file.h" | |
6 | |
7 #include <windows.h> | |
8 | |
9 #include <bitset> | |
10 #include <string> | |
11 | |
12 #include "base/base_paths_win.h" | |
13 #include "base/file_util.h" | |
14 #include "base/files/file_path.h" | |
15 #include "base/files/scoped_temp_dir.h" | |
16 #include "base/path_service.h" | |
17 #include "base/threading/platform_thread.h" | |
18 #include "base/win/iat_patch_function.h" | |
19 #include "base/win/scoped_handle.h" | |
20 #include "base/win/windows_version.h" | |
21 #include "chrome_elf/chrome_elf_constants.h" | |
22 #include "chrome_elf/ntdll_cache.h" | |
23 #include "sandbox/win/src/nt_internals.h" | |
24 #include "testing/gtest/include/gtest/gtest.h" | |
25 #include "testing/platform_test.h" | |
26 | |
27 | |
28 namespace { | |
29 | |
30 // Test fixtures ------------------------------------------------------------- | |
31 | |
32 class ChromeCreateFileTest : public PlatformTest { | |
33 protected: | |
34 struct NtCreateFileParams { | |
35 ACCESS_MASK desired_access; | |
36 OBJECT_ATTRIBUTES object_attributes; | |
37 PLARGE_INTEGER allocation_size; | |
38 ULONG file_attributes; | |
39 ULONG share_access; | |
40 ULONG create_disposition; | |
41 ULONG create_options; | |
42 PVOID ea_buffer; | |
43 ULONG ea_length; | |
44 }; | |
45 | |
46 enum CallPath { | |
47 ELF, | |
48 KERNEL | |
49 }; | |
50 | |
51 template<CallPath path> | |
52 static NTSTATUS WINAPI FakeNtCreateFile( | |
53 PHANDLE file_handle, | |
54 ACCESS_MASK desired_access, | |
55 POBJECT_ATTRIBUTES object_attributes, | |
56 PIO_STATUS_BLOCK io_status_block, | |
57 PLARGE_INTEGER allocation_size, | |
58 ULONG file_attributes, | |
59 ULONG share_access, | |
60 ULONG create_disposition, | |
61 ULONG create_options, | |
62 PVOID ea_buffer, | |
63 ULONG ea_length) { | |
64 return self_->HandleCreateFileCall(file_handle, | |
65 desired_access, | |
66 object_attributes, | |
67 io_status_block, | |
68 allocation_size, | |
69 file_attributes, | |
70 share_access, | |
71 create_disposition, | |
72 create_options, | |
73 ea_buffer, | |
74 ea_length, | |
75 path); | |
76 } | |
77 | |
78 virtual void SetUp() OVERRIDE { | |
79 original_thread_ = base::PlatformThread::CurrentId(); | |
80 InitCache(); | |
81 PlatformTest::SetUp(); | |
82 | |
83 base::FilePath user_data_dir; | |
84 PathService::Get(base::DIR_LOCAL_APP_DATA, &user_data_dir); | |
85 ASSERT_TRUE(temp_dir_.CreateUniqueTempDirUnderPath(user_data_dir)); | |
86 ASSERT_TRUE(temp_dir2_.CreateUniqueTempDir()); | |
87 self_ = this; | |
88 } | |
89 | |
90 void RedirectNtCreateFileCalls() { | |
91 old_func_ptr_ = | |
92 reinterpret_cast<NtCreateFileFunction>(g_ntdll_lookup["NtCreateFile"]); | |
93 | |
94 // KernelBase.dll only exists for Win7 and later, prior to that, kernel32 | |
95 // imports from ntdll directly. | |
96 if (base::win::GetVersion() < base::win::VERSION_WIN7) { | |
97 patcher_.Patch(L"kernel32.dll", "ntdll.dll", "NtCreateFile", | |
98 reinterpret_cast<void(*)()>(&FakeNtCreateFile<KERNEL>)); | |
99 } else { | |
100 patcher_.Patch(L"kernelbase.dll", "ntdll.dll", "NtCreateFile", | |
101 reinterpret_cast<void(*)()>(&FakeNtCreateFile<KERNEL>)); | |
102 } | |
103 | |
104 g_ntdll_lookup["NtCreateFile"] = reinterpret_cast<void(*)()>( | |
105 &ChromeCreateFileTest::FakeNtCreateFile<ELF>); | |
106 } | |
107 | |
108 void ResetNtCreateFileCalls() { | |
109 g_ntdll_lookup["NtCreateFile"] = reinterpret_cast<void*>(old_func_ptr_); | |
110 patcher_.Unpatch(); | |
111 } | |
112 | |
113 NTSTATUS HandleCreateFileCall(PHANDLE file_handle, | |
114 ACCESS_MASK desired_access, | |
115 POBJECT_ATTRIBUTES object_attributes, | |
116 PIO_STATUS_BLOCK io_status_block, | |
117 PLARGE_INTEGER allocation_size, | |
118 ULONG file_attributes, | |
119 ULONG share_access, | |
120 ULONG create_disposition, | |
121 ULONG create_options, | |
122 PVOID ea_buffer, | |
123 ULONG ea_length, | |
124 CallPath call_path) { | |
125 if (original_thread_ == base::PlatformThread::CurrentId()) { | |
126 SetParams(desired_access, | |
127 object_attributes, | |
128 allocation_size, | |
129 file_attributes, | |
130 share_access, | |
131 create_disposition, | |
132 create_options, | |
133 ea_buffer, | |
134 ea_length, | |
135 call_path == ELF ? &elf_params_ : &kernel_params_); | |
136 } | |
137 | |
138 // Forward the call to the real NTCreateFile. | |
139 return old_func_ptr_(file_handle, | |
140 desired_access, | |
141 object_attributes, | |
142 io_status_block, | |
143 allocation_size, | |
144 file_attributes, | |
145 share_access, | |
146 create_disposition, | |
147 create_options, | |
148 ea_buffer, | |
149 ea_length); | |
150 } | |
151 | |
152 void SetParams(ACCESS_MASK desired_access, | |
153 POBJECT_ATTRIBUTES object_attributes, | |
154 PLARGE_INTEGER allocation_size, | |
155 ULONG file_attributes, | |
156 ULONG share_access, | |
157 ULONG create_disposition, | |
158 ULONG create_options, | |
159 PVOID ea_buffer, | |
160 ULONG ea_length, | |
161 NtCreateFileParams* params) { | |
162 params->desired_access = desired_access; | |
163 params->object_attributes.Length = object_attributes->Length; | |
164 params->object_attributes.ObjectName = object_attributes->ObjectName; | |
165 params->object_attributes.RootDirectory = object_attributes->RootDirectory; | |
166 params->object_attributes.Attributes = object_attributes->Attributes; | |
167 params->object_attributes.SecurityDescriptor = | |
168 object_attributes->SecurityDescriptor; | |
169 params->object_attributes.SecurityQualityOfService = | |
170 object_attributes->SecurityQualityOfService; | |
171 params->allocation_size = allocation_size; | |
172 params->file_attributes = file_attributes; | |
173 params->share_access = share_access; | |
174 params->create_disposition = create_disposition; | |
175 params->create_options = create_options; | |
176 params->ea_buffer = ea_buffer; | |
177 params->ea_length = ea_length; | |
178 } | |
179 | |
180 void CheckParams() { | |
181 std::bitset<32> elf((int) elf_params_.desired_access); | |
182 std::bitset<32> ker((int) kernel_params_.desired_access); | |
183 | |
184 EXPECT_EQ(kernel_params_.desired_access, elf_params_.desired_access) | |
185 << elf << "\n" << ker; | |
186 EXPECT_EQ(kernel_params_.object_attributes.Length, | |
187 elf_params_.object_attributes.Length); | |
188 EXPECT_EQ(kernel_params_.object_attributes.RootDirectory, | |
189 elf_params_.object_attributes.RootDirectory); | |
190 EXPECT_EQ(kernel_params_.object_attributes.Attributes, | |
191 elf_params_.object_attributes.Attributes); | |
192 EXPECT_EQ(kernel_params_.object_attributes.SecurityDescriptor, | |
193 elf_params_.object_attributes.SecurityDescriptor); | |
194 EXPECT_EQ(kernel_params_.allocation_size, elf_params_.allocation_size); | |
195 EXPECT_EQ(kernel_params_.file_attributes, elf_params_.file_attributes); | |
196 EXPECT_EQ(kernel_params_.share_access, elf_params_.share_access); | |
197 EXPECT_EQ(kernel_params_.create_disposition, | |
198 elf_params_.create_disposition); | |
199 EXPECT_EQ(kernel_params_.create_options, elf_params_.create_options); | |
200 EXPECT_EQ(kernel_params_.ea_buffer, elf_params_.ea_buffer); | |
201 EXPECT_EQ(kernel_params_.ea_length, elf_params_.ea_length); | |
202 } | |
203 | |
204 void DoWriteCheck(const base::FilePath& path, bool is_system) { | |
205 base::win::ScopedHandle file_handle; | |
206 const char kTestData[] = "0123456789"; | |
207 int buffer_size = sizeof(kTestData) - 1; | |
208 DWORD bytes_written; | |
209 | |
210 if (is_system) { | |
211 file_handle.Set(::CreateFileW(path.value().c_str(), | |
212 GENERIC_WRITE, | |
213 0, | |
214 NULL, | |
215 CREATE_ALWAYS, | |
216 FILE_ATTRIBUTE_NORMAL, | |
217 NULL)); | |
218 } else { | |
219 file_handle.Set(CreateFileNTDLL(path.value().c_str(), | |
220 GENERIC_WRITE, | |
221 0, | |
222 NULL, | |
223 CREATE_ALWAYS, | |
224 FILE_ATTRIBUTE_NORMAL, | |
225 NULL)); | |
226 } | |
227 | |
228 | |
229 EXPECT_FALSE(file_handle == INVALID_HANDLE_VALUE); | |
230 ::WriteFile(file_handle, kTestData, buffer_size, &bytes_written, NULL); | |
231 EXPECT_EQ(buffer_size, bytes_written); | |
232 } | |
233 | |
234 void DoReadCheck(const base::FilePath& path, bool is_system) { | |
235 base::win::ScopedHandle file_handle; | |
236 const char kTestData[] = "0123456789"; | |
237 int buffer_size = sizeof(kTestData) - 1; | |
238 DWORD bytes_read; | |
239 char read_buffer[10]; | |
240 | |
241 if (is_system) { | |
242 file_handle.Set(::CreateFileW(path.value().c_str(), | |
243 GENERIC_READ, | |
244 0, | |
245 NULL, | |
246 OPEN_ALWAYS, | |
247 FILE_ATTRIBUTE_NORMAL, | |
248 NULL)); | |
249 } else { | |
250 file_handle.Set(CreateFileNTDLL(path.value().c_str(), | |
251 GENERIC_READ, | |
252 0, | |
253 NULL, | |
254 OPEN_ALWAYS, | |
255 FILE_ATTRIBUTE_NORMAL, | |
256 NULL)); | |
257 } | |
258 | |
259 EXPECT_FALSE(file_handle == INVALID_HANDLE_VALUE); | |
260 ::ReadFile(file_handle, read_buffer, buffer_size, &bytes_read, NULL); | |
261 EXPECT_EQ(buffer_size, bytes_read); | |
262 EXPECT_EQ(0, memcmp(kTestData, read_buffer, bytes_read)); | |
263 } | |
264 | |
265 static ChromeCreateFileTest* self_; | |
266 | |
267 NtCreateFileFunction old_func_ptr_; | |
268 base::ScopedTempDir temp_dir_; | |
269 base::ScopedTempDir temp_dir2_; | |
270 base::win::IATPatchFunction patcher_; | |
271 NtCreateFileParams kernel_params_; | |
272 NtCreateFileParams elf_params_; | |
273 base::PlatformThreadId original_thread_; | |
274 }; | |
275 | |
276 ChromeCreateFileTest* ChromeCreateFileTest::self_ = NULL; | |
277 | |
278 // Tests --------------------------------------------------------------------- | |
279 | |
280 TEST_F(ChromeCreateFileTest, CheckWriteAndReadParams) { | |
281 RedirectNtCreateFileCalls(); | |
282 | |
283 // Make sure we can write to this file handle when called via the system. | |
284 base::FilePath junk_path_1 = temp_dir_.path().Append(L"junk_1.txt"); | |
285 base::FilePath junk_path_2 = temp_dir_.path().Append(L"junk_2.txt"); | |
286 DoWriteCheck(junk_path_1, true); | |
287 DoWriteCheck(junk_path_2, false); | |
288 CheckParams(); | |
289 | |
290 // Make sure we can read from this file handle when called via the system. | |
291 DoReadCheck(junk_path_1, true); | |
292 DoReadCheck(junk_path_2, false); | |
293 CheckParams(); | |
294 | |
295 ResetNtCreateFileCalls(); | |
296 } | |
297 | |
298 TEST_F(ChromeCreateFileTest, BypassTest) { | |
299 std::wstring UNC_filepath_file(L"\\\\.\\some_file.txt"); | |
300 | |
301 base::FilePath local_path; | |
302 PathService::Get(base::DIR_LOCAL_APP_DATA, &local_path); | |
303 local_path = local_path.Append(kAppDataDirName).Append( | |
304 kUserDataDirName).Append(L"default\\Preferences"); | |
305 | |
306 base::FilePath desktop_path; | |
307 PathService::Get(base::DIR_USER_DESKTOP, &desktop_path); | |
308 desktop_path = desktop_path.Append(L"Downloads\\junk.txt"); | |
309 | |
310 EXPECT_FALSE(ShouldBypass(UNC_filepath_file.c_str())); | |
311 EXPECT_FALSE(ShouldBypass(desktop_path.value().c_str())); | |
312 EXPECT_TRUE(ShouldBypass(local_path.value().c_str())); | |
313 } | |
314 | |
315 TEST_F(ChromeCreateFileTest, NtCreateFileAddressCheck) { | |
316 HMODULE ntdll_handle = ::GetModuleHandle(L"ntdll.dll"); | |
317 EXPECT_EQ(::GetProcAddress(ntdll_handle, "NtCreateFile"), | |
318 g_ntdll_lookup["NtCreateFile"]); | |
319 } | |
320 | |
321 TEST_F(ChromeCreateFileTest, ReadWriteFromNtDll) { | |
322 base::FilePath file_name = temp_dir_.path().Append(L"some_file.txt"); | |
323 DoWriteCheck(file_name, false); | |
324 DoReadCheck(file_name, false); | |
325 } | |
326 | |
327 } // namespace | |
OLD | NEW |