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