| 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, DWORD flag, 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                                     FILE_SHARE_READ, | 
|  | 214                                     NULL, | 
|  | 215                                     CREATE_ALWAYS, | 
|  | 216                                     FILE_ATTRIBUTE_NORMAL | flag, | 
|  | 217                                     NULL)); | 
|  | 218     } else { | 
|  | 219       file_handle.Set(CreateFileNTDLL(path.value().c_str(), | 
|  | 220                                       GENERIC_WRITE, | 
|  | 221                                       FILE_SHARE_READ, | 
|  | 222                                       NULL, | 
|  | 223                                       CREATE_ALWAYS, | 
|  | 224                                       FILE_ATTRIBUTE_NORMAL | flag, | 
|  | 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, DWORD flag, 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 | flag, | 
|  | 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 | flag, | 
|  | 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   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); | 
|  | 271     CheckParams(); | 
|  | 272 | 
|  | 273     if (check_reads) { | 
|  | 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); | 
|  | 277       CheckParams(); | 
|  | 278     } | 
|  | 279     base::DeleteFile(junk_path_1, false); | 
|  | 280     base::DeleteFile(junk_path_2, false); | 
|  | 281 | 
|  | 282   } | 
|  | 283 | 
|  | 284   static ChromeCreateFileTest* self_; | 
|  | 285 | 
|  | 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_; | 
|  | 293 }; | 
|  | 294 | 
|  | 295 ChromeCreateFileTest* ChromeCreateFileTest::self_ = NULL; | 
|  | 296 | 
|  | 297 // Tests --------------------------------------------------------------------- | 
|  | 298 TEST_F(ChromeCreateFileTest, CheckParams_FILE_ATTRIBUTE_NORMAL) { | 
|  | 299   RedirectNtCreateFileCalls(); | 
|  | 300   RunChecks(FILE_ATTRIBUTE_NORMAL, true); | 
|  | 301   ResetNtCreateFileCalls(); | 
|  | 302 } | 
|  | 303 | 
|  | 304 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_WRITE_THROUGH) { | 
|  | 305   RedirectNtCreateFileCalls(); | 
|  | 306   RunChecks(FILE_FLAG_WRITE_THROUGH, true); | 
|  | 307   ResetNtCreateFileCalls(); | 
|  | 308 } | 
|  | 309 | 
|  | 310 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_RANDOM_ACCESS) { | 
|  | 311   RedirectNtCreateFileCalls(); | 
|  | 312   RunChecks(FILE_FLAG_RANDOM_ACCESS, true); | 
|  | 313   ResetNtCreateFileCalls(); | 
|  | 314 } | 
|  | 315 | 
|  | 316 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_SEQUENTIAL_SCAN) { | 
|  | 317   RedirectNtCreateFileCalls(); | 
|  | 318   RunChecks(FILE_FLAG_SEQUENTIAL_SCAN, true); | 
|  | 319   ResetNtCreateFileCalls(); | 
|  | 320 } | 
|  | 321 | 
|  | 322 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_DELETE_ON_CLOSE) { | 
|  | 323   RedirectNtCreateFileCalls(); | 
|  | 324   RunChecks(FILE_FLAG_DELETE_ON_CLOSE, false); | 
|  | 325   ResetNtCreateFileCalls(); | 
|  | 326 } | 
|  | 327 | 
|  | 328 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_BACKUP_SEMANTICS) { | 
|  | 329   RedirectNtCreateFileCalls(); | 
|  | 330   RunChecks(FILE_FLAG_BACKUP_SEMANTICS, true); | 
|  | 331   ResetNtCreateFileCalls(); | 
|  | 332 } | 
|  | 333 | 
|  | 334 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_REPARSE_POINT) { | 
|  | 335   RedirectNtCreateFileCalls(); | 
|  | 336   RunChecks(FILE_FLAG_OPEN_REPARSE_POINT, true); | 
|  | 337   ResetNtCreateFileCalls(); | 
|  | 338 } | 
|  | 339 | 
|  | 340 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_NO_RECALL) { | 
|  | 341   RedirectNtCreateFileCalls(); | 
|  | 342   RunChecks(FILE_FLAG_OPEN_NO_RECALL, true); | 
|  | 343   ResetNtCreateFileCalls(); | 
|  | 344 } | 
|  | 345 | 
|  | 346 TEST_F(ChromeCreateFileTest, BypassTest) { | 
|  | 347   std::wstring UNC_filepath_file(L"\\\\.\\some_file.txt"); | 
|  | 348 | 
|  | 349   base::FilePath local_path; | 
|  | 350   PathService::Get(base::DIR_LOCAL_APP_DATA, &local_path); | 
|  | 351 | 
|  | 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"); | 
|  | 358 | 
|  | 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"); | 
|  | 365 | 
|  | 366   // Don't redirect UNC files. | 
|  | 367   EXPECT_FALSE(ShouldBypass(UNC_filepath_file.c_str())); | 
|  | 368 | 
|  | 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())); | 
|  | 372 | 
|  | 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())); | 
|  | 377 } | 
|  | 378 | 
|  | 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"]); | 
|  | 383 } | 
|  | 384 | 
|  | 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); | 
|  | 389 } | 
|  | 390 | 
|  | 391 }  // namespace | 
| OLD | NEW | 
|---|