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/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"
     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_FALSE(file_handle == INVALID_HANDLE_VALUE);
    246     ::WriteFile(file_handle, kTestData, buffer_size, &bytes_written, NULL);
    247     EXPECT_EQ(buffer_size, bytes_written);
    248   }
    249 
    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;
    254     DWORD bytes_read;
    255     char read_buffer[10];
    256 
    257     if (is_system) {
    258       file_handle.Set(::CreateFileW(path.value().c_str(),
    259                                     GENERIC_READ,
    260                                     0,
    261                                     NULL,
    262                                     OPEN_ALWAYS,
    263                                     FILE_ATTRIBUTE_NORMAL | flag,
    264                                     NULL));
    265     } else {
    266       file_handle.Set(CreateFileNTDLL(path.value().c_str(),
    267                                       GENERIC_READ,
    268                                       0,
    269                                       NULL,
    270                                       OPEN_ALWAYS,
    271                                       FILE_ATTRIBUTE_NORMAL | flag,
    272                                       NULL));
    273     }
    274 
    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));
    279   }
    280 
    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);
    287     CheckParams();
    288 
    289     if (check_reads) {
    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);
    293       CheckParams();
    294     }
    295     base::DeleteFile(junk_path_1, false);
    296     base::DeleteFile(junk_path_2, false);
    297 
    298   }
    299 
    300   static ChromeCreateFileTest* self_;
    301 
    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_;
    309 };
    310 
    311 ChromeCreateFileTest* ChromeCreateFileTest::self_ = NULL;
    312 
    313 // Tests ---------------------------------------------------------------------
    314 TEST_F(ChromeCreateFileTest, CheckParams_FILE_ATTRIBUTE_NORMAL) {
    315   RedirectNtCreateFileCalls();
    316   RunChecks(FILE_ATTRIBUTE_NORMAL, true);
    317   ResetNtCreateFileCalls();
    318 }
    319 
    320 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_WRITE_THROUGH) {
    321   RedirectNtCreateFileCalls();
    322   RunChecks(FILE_FLAG_WRITE_THROUGH, true);
    323   ResetNtCreateFileCalls();
    324 }
    325 
    326 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_RANDOM_ACCESS) {
    327   RedirectNtCreateFileCalls();
    328   RunChecks(FILE_FLAG_RANDOM_ACCESS, true);
    329   ResetNtCreateFileCalls();
    330 }
    331 
    332 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_SEQUENTIAL_SCAN) {
    333   RedirectNtCreateFileCalls();
    334   RunChecks(FILE_FLAG_SEQUENTIAL_SCAN, true);
    335   ResetNtCreateFileCalls();
    336 }
    337 
    338 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_DELETE_ON_CLOSE) {
    339   RedirectNtCreateFileCalls();
    340   RunChecks(FILE_FLAG_DELETE_ON_CLOSE, false);
    341   ResetNtCreateFileCalls();
    342 }
    343 
    344 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_BACKUP_SEMANTICS) {
    345   RedirectNtCreateFileCalls();
    346   RunChecks(FILE_FLAG_BACKUP_SEMANTICS, true);
    347   ResetNtCreateFileCalls();
    348 }
    349 
    350 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_REPARSE_POINT) {
    351   RedirectNtCreateFileCalls();
    352   RunChecks(FILE_FLAG_OPEN_REPARSE_POINT, true);
    353   ResetNtCreateFileCalls();
    354 }
    355 
    356 TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_NO_RECALL) {
    357   RedirectNtCreateFileCalls();
    358   RunChecks(FILE_FLAG_OPEN_NO_RECALL, true);
    359   ResetNtCreateFileCalls();
    360 }
    361 
    362 TEST_F(ChromeCreateFileTest, BypassTest) {
    363   std::wstring UNC_filepath_file(L"\\\\.\\some_file.txt");
    364 
    365   base::FilePath local_path;
    366   PathService::Get(base::DIR_LOCAL_APP_DATA, &local_path);
    367 
    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");
    374 
    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");
    381 
    382   // Don't redirect UNC files.
    383   EXPECT_FALSE(ShouldBypass(UNC_filepath_file.c_str()));
    384 
    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()));
    388 
    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()));
    393 }
    394 
    395 TEST_F(ChromeCreateFileTest, ReadWriteFromNtDll) {
    396   UnsetThunkStorage();
    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);
    400 }
    401 
    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);
    406 }
    407 
    408 }  // namespace
    409