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/nt_internals.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "testing/platform_test.h"
30 // Test fixtures -------------------------------------------------------------
32 class ChromeCreateFileTest
: public PlatformTest
{
34 struct NtCreateFileParams
{
35 ACCESS_MASK desired_access
;
36 OBJECT_ATTRIBUTES object_attributes
;
37 PLARGE_INTEGER allocation_size
;
38 ULONG file_attributes
;
40 ULONG create_disposition
;
51 template<CallPath path
>
52 static NTSTATUS WINAPI
FakeNtCreateFile(
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
,
60 ULONG create_disposition
,
64 return self_
->HandleCreateFileCall(file_handle
,
78 virtual void SetUp() OVERRIDE
{
79 original_thread_
= base::PlatformThread::CurrentId();
81 PlatformTest::SetUp();
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());
90 void RedirectNtCreateFileCalls() {
92 reinterpret_cast<NtCreateFileFunction
>(g_ntdll_lookup
["NtCreateFile"]);
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
>));
100 patcher_
.Patch(L
"kernelbase.dll", "ntdll.dll", "NtCreateFile",
101 reinterpret_cast<void(*)()>(&FakeNtCreateFile
<KERNEL
>));
104 g_ntdll_lookup
["NtCreateFile"] = reinterpret_cast<void(*)()>(
105 &ChromeCreateFileTest::FakeNtCreateFile
<ELF
>);
108 void ResetNtCreateFileCalls() {
109 g_ntdll_lookup
["NtCreateFile"] = reinterpret_cast<void*>(old_func_ptr_
);
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
,
120 ULONG create_disposition
,
121 ULONG create_options
,
124 CallPath call_path
) {
125 if (original_thread_
== base::PlatformThread::CurrentId()) {
126 SetParams(desired_access
,
135 call_path
== ELF
? &elf_params_
: &kernel_params_
);
138 // Forward the call to the real NTCreateFile.
139 return old_func_ptr_(file_handle
,
152 void SetParams(ACCESS_MASK desired_access
,
153 POBJECT_ATTRIBUTES object_attributes
,
154 PLARGE_INTEGER allocation_size
,
155 ULONG file_attributes
,
157 ULONG create_disposition
,
158 ULONG create_options
,
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
;
181 std::bitset
<32> elf((int) elf_params_
.desired_access
);
182 std::bitset
<32> ker((int) kernel_params_
.desired_access
);
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
);
204 void DoWriteCheck(const base::FilePath
& path
, DWORD flag
, bool is_system
) {
205 base::win::ScopedHandle file_handle
;
206 const char kTestData
[] = "0123456789";
207 int buffer_size
= sizeof(kTestData
) - 1;
211 file_handle
.Set(::CreateFileW(path
.value().c_str(),
216 FILE_ATTRIBUTE_NORMAL
| flag
,
219 file_handle
.Set(CreateFileNTDLL(path
.value().c_str(),
224 FILE_ATTRIBUTE_NORMAL
| flag
,
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
);
234 void DoReadCheck(const base::FilePath
& path
, DWORD flag
, bool is_system
) {
235 base::win::ScopedHandle file_handle
;
236 const char kTestData
[] = "0123456789";
237 int buffer_size
= sizeof(kTestData
) - 1;
239 char read_buffer
[10];
242 file_handle
.Set(::CreateFileW(path
.value().c_str(),
247 FILE_ATTRIBUTE_NORMAL
| flag
,
250 file_handle
.Set(CreateFileNTDLL(path
.value().c_str(),
255 FILE_ATTRIBUTE_NORMAL
| flag
,
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
));
265 void RunChecks(DWORD flag
, bool check_reads
) {
266 // Make sure we can write to this file handle when called via the system.
267 base::FilePath junk_path_1
= temp_dir_
.path().Append(L
"junk_1.txt");
268 base::FilePath junk_path_2
= temp_dir_
.path().Append(L
"junk_2.txt");
269 DoWriteCheck(junk_path_1
, flag
, true);
270 DoWriteCheck(junk_path_2
, flag
, false);
274 // Make sure we can read from this file handle when called via the system.
275 DoReadCheck(junk_path_1
, flag
, true);
276 DoReadCheck(junk_path_2
, flag
, false);
279 base::DeleteFile(junk_path_1
, false);
280 base::DeleteFile(junk_path_2
, false);
284 static ChromeCreateFileTest
* self_
;
286 NtCreateFileFunction old_func_ptr_
;
287 base::ScopedTempDir temp_dir_
;
288 base::ScopedTempDir temp_dir2_
;
289 base::win::IATPatchFunction patcher_
;
290 NtCreateFileParams kernel_params_
;
291 NtCreateFileParams elf_params_
;
292 base::PlatformThreadId original_thread_
;
295 ChromeCreateFileTest
* ChromeCreateFileTest::self_
= NULL
;
297 // Tests ---------------------------------------------------------------------
298 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_ATTRIBUTE_NORMAL
) {
299 RedirectNtCreateFileCalls();
300 RunChecks(FILE_ATTRIBUTE_NORMAL
, true);
301 ResetNtCreateFileCalls();
304 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_WRITE_THROUGH
) {
305 RedirectNtCreateFileCalls();
306 RunChecks(FILE_FLAG_WRITE_THROUGH
, true);
307 ResetNtCreateFileCalls();
310 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_RANDOM_ACCESS
) {
311 RedirectNtCreateFileCalls();
312 RunChecks(FILE_FLAG_RANDOM_ACCESS
, true);
313 ResetNtCreateFileCalls();
316 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_SEQUENTIAL_SCAN
) {
317 RedirectNtCreateFileCalls();
318 RunChecks(FILE_FLAG_SEQUENTIAL_SCAN
, true);
319 ResetNtCreateFileCalls();
322 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_DELETE_ON_CLOSE
) {
323 RedirectNtCreateFileCalls();
324 RunChecks(FILE_FLAG_DELETE_ON_CLOSE
, false);
325 ResetNtCreateFileCalls();
328 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_BACKUP_SEMANTICS
) {
329 RedirectNtCreateFileCalls();
330 RunChecks(FILE_FLAG_BACKUP_SEMANTICS
, true);
331 ResetNtCreateFileCalls();
334 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_OPEN_REPARSE_POINT
) {
335 RedirectNtCreateFileCalls();
336 RunChecks(FILE_FLAG_OPEN_REPARSE_POINT
, true);
337 ResetNtCreateFileCalls();
340 TEST_F(ChromeCreateFileTest
, CheckParams_FILE_FLAG_OPEN_NO_RECALL
) {
341 RedirectNtCreateFileCalls();
342 RunChecks(FILE_FLAG_OPEN_NO_RECALL
, true);
343 ResetNtCreateFileCalls();
346 TEST_F(ChromeCreateFileTest
, BypassTest
) {
347 std::wstring
UNC_filepath_file(L
"\\\\.\\some_file.txt");
349 base::FilePath local_path
;
350 PathService::Get(base::DIR_LOCAL_APP_DATA
, &local_path
);
352 base::FilePath local_prefs_path
= local_path
.Append(kAppDataDirName
).Append(
353 kUserDataDirName
).Append(L
"default\\Preferences");
354 base::FilePath local_state_path
= local_path
.Append(kAppDataDirName
).Append(
355 kUserDataDirName
).Append(L
"ninja\\Local State");
356 base::FilePath local_junk_path
= local_path
.Append(kAppDataDirName
).Append(
357 kUserDataDirName
).Append(L
"default\\Junk");
359 base::FilePath desktop_path
;
360 PathService::Get(base::DIR_USER_DESKTOP
, &desktop_path
);
361 base::FilePath desktop_junk_path
=
362 desktop_path
.Append(L
"Downloads\\junk.txt");
363 base::FilePath desktop_prefs_path
=
364 desktop_path
.Append(L
"Downloads\\Preferences");
366 // Don't redirect UNC files.
367 EXPECT_FALSE(ShouldBypass(UNC_filepath_file
.c_str()));
369 // Don't redirect if file is not in UserData directory.
370 EXPECT_FALSE(ShouldBypass(desktop_junk_path
.value().c_str()));
371 EXPECT_FALSE(ShouldBypass(desktop_prefs_path
.value().c_str()));
373 // Only redirect "Preferences" and "Local State" files.
374 EXPECT_TRUE(ShouldBypass(local_prefs_path
.value().c_str()));
375 EXPECT_TRUE(ShouldBypass(local_state_path
.value().c_str()));
376 EXPECT_FALSE(ShouldBypass(local_junk_path
.value().c_str()));
379 TEST_F(ChromeCreateFileTest
, NtCreateFileAddressCheck
) {
380 HMODULE ntdll_handle
= ::GetModuleHandle(L
"ntdll.dll");
381 EXPECT_EQ(::GetProcAddress(ntdll_handle
, "NtCreateFile"),
382 g_ntdll_lookup
["NtCreateFile"]);
385 TEST_F(ChromeCreateFileTest
, ReadWriteFromNtDll
) {
386 base::FilePath file_name
= temp_dir_
.path().Append(L
"some_file.txt");
387 DoWriteCheck(file_name
, FILE_ATTRIBUTE_NORMAL
, false);
388 DoReadCheck(file_name
, FILE_ATTRIBUTE_NORMAL
, false);