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/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/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 virtual 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_FALSE(file_handle
== INVALID_HANDLE_VALUE
);
246 ::WriteFile(file_handle
, kTestData
, buffer_size
, &bytes_written
, NULL
);
247 EXPECT_EQ(buffer_size
, bytes_written
);
250 void DoReadCheck(const base::FilePath
& path
, DWORD flag
, bool is_system
) {
251 base::win::ScopedHandle file_handle
;
252 const char kTestData
[] = "0123456789";
253 int buffer_size
= sizeof(kTestData
) - 1;
255 char read_buffer
[10];
258 file_handle
.Set(::CreateFileW(path
.value().c_str(),
263 FILE_ATTRIBUTE_NORMAL
| flag
,
266 file_handle
.Set(CreateFileNTDLL(path
.value().c_str(),
271 FILE_ATTRIBUTE_NORMAL
| flag
,
275 EXPECT_FALSE(file_handle
== INVALID_HANDLE_VALUE
);
276 ::ReadFile(file_handle
, read_buffer
, buffer_size
, &bytes_read
, NULL
);
277 EXPECT_EQ(buffer_size
, bytes_read
);
278 EXPECT_EQ(0, memcmp(kTestData
, read_buffer
, bytes_read
));
281 void RunChecks(DWORD flag
, bool check_reads
) {
282 // Make sure we can write to this file handle when called via the system.
283 base::FilePath junk_path_1
= temp_dir_
.path().Append(L
"junk_1.txt");
284 base::FilePath junk_path_2
= temp_dir_
.path().Append(L
"junk_2.txt");
285 DoWriteCheck(junk_path_1
, flag
, true);
286 DoWriteCheck(junk_path_2
, flag
, false);
290 // Make sure we can read from this file handle when called via the system.
291 DoReadCheck(junk_path_1
, flag
, true);
292 DoReadCheck(junk_path_2
, flag
, false);
295 base::DeleteFile(junk_path_1
, false);
296 base::DeleteFile(junk_path_2
, false);
300 static ChromeCreateFileTest
* self_
;
302 NtCreateFileFunction old_func_ptr_
;
303 base::ScopedTempDir temp_dir_
;
304 base::ScopedTempDir temp_dir2_
;
305 base::win::IATPatchFunction patcher_
;
306 NtCreateFileParams kernel_params_
;
307 NtCreateFileParams elf_params_
;
308 base::PlatformThreadId original_thread_
;
311 ChromeCreateFileTest
* ChromeCreateFileTest::self_
= NULL
;
313 // Tests ---------------------------------------------------------------------
314 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_ATTRIBUTE_NORMAL
) {
315 RedirectNtCreateFileCalls();
316 RunChecks(FILE_ATTRIBUTE_NORMAL
, true);
317 ResetNtCreateFileCalls();
320 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_WRITE_THROUGH
) {
321 RedirectNtCreateFileCalls();
322 RunChecks(FILE_FLAG_WRITE_THROUGH
, true);
323 ResetNtCreateFileCalls();
326 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_RANDOM_ACCESS
) {
327 RedirectNtCreateFileCalls();
328 RunChecks(FILE_FLAG_RANDOM_ACCESS
, true);
329 ResetNtCreateFileCalls();
332 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_SEQUENTIAL_SCAN
) {
333 RedirectNtCreateFileCalls();
334 RunChecks(FILE_FLAG_SEQUENTIAL_SCAN
, true);
335 ResetNtCreateFileCalls();
338 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_DELETE_ON_CLOSE
) {
339 RedirectNtCreateFileCalls();
340 RunChecks(FILE_FLAG_DELETE_ON_CLOSE
, false);
341 ResetNtCreateFileCalls();
344 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_BACKUP_SEMANTICS
) {
345 RedirectNtCreateFileCalls();
346 RunChecks(FILE_FLAG_BACKUP_SEMANTICS
, true);
347 ResetNtCreateFileCalls();
350 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_OPEN_REPARSE_POINT
) {
351 RedirectNtCreateFileCalls();
352 RunChecks(FILE_FLAG_OPEN_REPARSE_POINT
, true);
353 ResetNtCreateFileCalls();
356 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_OPEN_NO_RECALL
) {
357 RedirectNtCreateFileCalls();
358 RunChecks(FILE_FLAG_OPEN_NO_RECALL
, true);
359 ResetNtCreateFileCalls();
362 TEST_F(ChromeCreateFileTest
, BypassTest
) {
363 std::wstring
UNC_filepath_file(L
"\\\\.\\some_file.txt");
365 base::FilePath local_path
;
366 PathService::Get(base::DIR_LOCAL_APP_DATA
, &local_path
);
368 base::FilePath local_prefs_path
= local_path
.Append(kAppDataDirName
).Append(
369 kUserDataDirName
).Append(L
"default\\Preferences");
370 base::FilePath local_state_path
= local_path
.Append(kAppDataDirName
).Append(
371 kUserDataDirName
).Append(L
"ninja\\Local State");
372 base::FilePath local_junk_path
= local_path
.Append(kAppDataDirName
).Append(
373 kUserDataDirName
).Append(L
"default\\Junk");
375 base::FilePath desktop_path
;
376 PathService::Get(base::DIR_USER_DESKTOP
, &desktop_path
);
377 base::FilePath desktop_junk_path
=
378 desktop_path
.Append(L
"Downloads\\junk.txt");
379 base::FilePath desktop_prefs_path
=
380 desktop_path
.Append(L
"Downloads\\Preferences");
382 // Don't redirect UNC files.
383 EXPECT_FALSE(ShouldBypass(UNC_filepath_file
.c_str()));
385 // Don't redirect if file is not in UserData directory.
386 EXPECT_FALSE(ShouldBypass(desktop_junk_path
.value().c_str()));
387 EXPECT_FALSE(ShouldBypass(desktop_prefs_path
.value().c_str()));
389 // Only redirect "Preferences" and "Local State" files.
390 EXPECT_TRUE(ShouldBypass(local_prefs_path
.value().c_str()));
391 EXPECT_TRUE(ShouldBypass(local_state_path
.value().c_str()));
392 EXPECT_FALSE(ShouldBypass(local_junk_path
.value().c_str()));
395 TEST_F(ChromeCreateFileTest
, ReadWriteFromNtDll
) {
397 base::FilePath file_name
= temp_dir_
.path().Append(L
"some_file.txt");
398 DoWriteCheck(file_name
, FILE_ATTRIBUTE_NORMAL
, false);
399 DoReadCheck(file_name
, FILE_ATTRIBUTE_NORMAL
, false);
402 TEST_F(ChromeCreateFileTest
, ReadWriteFromThunk
) {
403 base::FilePath file_name
= temp_dir_
.path().Append(L
"some_file.txt");
404 DoWriteCheck(file_name
, FILE_ATTRIBUTE_NORMAL
, false);
405 DoReadCheck(file_name
, FILE_ATTRIBUTE_NORMAL
, false);