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.
5 #include "chrome_elf/create_file/chrome_create_file.h"
12 #include "base/base_paths_win.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.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/interception_internal.h"
24 #include "sandbox/win/src/nt_internals.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "testing/platform_test.h"
31 // Test fixtures -------------------------------------------------------------
33 class ChromeCreateFileTest
: public PlatformTest
{
35 struct NtCreateFileParams
{
36 ACCESS_MASK desired_access
;
37 OBJECT_ATTRIBUTES object_attributes
;
38 PLARGE_INTEGER allocation_size
;
39 ULONG file_attributes
;
41 ULONG create_disposition
;
52 template<CallPath path
>
53 static NTSTATUS WINAPI
FakeNtCreateFile(
55 ACCESS_MASK desired_access
,
56 POBJECT_ATTRIBUTES object_attributes
,
57 PIO_STATUS_BLOCK io_status_block
,
58 PLARGE_INTEGER allocation_size
,
59 ULONG file_attributes
,
61 ULONG create_disposition
,
65 return self_
->HandleCreateFileCall(file_handle
,
79 void SetUp() override
{
80 original_thread_
= base::PlatformThread::CurrentId();
82 PlatformTest::SetUp();
84 base::FilePath user_data_dir
;
85 PathService::Get(base::DIR_LOCAL_APP_DATA
, &user_data_dir
);
86 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDirUnderPath(user_data_dir
));
87 ASSERT_TRUE(temp_dir2_
.CreateUniqueTempDir());
91 void UnsetThunkStorage() {
92 DWORD old_protect
= 0;
93 EXPECT_TRUE(::VirtualProtect(&g_nt_thunk_storage
,
94 sizeof(g_nt_thunk_storage
),
95 PAGE_EXECUTE_READWRITE
,
97 memset(&g_nt_thunk_storage
, 0, sizeof(g_nt_thunk_storage
));
99 EXPECT_TRUE(::VirtualProtect(&g_nt_thunk_storage
,
100 sizeof(g_nt_thunk_storage
),
105 void RedirectNtCreateFileCalls() {
108 reinterpret_cast<NtCreateFileFunction
>(g_ntdll_lookup
["NtCreateFile"]);
110 // KernelBase.dll only exists for Win7 and later, prior to that, kernel32
111 // imports from ntdll directly.
112 if (base::win::GetVersion() < base::win::VERSION_WIN7
) {
113 patcher_
.Patch(L
"kernel32.dll", "ntdll.dll", "NtCreateFile",
114 reinterpret_cast<void(*)()>(&FakeNtCreateFile
<KERNEL
>));
116 patcher_
.Patch(L
"kernelbase.dll", "ntdll.dll", "NtCreateFile",
117 reinterpret_cast<void(*)()>(&FakeNtCreateFile
<KERNEL
>));
120 g_ntdll_lookup
["NtCreateFile"] = reinterpret_cast<void(*)()>(
121 &ChromeCreateFileTest::FakeNtCreateFile
<ELF
>);
124 void ResetNtCreateFileCalls() {
125 g_ntdll_lookup
["NtCreateFile"] = reinterpret_cast<void*>(old_func_ptr_
);
129 NTSTATUS
HandleCreateFileCall(PHANDLE file_handle
,
130 ACCESS_MASK desired_access
,
131 POBJECT_ATTRIBUTES object_attributes
,
132 PIO_STATUS_BLOCK io_status_block
,
133 PLARGE_INTEGER allocation_size
,
134 ULONG file_attributes
,
136 ULONG create_disposition
,
137 ULONG create_options
,
140 CallPath call_path
) {
141 if (original_thread_
== base::PlatformThread::CurrentId()) {
142 SetParams(desired_access
,
151 call_path
== ELF
? &elf_params_
: &kernel_params_
);
154 // Forward the call to the real NTCreateFile.
155 return old_func_ptr_(file_handle
,
168 void SetParams(ACCESS_MASK desired_access
,
169 POBJECT_ATTRIBUTES object_attributes
,
170 PLARGE_INTEGER allocation_size
,
171 ULONG file_attributes
,
173 ULONG create_disposition
,
174 ULONG create_options
,
177 NtCreateFileParams
* params
) {
178 params
->desired_access
= desired_access
;
179 params
->object_attributes
.Length
= object_attributes
->Length
;
180 params
->object_attributes
.ObjectName
= object_attributes
->ObjectName
;
181 params
->object_attributes
.RootDirectory
= object_attributes
->RootDirectory
;
182 params
->object_attributes
.Attributes
= object_attributes
->Attributes
;
183 params
->object_attributes
.SecurityDescriptor
=
184 object_attributes
->SecurityDescriptor
;
185 params
->object_attributes
.SecurityQualityOfService
=
186 object_attributes
->SecurityQualityOfService
;
187 params
->allocation_size
= allocation_size
;
188 params
->file_attributes
= file_attributes
;
189 params
->share_access
= share_access
;
190 params
->create_disposition
= create_disposition
;
191 params
->create_options
= create_options
;
192 params
->ea_buffer
= ea_buffer
;
193 params
->ea_length
= ea_length
;
197 std::bitset
<32> elf((int) elf_params_
.desired_access
);
198 std::bitset
<32> ker((int) kernel_params_
.desired_access
);
200 EXPECT_EQ(kernel_params_
.desired_access
, elf_params_
.desired_access
)
201 << elf
<< "\n" << ker
;
202 EXPECT_EQ(kernel_params_
.object_attributes
.Length
,
203 elf_params_
.object_attributes
.Length
);
204 EXPECT_EQ(kernel_params_
.object_attributes
.RootDirectory
,
205 elf_params_
.object_attributes
.RootDirectory
);
206 EXPECT_EQ(kernel_params_
.object_attributes
.Attributes
,
207 elf_params_
.object_attributes
.Attributes
);
208 EXPECT_EQ(kernel_params_
.object_attributes
.SecurityDescriptor
,
209 elf_params_
.object_attributes
.SecurityDescriptor
);
210 EXPECT_EQ(kernel_params_
.allocation_size
, elf_params_
.allocation_size
);
211 EXPECT_EQ(kernel_params_
.file_attributes
, elf_params_
.file_attributes
);
212 EXPECT_EQ(kernel_params_
.share_access
, elf_params_
.share_access
);
213 EXPECT_EQ(kernel_params_
.create_disposition
,
214 elf_params_
.create_disposition
);
215 EXPECT_EQ(kernel_params_
.create_options
, elf_params_
.create_options
);
216 EXPECT_EQ(kernel_params_
.ea_buffer
, elf_params_
.ea_buffer
);
217 EXPECT_EQ(kernel_params_
.ea_length
, elf_params_
.ea_length
);
220 void DoWriteCheck(const base::FilePath
& path
, DWORD flag
, bool is_system
) {
221 base::win::ScopedHandle file_handle
;
222 const char kTestData
[] = "0123456789";
223 int buffer_size
= sizeof(kTestData
) - 1;
227 file_handle
.Set(::CreateFileW(path
.value().c_str(),
232 FILE_ATTRIBUTE_NORMAL
| flag
,
235 file_handle
.Set(CreateFileNTDLL(path
.value().c_str(),
240 FILE_ATTRIBUTE_NORMAL
| flag
,
245 EXPECT_TRUE(file_handle
.IsValid());
246 ::WriteFile(file_handle
.Get(), kTestData
, buffer_size
, &bytes_written
,
248 EXPECT_EQ(buffer_size
, bytes_written
);
251 void DoReadCheck(const base::FilePath
& path
, DWORD flag
, bool is_system
) {
252 base::win::ScopedHandle file_handle
;
253 const char kTestData
[] = "0123456789";
254 int buffer_size
= sizeof(kTestData
) - 1;
256 char read_buffer
[10];
259 file_handle
.Set(::CreateFileW(path
.value().c_str(),
264 FILE_ATTRIBUTE_NORMAL
| flag
,
267 file_handle
.Set(CreateFileNTDLL(path
.value().c_str(),
272 FILE_ATTRIBUTE_NORMAL
| flag
,
276 EXPECT_TRUE(file_handle
.IsValid());
277 ::ReadFile(file_handle
.Get(), read_buffer
, buffer_size
, &bytes_read
, NULL
);
278 EXPECT_EQ(buffer_size
, bytes_read
);
279 EXPECT_EQ(0, memcmp(kTestData
, read_buffer
, bytes_read
));
282 void RunChecks(DWORD flag
, bool check_reads
) {
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
, flag
, true);
287 DoWriteCheck(junk_path_2
, flag
, false);
291 // Make sure we can read from this file handle when called via the system.
292 DoReadCheck(junk_path_1
, flag
, true);
293 DoReadCheck(junk_path_2
, flag
, false);
296 base::DeleteFile(junk_path_1
, false);
297 base::DeleteFile(junk_path_2
, false);
301 static ChromeCreateFileTest
* self_
;
303 NtCreateFileFunction old_func_ptr_
;
304 base::ScopedTempDir temp_dir_
;
305 base::ScopedTempDir temp_dir2_
;
306 base::win::IATPatchFunction patcher_
;
307 NtCreateFileParams kernel_params_
;
308 NtCreateFileParams elf_params_
;
309 base::PlatformThreadId original_thread_
;
312 ChromeCreateFileTest
* ChromeCreateFileTest::self_
= NULL
;
314 // Tests ---------------------------------------------------------------------
315 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_ATTRIBUTE_NORMAL
) {
316 RedirectNtCreateFileCalls();
317 RunChecks(FILE_ATTRIBUTE_NORMAL
, true);
318 ResetNtCreateFileCalls();
321 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_WRITE_THROUGH
) {
322 RedirectNtCreateFileCalls();
323 RunChecks(FILE_FLAG_WRITE_THROUGH
, true);
324 ResetNtCreateFileCalls();
327 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_RANDOM_ACCESS
) {
328 RedirectNtCreateFileCalls();
329 RunChecks(FILE_FLAG_RANDOM_ACCESS
, true);
330 ResetNtCreateFileCalls();
333 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_SEQUENTIAL_SCAN
) {
334 RedirectNtCreateFileCalls();
335 RunChecks(FILE_FLAG_SEQUENTIAL_SCAN
, true);
336 ResetNtCreateFileCalls();
339 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_DELETE_ON_CLOSE
) {
340 RedirectNtCreateFileCalls();
341 RunChecks(FILE_FLAG_DELETE_ON_CLOSE
, false);
342 ResetNtCreateFileCalls();
345 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_BACKUP_SEMANTICS
) {
346 RedirectNtCreateFileCalls();
347 RunChecks(FILE_FLAG_BACKUP_SEMANTICS
, true);
348 ResetNtCreateFileCalls();
351 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_OPEN_REPARSE_POINT
) {
352 RedirectNtCreateFileCalls();
353 RunChecks(FILE_FLAG_OPEN_REPARSE_POINT
, true);
354 ResetNtCreateFileCalls();
357 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_OPEN_NO_RECALL
) {
358 RedirectNtCreateFileCalls();
359 RunChecks(FILE_FLAG_OPEN_NO_RECALL
, true);
360 ResetNtCreateFileCalls();
363 TEST_F(ChromeCreateFileTest
, BypassTest
) {
364 std::wstring
UNC_filepath_file(L
"\\\\.\\some_file.txt");
366 base::FilePath local_path
;
367 PathService::Get(base::DIR_LOCAL_APP_DATA
, &local_path
);
369 base::FilePath local_prefs_path
= local_path
.Append(kAppDataDirName
).Append(
370 kUserDataDirName
).Append(L
"default\\Preferences");
371 base::FilePath local_state_path
= local_path
.Append(kAppDataDirName
).Append(
372 kUserDataDirName
).Append(L
"ninja\\Local State");
373 base::FilePath local_junk_path
= local_path
.Append(kAppDataDirName
).Append(
374 kUserDataDirName
).Append(L
"default\\Junk");
376 base::FilePath desktop_path
;
377 PathService::Get(base::DIR_USER_DESKTOP
, &desktop_path
);
378 base::FilePath desktop_junk_path
=
379 desktop_path
.Append(L
"Downloads\\junk.txt");
380 base::FilePath desktop_prefs_path
=
381 desktop_path
.Append(L
"Downloads\\Preferences");
383 // Don't redirect UNC files.
384 EXPECT_FALSE(ShouldBypass(UNC_filepath_file
.c_str()));
386 // Don't redirect if file is not in UserData directory.
387 EXPECT_FALSE(ShouldBypass(desktop_junk_path
.value().c_str()));
388 EXPECT_FALSE(ShouldBypass(desktop_prefs_path
.value().c_str()));
390 // Only redirect "Preferences" and "Local State" files.
391 EXPECT_TRUE(ShouldBypass(local_prefs_path
.value().c_str()));
392 EXPECT_TRUE(ShouldBypass(local_state_path
.value().c_str()));
393 EXPECT_FALSE(ShouldBypass(local_junk_path
.value().c_str()));
396 TEST_F(ChromeCreateFileTest
, ReadWriteFromNtDll
) {
398 base::FilePath file_name
= temp_dir_
.path().Append(L
"some_file.txt");
399 DoWriteCheck(file_name
, FILE_ATTRIBUTE_NORMAL
, false);
400 DoReadCheck(file_name
, FILE_ATTRIBUTE_NORMAL
, false);
403 TEST_F(ChromeCreateFileTest
, ReadWriteFromThunk
) {
404 base::FilePath file_name
= temp_dir_
.path().Append(L
"some_file.txt");
405 DoWriteCheck(file_name
, FILE_ATTRIBUTE_NORMAL
, false);
406 DoReadCheck(file_name
, FILE_ATTRIBUTE_NORMAL
, false);