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 29namespace { 30 31// Test fixtures ------------------------------------------------------------- 32 33class 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 312ChromeCreateFileTest* ChromeCreateFileTest::self_ = NULL; 313 314// Tests --------------------------------------------------------------------- 315TEST_F(ChromeCreateFileTest, CheckParams_FILE_ATTRIBUTE_NORMAL) { 316 RedirectNtCreateFileCalls(); 317 RunChecks(FILE_ATTRIBUTE_NORMAL, true); 318 ResetNtCreateFileCalls(); 319} 320 321TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_WRITE_THROUGH) { 322 RedirectNtCreateFileCalls(); 323 RunChecks(FILE_FLAG_WRITE_THROUGH, true); 324 ResetNtCreateFileCalls(); 325} 326 327TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_RANDOM_ACCESS) { 328 RedirectNtCreateFileCalls(); 329 RunChecks(FILE_FLAG_RANDOM_ACCESS, true); 330 ResetNtCreateFileCalls(); 331} 332 333TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_SEQUENTIAL_SCAN) { 334 RedirectNtCreateFileCalls(); 335 RunChecks(FILE_FLAG_SEQUENTIAL_SCAN, true); 336 ResetNtCreateFileCalls(); 337} 338 339TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_DELETE_ON_CLOSE) { 340 RedirectNtCreateFileCalls(); 341 RunChecks(FILE_FLAG_DELETE_ON_CLOSE, false); 342 ResetNtCreateFileCalls(); 343} 344 345TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_BACKUP_SEMANTICS) { 346 RedirectNtCreateFileCalls(); 347 RunChecks(FILE_FLAG_BACKUP_SEMANTICS, true); 348 ResetNtCreateFileCalls(); 349} 350 351TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_REPARSE_POINT) { 352 RedirectNtCreateFileCalls(); 353 RunChecks(FILE_FLAG_OPEN_REPARSE_POINT, true); 354 ResetNtCreateFileCalls(); 355} 356 357TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_NO_RECALL) { 358 RedirectNtCreateFileCalls(); 359 RunChecks(FILE_FLAG_OPEN_NO_RECALL, true); 360 ResetNtCreateFileCalls(); 361} 362 363TEST_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 396TEST_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 403TEST_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