Home | History | Annotate | Download | only in create_file
      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/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"
     27 
     28 
     29 namespace {
     30 
     31 // Test fixtures -------------------------------------------------------------
     32 
     33 class ChromeCreateFileTest : public PlatformTest {
     34  protected:
     35   struct NtCreateFileParams {
     36     ACCESS_MASK desired_access;
     37     OBJECT_ATTRIBUTES object_attributes;
     38     PLARGE_INTEGER allocation_size;
     39     ULONG file_attributes;
     40     ULONG share_access;
     41     ULONG create_disposition;
     42     ULONG create_options;
     43     PVOID ea_buffer;
     44     ULONG ea_length;
     45   };
     46 
     47   enum CallPath {
     48     ELF,
     49     KERNEL
     50   };
     51 
     52   template<CallPath path>
     53   static NTSTATUS WINAPI FakeNtCreateFile(
     54       PHANDLE file_handle,
     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,
     60       ULONG share_access,
     61       ULONG create_disposition,
     62       ULONG create_options,
     63       PVOID ea_buffer,
     64       ULONG ea_length) {
     65     return self_->HandleCreateFileCall(file_handle,
     66         desired_access,
     67         object_attributes,
     68         io_status_block,
     69         allocation_size,
     70         file_attributes,
     71         share_access,
     72         create_disposition,
     73         create_options,
     74         ea_buffer,
     75         ea_length,
     76         path);
     77   }
     78 
     79   virtual void SetUp() OVERRIDE {
     80     original_thread_ = base::PlatformThread::CurrentId();
     81     InitCache();
     82     PlatformTest::SetUp();
     83 
     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());
     88     self_ = this;
     89   }
     90 
     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,
     96                                  &old_protect));
     97     memset(&g_nt_thunk_storage, 0, sizeof(g_nt_thunk_storage));
     98 
     99     EXPECT_TRUE(::VirtualProtect(&g_nt_thunk_storage,
    100                                  sizeof(g_nt_thunk_storage),
    101                                  PAGE_EXECUTE_READ,
    102                                  &old_protect));
    103   }
    104 
    105   void RedirectNtCreateFileCalls() {
    106     UnsetThunkStorage();
    107     old_func_ptr_ =
    108         reinterpret_cast<NtCreateFileFunction>(g_ntdll_lookup["NtCreateFile"]);
    109 
    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>));
    115     } else {
    116       patcher_.Patch(L"kernelbase.dll", "ntdll.dll", "NtCreateFile",
    117           reinterpret_cast<void(*)()>(&FakeNtCreateFile<KERNEL>));
    118     }
    119 
    120     g_ntdll_lookup["NtCreateFile"] = reinterpret_cast<void(*)()>(
    121         &ChromeCreateFileTest::FakeNtCreateFile<ELF>);
    122   }
    123 
    124   void ResetNtCreateFileCalls() {
    125     g_ntdll_lookup["NtCreateFile"] = reinterpret_cast<void*>(old_func_ptr_);
    126     patcher_.Unpatch();
    127   }
    128 
    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,
    135       ULONG share_access,
    136       ULONG create_disposition,
    137       ULONG create_options,
    138       PVOID ea_buffer,
    139       ULONG ea_length,
    140       CallPath call_path) {
    141     if (original_thread_ == base::PlatformThread::CurrentId()) {
    142       SetParams(desired_access,
    143           object_attributes,
    144           allocation_size,
    145           file_attributes,
    146           share_access,
    147           create_disposition,
    148           create_options,
    149           ea_buffer,
    150           ea_length,
    151           call_path == ELF ? &elf_params_ : &kernel_params_);
    152     }
    153 
    154     // Forward the call to the real NTCreateFile.
    155     return old_func_ptr_(file_handle,
    156         desired_access,
    157         object_attributes,
    158         io_status_block,
    159         allocation_size,
    160         file_attributes,
    161         share_access,
    162         create_disposition,
    163         create_options,
    164         ea_buffer,
    165         ea_length);
    166   }
    167 
    168   void SetParams(ACCESS_MASK desired_access,
    169       POBJECT_ATTRIBUTES object_attributes,
    170       PLARGE_INTEGER allocation_size,
    171       ULONG file_attributes,
    172       ULONG share_access,
    173       ULONG create_disposition,
    174       ULONG create_options,
    175       PVOID ea_buffer,
    176       ULONG ea_length,
    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;
    194   }
    195 
    196   void CheckParams() {
    197     std::bitset<32> elf((int) elf_params_.desired_access);
    198     std::bitset<32> ker((int) kernel_params_.desired_access);
    199 
    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);
    218   }
    219 
    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;
    224     DWORD bytes_written;
    225 
    226     if (is_system) {
    227       file_handle.Set(::CreateFileW(path.value().c_str(),
    228                                     GENERIC_WRITE,
    229                                     FILE_SHARE_READ,
    230                                     NULL,
    231                                     CREATE_ALWAYS,
    232                                     FILE_ATTRIBUTE_NORMAL | flag,
    233                                     NULL));
    234     } else {
    235       file_handle.Set(CreateFileNTDLL(path.value().c_str(),
    236                                       GENERIC_WRITE,
    237                                       FILE_SHARE_READ,
    238                                       NULL,
    239                                       CREATE_ALWAYS,
    240                                       FILE_ATTRIBUTE_NORMAL | flag,
    241                                       NULL));
    242     }
    243 
    244 
    245     EXPECT_TRUE(file_handle.IsValid());
    246     ::WriteFile(file_handle.Get(), kTestData, buffer_size, &bytes_written,
    247                 NULL);
    248     EXPECT_EQ(buffer_size, bytes_written);
    249   }
    250 
    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;
    255     DWORD bytes_read;
    256     char read_buffer[10];
    257 
    258     if (is_system) {
    259       file_handle.Set(::CreateFileW(path.value().c_str(),
    260                                     GENERIC_READ,
    261                                     0,
    262                                     NULL,
    263                                     OPEN_ALWAYS,
    264                                     FILE_ATTRIBUTE_NORMAL | flag,
    265                                     NULL));
    266     } else {
    267       file_handle.Set(CreateFileNTDLL(path.value().c_str(),
    268                                       GENERIC_READ,
    269                                       0,
    270                                       NULL,
    271                                       OPEN_ALWAYS,
    272                                       FILE_ATTRIBUTE_NORMAL | flag,
    273                                       NULL));
    274     }
    275 
    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));
    280   }
    281 
    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);
    288     CheckParams();
    289 
    290     if (check_reads) {
    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);
    294       CheckParams();
    295     }
    296     base::DeleteFile(junk_path_1, false);
    297     base::DeleteFile(junk_path_2, false);
    298 
    299   }
    300 
    301   static ChromeCreateFileTest* self_;
    302 
    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_;
    310 };
    311 
    312 ChromeCreateFileTest* ChromeCreateFileTest::self_ = NULL;
    313 
    314 // Tests ---------------------------------------------------------------------
    315 TEST_F(ChromeCreateFileTest, CheckParams_FILE_ATTRIBUTE_NORMAL) {
    316   RedirectNtCreateFileCalls();
    317   RunChecks(FILE_ATTRIBUTE_NORMAL, true);
    318   ResetNtCreateFileCalls();
    319 }
    320 
    321 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_WRITE_THROUGH) {
    322   RedirectNtCreateFileCalls();
    323   RunChecks(FILE_FLAG_WRITE_THROUGH, true);
    324   ResetNtCreateFileCalls();
    325 }
    326 
    327 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_RANDOM_ACCESS) {
    328   RedirectNtCreateFileCalls();
    329   RunChecks(FILE_FLAG_RANDOM_ACCESS, true);
    330   ResetNtCreateFileCalls();
    331 }
    332 
    333 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_SEQUENTIAL_SCAN) {
    334   RedirectNtCreateFileCalls();
    335   RunChecks(FILE_FLAG_SEQUENTIAL_SCAN, true);
    336   ResetNtCreateFileCalls();
    337 }
    338 
    339 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_DELETE_ON_CLOSE) {
    340   RedirectNtCreateFileCalls();
    341   RunChecks(FILE_FLAG_DELETE_ON_CLOSE, false);
    342   ResetNtCreateFileCalls();
    343 }
    344 
    345 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_BACKUP_SEMANTICS) {
    346   RedirectNtCreateFileCalls();
    347   RunChecks(FILE_FLAG_BACKUP_SEMANTICS, true);
    348   ResetNtCreateFileCalls();
    349 }
    350 
    351 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_REPARSE_POINT) {
    352   RedirectNtCreateFileCalls();
    353   RunChecks(FILE_FLAG_OPEN_REPARSE_POINT, true);
    354   ResetNtCreateFileCalls();
    355 }
    356 
    357 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_NO_RECALL) {
    358   RedirectNtCreateFileCalls();
    359   RunChecks(FILE_FLAG_OPEN_NO_RECALL, true);
    360   ResetNtCreateFileCalls();
    361 }
    362 
    363 TEST_F(ChromeCreateFileTest, BypassTest) {
    364   std::wstring UNC_filepath_file(L"\\\\.\\some_file.txt");
    365 
    366   base::FilePath local_path;
    367   PathService::Get(base::DIR_LOCAL_APP_DATA, &local_path);
    368 
    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");
    375 
    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");
    382 
    383   // Don't redirect UNC files.
    384   EXPECT_FALSE(ShouldBypass(UNC_filepath_file.c_str()));
    385 
    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()));
    389 
    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()));
    394 }
    395 
    396 TEST_F(ChromeCreateFileTest, ReadWriteFromNtDll) {
    397   UnsetThunkStorage();
    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);
    401 }
    402 
    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);
    407 }
    408 
    409 }  // namespace
    410