diff options
24 files changed, 920 insertions, 13 deletions
diff --git a/chrome/app/chrome_exe_main_win.cc b/chrome/app/chrome_exe_main_win.cc index 68a2519..2dc2c94 100644 --- a/chrome/app/chrome_exe_main_win.cc +++ b/chrome/app/chrome_exe_main_win.cc @@ -128,9 +128,8 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prev, wchar_t*, int) { if (AttemptFastNotify(*CommandLine::ForCurrentProcess())) return 0; - // The purpose of this call is to force the addition of an entry in the IAT - // for chrome_elf.dll to force a load time dependency. - InitChromeElf(); + // Signal Chrome Elf that Chrome has begun to start. + SignalChromeElf(); MetroDriver metro_driver; if (metro_driver.in_metro_mode()) diff --git a/chrome/chrome_exe.gypi b/chrome/chrome_exe.gypi index abd63a3..c8f9cef 100644 --- a/chrome/chrome_exe.gypi +++ b/chrome/chrome_exe.gypi @@ -465,6 +465,8 @@ }], ['OS=="win"', { 'dependencies': [ + # Note that chrome_elf must be listed first. Do not reorder it. + '../chrome_elf/chrome_elf.gyp:chrome_elf', 'chrome_dll', 'chrome_nacl_win64', 'chrome_process_finder', @@ -474,7 +476,6 @@ '../base/base.gyp:base', '../breakpad/breakpad.gyp:breakpad_handler', '../breakpad/breakpad.gyp:breakpad_sender', - '../chrome_elf/chrome_elf.gyp:chrome_elf', '../components/components.gyp:breakpad_component', '../components/components.gyp:policy', '../sandbox/sandbox.gyp:sandbox', diff --git a/chrome_elf/DEPS b/chrome_elf/DEPS index 48e8875..ec69c8f 100644 --- a/chrome_elf/DEPS +++ b/chrome_elf/DEPS @@ -1,2 +1,3 @@ include_rules = [ + "+sandbox", ] diff --git a/chrome_elf/blacklist.gypi b/chrome_elf/blacklist.gypi new file mode 100644 index 0000000..ab5597d --- /dev/null +++ b/chrome_elf/blacklist.gypi @@ -0,0 +1,60 @@ +# Copyright 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'blacklist', + 'type': 'static_library', + 'sources': [ + 'blacklist/blacklist.cc', + 'blacklist/blacklist.h', + 'blacklist/blacklist_interceptions.cc', + 'blacklist/blacklist_interceptions.h', + ], + 'dependencies': [ + # Depend on base_static, but do NOT take a dependency on base.gyp:base + # as that would risk pulling in base's link-time dependencies which + # chrome_elf cannot do. + '../base/base.gyp:base_static', + '../sandbox/sandbox.gyp:sandbox', + ], + }, + { + 'target_name': 'blacklist_test_main_dll', + 'type': 'shared_library', + 'sources': [ + 'blacklist/test/blacklist_test_main_dll.cc', + 'blacklist/test/blacklist_test_main_dll.def', + ], + 'dependencies': [ + '../base/base.gyp:base', + 'blacklist', + ], + }, + { + 'target_name': 'blacklist_test_dll_1', + 'type': 'loadable_module', + 'sources': [ + 'blacklist/test/blacklist_test_dll_1.cc', + 'blacklist/test/blacklist_test_dll_1.def', + ], + }, + { + 'target_name': 'blacklist_test_dll_2', + 'type': 'loadable_module', + 'sources': [ + 'blacklist/test/blacklist_test_dll_2.cc', + 'blacklist/test/blacklist_test_dll_2.def', + ], + }, + { + 'target_name': 'blacklist_test_dll_3', + 'type': 'loadable_module', + 'sources': [ + 'blacklist/test/blacklist_test_dll_3.cc', + ], + }, + ], +} + diff --git a/chrome_elf/blacklist/blacklist.cc b/chrome_elf/blacklist/blacklist.cc new file mode 100644 index 0000000..f87c41a --- /dev/null +++ b/chrome_elf/blacklist/blacklist.cc @@ -0,0 +1,274 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome_elf/blacklist/blacklist.h" + +#include <string.h> + +#include "base/basictypes.h" +#include "chrome_elf/blacklist/blacklist_interceptions.h" +#include "sandbox/win/src/interception_internal.h" +#include "sandbox/win/src/internal_types.h" +#include "sandbox/win/src/sandbox_utils.h" +#include "sandbox/win/src/service_resolver.h" + +// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx +extern "C" IMAGE_DOS_HEADER __ImageBase; + +namespace blacklist{ + +const wchar_t* g_troublesome_dlls[kTroublesomeDllsMaxCount] = {}; +int g_troublesome_dlls_cur_index = 0; + +const wchar_t kRegistryBeaconPath[] = L"SOFTWARE\\Google\\Chrome\\BLBeacon"; + +} // namespace blacklist + +// Allocate storage for thunks in a RWX page of this module to save on doing +// an extra allocation at run time. +#if !defined(_WIN64) +// 64-bit images appear to not support writeable and executable pages. +// This would yield compile warning C4330. +// TODO(robertshield): Add 64 bit support. +#pragma section(".crthunk",read,write,execute) +__declspec(allocate(".crthunk")) sandbox::ThunkData g_thunk_storage; +#endif + +namespace { + +enum Version { + VERSION_PRE_XP_SP2 = 0, // Not supported. + VERSION_XP_SP2, + VERSION_SERVER_2003, // Also includes XP Pro x64 and Server 2003 R2. + VERSION_VISTA, // Also includes Windows Server 2008. + VERSION_WIN7, // Also includes Windows Server 2008 R2. + VERSION_WIN8, // Also includes Windows Server 2012. + VERSION_WIN8_1, + VERSION_WIN_LAST, // Indicates error condition. +}; + +// Whether a process is running under WOW64 (the wrapper that allows 32-bit +// processes to run on 64-bit versions of Windows). This will return +// WOW64_DISABLED for both "32-bit Chrome on 32-bit Windows" and "64-bit +// Chrome on 64-bit Windows". WOW64_UNKNOWN means "an error occurred", e.g. +// the process does not have sufficient access rights to determine this. +enum WOW64Status { + WOW64_DISABLED, + WOW64_ENABLED, + WOW64_UNKNOWN, +}; + +WOW64Status GetWOW64StatusForCurrentProcess() { + typedef BOOL (WINAPI* IsWow64ProcessFunc)(HANDLE, PBOOL); + IsWow64ProcessFunc is_wow64_process = reinterpret_cast<IsWow64ProcessFunc>( + GetProcAddress(GetModuleHandle(L"kernel32.dll"), "IsWow64Process")); + if (!is_wow64_process) + return WOW64_DISABLED; + BOOL is_wow64 = FALSE; + if (!(*is_wow64_process)(GetCurrentProcess(), &is_wow64)) + return WOW64_UNKNOWN; + return is_wow64 ? WOW64_ENABLED : WOW64_DISABLED; +} + +class OSInfo { + public: + struct VersionNumber { + int major; + int minor; + int build; + }; + + struct ServicePack { + int major; + int minor; + }; + + OSInfo() { + OSVERSIONINFOEX version_info = { sizeof(version_info) }; + GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info)); + version_number_.major = version_info.dwMajorVersion; + version_number_.minor = version_info.dwMinorVersion; + version_number_.build = version_info.dwBuildNumber; + if ((version_number_.major == 5) && (version_number_.minor > 0)) { + // Treat XP Pro x64, Home Server, and Server 2003 R2 as Server 2003. + version_ = (version_number_.minor == 1) ? VERSION_XP_SP2 : + VERSION_SERVER_2003; + if (version_ == VERSION_XP_SP2 && version_info.wServicePackMajor < 2) + version_ = VERSION_PRE_XP_SP2; + } else if (version_number_.major == 6) { + switch (version_number_.minor) { + case 0: + // Treat Windows Server 2008 the same as Windows Vista. + version_ = VERSION_VISTA; + break; + case 1: + // Treat Windows Server 2008 R2 the same as Windows 7. + version_ = VERSION_WIN7; + break; + case 2: + // Treat Windows Server 2012 the same as Windows 8. + version_ = VERSION_WIN8; + break; + default: + version_ = VERSION_WIN8_1; + break; + } + } else if (version_number_.major > 6) { + version_ = VERSION_WIN_LAST; + } else { + version_ = VERSION_PRE_XP_SP2; + } + + service_pack_.major = version_info.wServicePackMajor; + service_pack_.minor = version_info.wServicePackMinor; + } + + Version version() const { return version_; } + VersionNumber version_number() const { return version_number_; } + ServicePack service_pack() const { return service_pack_; } + + private: + Version version_; + VersionNumber version_number_; + ServicePack service_pack_; + + DISALLOW_COPY_AND_ASSIGN(OSInfo); +}; + +bool IsNonBrowserProcess() { + wchar_t* command_line = GetCommandLine(); + return (command_line && wcsstr(command_line, L"--type")); +} + +} // namespace + +namespace blacklist { + +bool CreateBeacon() { + HKEY beacon_key = NULL; + DWORD disposition = 0; + LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, + kRegistryBeaconPath, + 0, + NULL, + 0, + KEY_WRITE, + NULL, + &beacon_key, + &disposition); + bool success = (result == ERROR_SUCCESS && + disposition != REG_OPENED_EXISTING_KEY); + if (result == ERROR_SUCCESS) + ::RegCloseKey(beacon_key); + return success; +} + +bool ClearBeacon() { + LONG result = ::RegDeleteKey(HKEY_CURRENT_USER, kRegistryBeaconPath); + return (result == ERROR_SUCCESS); +} + +bool AddDllToBlacklist(const wchar_t* dll_name) { + if (g_troublesome_dlls_cur_index >= kTroublesomeDllsMaxCount) + return false; + for (int i = 0; i < g_troublesome_dlls_cur_index; ++i) { + if (!wcscmp(g_troublesome_dlls[i], dll_name)) + return true; + } + + // Copy string to blacklist. + wchar_t* str_buffer = new wchar_t[wcslen(dll_name) + 1]; + wcscpy(str_buffer, dll_name); + + g_troublesome_dlls[g_troublesome_dlls_cur_index] = str_buffer; + g_troublesome_dlls_cur_index++; + return true; +} + +bool RemoveDllFromBlacklist(const wchar_t* dll_name) { + for (int i = 0; i < g_troublesome_dlls_cur_index; ++i) { + if (!wcscmp(g_troublesome_dlls[i], dll_name)) { + // Found the thing to remove. Delete it then replace it with the last + // element. + g_troublesome_dlls_cur_index--; + delete[] g_troublesome_dlls[i]; + g_troublesome_dlls[i] = g_troublesome_dlls[g_troublesome_dlls_cur_index]; + g_troublesome_dlls[g_troublesome_dlls_cur_index] = NULL; + return true; + } + } + return false; +} + +bool Initialize(bool force) { +#if defined(_WIN64) + // TODO(robertshield): Implement 64-bit support by providing 64-bit + // interceptors. + return false; +#endif + + // Check to see that we found the functions we need in ntdll. + if (!InitializeInterceptImports()) + return false; + + // Check to see if this is a non-browser process, abort if so. + if (IsNonBrowserProcess()) + return false; + + // Check to see if a beacon is present, abort if so. + if (!force && !CreateBeacon()) + return false; + + // Don't try blacklisting on unsupported OS versions. + OSInfo os_info; + if (os_info.version() <= VERSION_PRE_XP_SP2) + return false; + + // Pseudo-handle, no need to close. + HANDLE current_process = ::GetCurrentProcess(); + + // Tells the resolver to patch already patched functions. + const bool kRelaxed = true; + + // Create a thunk via the appropriate ServiceResolver instance. + sandbox::ServiceResolverThunk* thunk; +#if defined(_WIN64) + // TODO(robertshield): Use the appropriate thunk for 64-bit support + // when said support is implemented. +#else + if (GetWOW64StatusForCurrentProcess() == WOW64_ENABLED) { + if (os_info.version() >= VERSION_WIN8) + thunk = new sandbox::Wow64W8ResolverThunk(current_process, kRelaxed); + else + thunk = new sandbox::Wow64ResolverThunk(current_process, kRelaxed); + } else if (os_info.version() >= VERSION_WIN8) { + thunk = new sandbox::Win8ResolverThunk(current_process, kRelaxed); + } else { + thunk = new sandbox::ServiceResolverThunk(current_process, kRelaxed); + } +#endif + +#if defined(_WIN64) + BYTE* thunk_storage = new BYTE[sizeof(sandbox::ThunkData)]; +#else + BYTE* thunk_storage = reinterpret_cast<BYTE*>(&g_thunk_storage); +#endif + + thunk->AllowLocalPatches(); + + // Get ntdll base, target name, interceptor address, + NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName), + reinterpret_cast<void*>(&__ImageBase), + "NtMapViewOfSection", + NULL, + &blacklist::BlNtMapViewOfSection, + thunk_storage, + sizeof(sandbox::ThunkData), + NULL); + + delete thunk; + return NT_SUCCESS(ret); +} + +} // namespace blacklist diff --git a/chrome_elf/blacklist/blacklist.h b/chrome_elf/blacklist/blacklist.h new file mode 100644 index 0000000..5787ddd --- /dev/null +++ b/chrome_elf/blacklist/blacklist.h @@ -0,0 +1,52 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_ELF_BLACKLIST_BLACKLIST_H_ +#define CHROME_ELF_BLACKLIST_BLACKLIST_H_ + +namespace blacklist { + +// Max size of the DLL blacklist. +const int kTroublesomeDllsMaxCount = 64; + +// The DLL blacklist. +extern const wchar_t* g_troublesome_dlls[kTroublesomeDllsMaxCount]; + +// Cursor to the current last element in the blacklist. +extern int g_troublesome_dlls_cur_index; + +// The registry path of the blacklist beacon. Exposed here for testing. +extern const wchar_t kRegistryBeaconPath[]; + +// Attempts to create a beacon in the current user's registry hive. +// If the beacon already exists or any other error occurs when creating the +// beacon, returns false. Otherwise returns true. +// The intent of the beacon is to act as an extra failure mode protection +// whereby if Chrome for some reason fails to start during blacklist setup, +// it will skip blacklisting on the subsequent run. +bool CreateBeacon(); + +// Looks for the beacon that CreateBeacon() creates and attempts to delete it. +// Returns true if the beacon was found and deleted. +bool ClearBeacon(); + +// Adds the given dll name to the blacklist. Returns true if the dll name is in +// the blacklist when this returns, false on error. Note that this will copy +// |dll_name| and will leak it on exit if the string is not subsequently removed +// using RemoveDllFromBlacklist. +extern "C" bool AddDllToBlacklist(const wchar_t* dll_name); + +// Removes the given dll name from the blacklist. Returns true if it was +// removed, false on error. +extern "C" bool RemoveDllFromBlacklist(const wchar_t* dll_name); + +// Initializes the DLL blacklist in the current process. This should be called +// before any undesirable DLLs might be loaded. If |force| is set to true, then +// initialization will take place even if a beacon is present. This is useful +// for tests. +bool Initialize(bool force); + +} // namespace blacklist + +#endif // CHROME_ELF_BLACKLIST_BLACKLIST_H_ diff --git a/chrome_elf/blacklist/blacklist_interceptions.cc b/chrome_elf/blacklist/blacklist_interceptions.cc new file mode 100644 index 0000000..4de0bd2 --- /dev/null +++ b/chrome_elf/blacklist/blacklist_interceptions.cc @@ -0,0 +1,227 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Implementation of NtMapViewOfSection intercept for 32 bit builds. +// +// TODO(robertshield): Implement the 64 bit intercept. + +#include "chrome_elf/blacklist/blacklist_interceptions.h" + +#include <string> +#include <vector> + +// Note that only #includes from base that are either header-only or built into +// base_static (see base/base.gyp) are allowed here. +#include "base/basictypes.h" +#include "base/strings/string16.h" +#include "base/win/pe_image.h" +#include "chrome_elf/blacklist/blacklist.h" +#include "sandbox/win/src/internal_types.h" +#include "sandbox/win/src/nt_internals.h" +#include "sandbox/win/src/sandbox_nt_util.h" +#include "sandbox/win/src/sandbox_types.h" + +namespace { + +NtQuerySectionFunction g_nt_query_section_func = NULL; +NtQueryVirtualMemoryFunction g_nt_query_virtual_memory_func = NULL; +NtUnmapViewOfSectionFunction g_nt_unmap_view_of_section_func = NULL; + +// TODO(robertshield): Merge with ntdll exports cache. +FARPROC GetNtDllExportByName(const char* export_name) { + HMODULE ntdll = ::GetModuleHandle(sandbox::kNtdllName); + return ::GetProcAddress(ntdll, export_name); +} + +bool DllMatch(const string16& module_name) { + for (int i = 0; i < blacklist::g_troublesome_dlls_cur_index; ++i) { + if (module_name == blacklist::g_troublesome_dlls[i]) + return true; + } + return false; +} + +// TODO(robertshield): Some of the helper functions below overlap somewhat with +// code in sandbox_nt_util.cc. See if they can be unified. + +// Native reimplementation of PSAPIs GetMappedFileName. +string16 GetBackingModuleFilePath(PVOID address) { + DCHECK_NT(g_nt_query_virtual_memory_func); + + // We'll start with something close to max_path characters for the name. + ULONG buffer_bytes = MAX_PATH * 2; + std::vector<BYTE> buffer_data(buffer_bytes); + + for (;;) { + MEMORY_SECTION_NAME* section_name = + reinterpret_cast<MEMORY_SECTION_NAME*>(&buffer_data[0]); + + if (!section_name) + break; + + ULONG returned_bytes; + NTSTATUS ret = g_nt_query_virtual_memory_func( + NtCurrentProcess, address, MemorySectionName, section_name, + buffer_bytes, &returned_bytes); + + if (STATUS_BUFFER_OVERFLOW == ret) { + // Retry the call with the given buffer size. + buffer_bytes = returned_bytes + 1; + buffer_data.resize(buffer_bytes); + section_name = NULL; + continue; + } + if (!NT_SUCCESS(ret)) + break; + + UNICODE_STRING* section_string = + reinterpret_cast<UNICODE_STRING*>(section_name); + return string16(section_string->Buffer, + section_string->Length / sizeof(wchar_t)); + } + + return string16(); +} + +bool IsModuleValidImageSection(HANDLE section, + PVOID *base, + PLARGE_INTEGER offset, + PSIZE_T view_size) { + DCHECK_NT(g_nt_query_section_func); + + if (!section || !base || !view_size || offset) + return false; + + SECTION_BASIC_INFORMATION basic_info; + SIZE_T bytes_returned; + NTSTATUS ret = g_nt_query_section_func(section, SectionBasicInformation, + &basic_info, sizeof(basic_info), + &bytes_returned); + + if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned) + return false; + + if (!(basic_info.Attributes & SEC_IMAGE)) + return false; + + return true; +} + +string16 ExtractLoadedModuleName(const string16& module_path) { + if (module_path.empty() || module_path[module_path.size() - 1] == L'\\') + return string16(); + + size_t sep = module_path.find_last_of(L'\\'); + if (sep == string16::npos) + return module_path; + else + return module_path.substr(sep+1); +} + +// Fills |out_name| with the image name from the given |pe| image and |flags| +// with additional info about the image. +void SafeGetImageInfo(const base::win::PEImage& pe, + std::string* out_name, + uint32* flags) { + out_name->clear(); + out_name->reserve(MAX_PATH); + *flags = 0; + __try { + if (pe.VerifyMagic()) { + *flags |= sandbox::MODULE_IS_PE_IMAGE; + + PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory(); + if (exports) { + const char* image_name = reinterpret_cast<const char*>( + pe.RVAToAddr(exports->Name)); + size_t i = 0; + for (; i < MAX_PATH && *image_name; ++i, ++image_name) + out_name->push_back(*image_name); + } + + PIMAGE_NT_HEADERS headers = pe.GetNTHeaders(); + if (headers) { + if (headers->OptionalHeader.AddressOfEntryPoint) + *flags |= sandbox::MODULE_HAS_ENTRY_POINT; + if (headers->OptionalHeader.SizeOfCode) + *flags |= sandbox::MODULE_HAS_CODE; + } + } + } __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + out_name->clear(); + } +} + +string16 GetImageInfoFromLoadedModule(HMODULE module, uint32* flags) { + std::string out_name; + base::win::PEImage pe(module); + SafeGetImageInfo(pe, &out_name, flags); + return string16(out_name.begin(), out_name.end()); +} + +} // namespace + +namespace blacklist { + +bool InitializeInterceptImports() { + g_nt_query_section_func = reinterpret_cast<NtQuerySectionFunction>( + GetNtDllExportByName("NtQuerySection")); + g_nt_query_virtual_memory_func = + reinterpret_cast<NtQueryVirtualMemoryFunction>( + GetNtDllExportByName("NtQueryVirtualMemory")); + g_nt_unmap_view_of_section_func = + reinterpret_cast<NtUnmapViewOfSectionFunction>( + GetNtDllExportByName("NtUnmapViewOfSection")); + + return g_nt_query_section_func && g_nt_query_virtual_memory_func && + g_nt_unmap_view_of_section_func; +} + +SANDBOX_INTERCEPT NTSTATUS WINAPI BlNtMapViewOfSection( + NtMapViewOfSectionFunction orig_MapViewOfSection, + HANDLE section, + HANDLE process, + PVOID *base, + ULONG_PTR zero_bits, + SIZE_T commit_size, + PLARGE_INTEGER offset, + PSIZE_T view_size, + SECTION_INHERIT inherit, + ULONG allocation_type, + ULONG protect) { + NTSTATUS ret = orig_MapViewOfSection(section, process, base, zero_bits, + commit_size, offset, view_size, inherit, + allocation_type, protect); + + if (!NT_SUCCESS(ret) || !sandbox::IsSameProcess(process) || + !IsModuleValidImageSection(section, base, offset, view_size)) { + return ret; + } + + HMODULE module = reinterpret_cast<HMODULE>(*base); + if (module) { + UINT image_flags; + + string16 module_name(GetImageInfoFromLoadedModule( + reinterpret_cast<HMODULE>(*base), &image_flags)); + string16 file_name(GetBackingModuleFilePath(*base)); + + if (module_name.empty() && (image_flags & sandbox::MODULE_HAS_CODE)) { + // If the module has no exports we retrieve the module name from the + // full path of the mapped section. + module_name = ExtractLoadedModuleName(file_name); + } + + if (!module_name.empty() && DllMatch(module_name)) { + DCHECK_NT(g_nt_unmap_view_of_section_func); + g_nt_unmap_view_of_section_func(process, *base); + ret = STATUS_UNSUCCESSFUL; + } + + } + return ret; +} + +} // namespace blacklist diff --git a/chrome_elf/blacklist/blacklist_interceptions.h b/chrome_elf/blacklist/blacklist_interceptions.h new file mode 100644 index 0000000..dfb4495 --- /dev/null +++ b/chrome_elf/blacklist/blacklist_interceptions.h @@ -0,0 +1,33 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_ELF_BLACKLIST_BLACKLIST_INTERCEPTIONS_H_ +#define CHROME_ELF_BLACKLIST_BLACKLIST_INTERCEPTIONS_H_ + +#include "sandbox/win/src/nt_internals.h" +#include "sandbox/win/src/sandbox_types.h" + +namespace blacklist { + +bool InitializeInterceptImports(); + +// Interception of NtMapViewOfSection within the current process. +// It should never be called directly. This function provides the means to +// detect dlls being loaded, so we can patch them if needed. +SANDBOX_INTERCEPT NTSTATUS WINAPI BlNtMapViewOfSection( + NtMapViewOfSectionFunction orig_MapViewOfSection, + HANDLE section, + HANDLE process, + PVOID *base, + ULONG_PTR zero_bits, + SIZE_T commit_size, + PLARGE_INTEGER offset, + PSIZE_T view_size, + SECTION_INHERIT inherit, + ULONG allocation_type, + ULONG protect); + +} // namespace blacklist + +#endif // CHROME_ELF_BLACKLIST_BLACKLIST_INTERCEPTIONS_H_ diff --git a/chrome_elf/blacklist/test/blacklist_test.cc b/chrome_elf/blacklist/test/blacklist_test.cc new file mode 100644 index 0000000..6556e25 --- /dev/null +++ b/chrome_elf/blacklist/test/blacklist_test.cc @@ -0,0 +1,127 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/environment.h" +#include "base/files/file_path.h" +#include "base/files/scoped_temp_dir.h" +#include "base/path_service.h" +#include "base/scoped_native_library.h" +#include "base/strings/string16.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/test_reg_util_win.h" +#include "chrome_elf/blacklist/blacklist.h" +#include "chrome_elf/blacklist/test/blacklist_test_main_dll.h" +#include "testing/gtest/include/gtest/gtest.h" + +const wchar_t kTestDllName1[] = L"blacklist_test_dll_1.dll"; +const wchar_t kTestDllName2[] = L"blacklist_test_dll_2.dll"; +const wchar_t kTestDllName3[] = L"blacklist_test_dll_3.dll"; + +const wchar_t kDll2Beacon[] = L"{F70A0100-2889-4629-9B44-610FE5C73231}"; +const wchar_t kDll3Beacon[] = L"{9E056AEC-169E-400c-B2D0-5A07E3ACE2EB}"; + +extern const wchar_t* kEnvVars[]; + +extern "C" { +// When modifying the blacklist in the test process, use the exported test dll +// functions on the test blacklist dll, not the ones linked into the test +// executable itself. +__declspec(dllimport) void TestDll_AddDllToBlacklist(const wchar_t* dll_name); +__declspec(dllimport) void TestDll_RemoveDllFromBlacklist( + const wchar_t* dll_name); +} + +class BlacklistTest : public testing::Test { + virtual void SetUp() { + // Force an import from blacklist_test_main_dll. + InitBlacklistTestDll(); + + // Ensure that the beacon state starts off cleared. + blacklist::ClearBeacon(); + } + + virtual void TearDown() { + TestDll_RemoveDllFromBlacklist(kTestDllName1); + TestDll_RemoveDllFromBlacklist(kTestDllName2); + } +}; + +TEST_F(BlacklistTest, Beacon) { + registry_util::RegistryOverrideManager override_manager; + override_manager.OverrideRegistry(HKEY_CURRENT_USER, L"beacon_test"); + + // First call should succeed as the beacon is newly created. + EXPECT_TRUE(blacklist::CreateBeacon()); + + // Second call should fail indicating the beacon already existed. + EXPECT_FALSE(blacklist::CreateBeacon()); + + // First call should find the beacon and delete it. + EXPECT_TRUE(blacklist::ClearBeacon()); + + // Second call should fail to find the beacon and delete it. + EXPECT_FALSE(blacklist::ClearBeacon()); +} + +TEST_F(BlacklistTest, AddAndRemoveModules) { + EXPECT_TRUE(blacklist::AddDllToBlacklist(L"foo.dll")); + // Adding the same item twice should be idempotent. + EXPECT_TRUE(blacklist::AddDllToBlacklist(L"foo.dll")); + EXPECT_TRUE(blacklist::RemoveDllFromBlacklist(L"foo.dll")); + EXPECT_FALSE(blacklist::RemoveDllFromBlacklist(L"foo.dll")); + + std::vector<string16> added_dlls; + added_dlls.reserve(blacklist::kTroublesomeDllsMaxCount); + for (int i = 0; i < blacklist::kTroublesomeDllsMaxCount; ++i) { + added_dlls.push_back(base::IntToString16(i) + L".dll"); + EXPECT_TRUE(blacklist::AddDllToBlacklist(added_dlls[i].c_str())) << i; + } + EXPECT_FALSE(blacklist::AddDllToBlacklist(L"overflow.dll")); + for (int i = 0; i < blacklist::kTroublesomeDllsMaxCount; ++i) { + EXPECT_TRUE(blacklist::RemoveDllFromBlacklist(added_dlls[i].c_str())) << i; + } + EXPECT_FALSE(blacklist::RemoveDllFromBlacklist(L"0.dll")); + EXPECT_FALSE(blacklist::RemoveDllFromBlacklist(L"63.dll")); +} + +TEST_F(BlacklistTest, LoadBlacklistedLibrary) { +// TODO(robertshield): Add 64-bit support. +#if !defined(_WIN64) + base::FilePath current_dir; + ASSERT_TRUE(PathService::Get(base::DIR_EXE, ¤t_dir)); + + // Test that an un-blacklisted DLL can load correctly. + base::ScopedNativeLibrary dll1(current_dir.Append(kTestDllName1)); + EXPECT_TRUE(dll1.is_valid()); + dll1.Reset(NULL); + + struct TestData { + const wchar_t* dll_name; + const wchar_t* dll_beacon; + } test_data[] = { + { kTestDllName2, kDll2Beacon }, + { kTestDllName3, kDll3Beacon } + }; + for (int i = 0 ; i < arraysize(test_data); ++i) { + // Add the DLL to the blacklist, ensure that it is not loaded both by + // inspecting the handle returned by LoadLibrary and by looking for an + // environment variable that is set when the DLL's entry point is called. + TestDll_AddDllToBlacklist(test_data[i].dll_name); + base::ScopedNativeLibrary dll_blacklisted( + current_dir.Append(test_data[i].dll_name)); + EXPECT_FALSE(dll_blacklisted.is_valid()); + EXPECT_EQ(0u, ::GetEnvironmentVariable(test_data[i].dll_beacon, NULL, 0)); + dll_blacklisted.Reset(NULL); + + // Remove the DLL from the blacklist. Ensure that it loads and that its + // entry point was called. + TestDll_RemoveDllFromBlacklist(test_data[i].dll_name); + base::ScopedNativeLibrary dll(current_dir.Append(test_data[i].dll_name)); + EXPECT_TRUE(dll.is_valid()); + EXPECT_NE(0u, ::GetEnvironmentVariable(test_data[i].dll_beacon, NULL, 0)); + dll.Reset(NULL); + } +#endif +} diff --git a/chrome_elf/blacklist/test/blacklist_test_dll_1.cc b/chrome_elf/blacklist/test/blacklist_test_dll_1.cc new file mode 100644 index 0000000..a4e414d --- /dev/null +++ b/chrome_elf/blacklist/test/blacklist_test_dll_1.cc @@ -0,0 +1,9 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <windows.h> + +BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) { + return TRUE; +} diff --git a/chrome_elf/blacklist/test/blacklist_test_dll_1.def b/chrome_elf/blacklist/test/blacklist_test_dll_1.def new file mode 100644 index 0000000..fc7b7be --- /dev/null +++ b/chrome_elf/blacklist/test/blacklist_test_dll_1.def @@ -0,0 +1,5 @@ +; Copyright 2013 The Chromium Authors. All rights reserved. +; Use of this source code is governed by a BSD-style license that can be +; found in the LICENSE file. + +LIBRARY "blacklist_test_dll_1.dll" diff --git a/chrome_elf/blacklist/test/blacklist_test_dll_2.cc b/chrome_elf/blacklist/test/blacklist_test_dll_2.cc new file mode 100644 index 0000000..7a4a7dc --- /dev/null +++ b/chrome_elf/blacklist/test/blacklist_test_dll_2.cc @@ -0,0 +1,19 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <windows.h> + +const wchar_t kDll2Beacon[] = L"{F70A0100-2889-4629-9B44-610FE5C73231}"; + +extern "C" { +// Have a dummy export so that the module gets an export table entry. +void DummyExport() {} +} + +BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) { + if (reason == DLL_PROCESS_ATTACH) { + ::SetEnvironmentVariable(kDll2Beacon, L"1"); + } + return TRUE; +} diff --git a/chrome_elf/blacklist/test/blacklist_test_dll_2.def b/chrome_elf/blacklist/test/blacklist_test_dll_2.def new file mode 100644 index 0000000..2cc122f --- /dev/null +++ b/chrome_elf/blacklist/test/blacklist_test_dll_2.def @@ -0,0 +1,8 @@ +; Copyright 2013 The Chromium Authors. All rights reserved. +; Use of this source code is governed by a BSD-style license that can be +; found in the LICENSE file. + +LIBRARY "blacklist_test_dll_2.dll" + +EXPORTS + DummyExport diff --git a/chrome_elf/blacklist/test/blacklist_test_dll_3.cc b/chrome_elf/blacklist/test/blacklist_test_dll_3.cc new file mode 100644 index 0000000..b4a6959 --- /dev/null +++ b/chrome_elf/blacklist/test/blacklist_test_dll_3.cc @@ -0,0 +1,14 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <windows.h> + +const wchar_t kDll3Beacon[] = L"{9E056AEC-169E-400c-B2D0-5A07E3ACE2EB}"; + +BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) { + if (reason == DLL_PROCESS_ATTACH) { + ::SetEnvironmentVariable(kDll3Beacon, L"1"); + } + return TRUE; +} diff --git a/chrome_elf/blacklist/test/blacklist_test_dll_3.lib b/chrome_elf/blacklist/test/blacklist_test_dll_3.lib new file mode 100644 index 0000000..7ab9a11 --- /dev/null +++ b/chrome_elf/blacklist/test/blacklist_test_dll_3.lib @@ -0,0 +1 @@ +LIBRARY "blacklist_test_dll_2.dll" diff --git a/chrome_elf/blacklist/test/blacklist_test_main.cc b/chrome_elf/blacklist/test/blacklist_test_main.cc new file mode 100644 index 0000000..c84b5ad --- /dev/null +++ b/chrome_elf/blacklist/test/blacklist_test_main.cc @@ -0,0 +1,17 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/at_exit.h" +#include "chrome_elf/blacklist/test/blacklist_test_main_dll.h" +#include "testing/gtest/include/gtest/gtest.h" + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + + base::AtExitManager at_exit_manager; + + InitBlacklistTestDll(); + + RUN_ALL_TESTS(); +} diff --git a/chrome_elf/blacklist/test/blacklist_test_main_dll.cc b/chrome_elf/blacklist/test/blacklist_test_main_dll.cc new file mode 100644 index 0000000..54e5eb8 --- /dev/null +++ b/chrome_elf/blacklist/test/blacklist_test_main_dll.cc @@ -0,0 +1,17 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <windows.h> + +#include "chrome_elf/blacklist/blacklist.h" + +extern "C" void InitBlacklistTestDll() {} + +BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) { + if (reason == DLL_PROCESS_ATTACH) { + blacklist::Initialize(true); // force always on, no beacon + } + + return TRUE; +} diff --git a/chrome_elf/blacklist/test/blacklist_test_main_dll.def b/chrome_elf/blacklist/test/blacklist_test_main_dll.def new file mode 100644 index 0000000..63522a0 --- /dev/null +++ b/chrome_elf/blacklist/test/blacklist_test_main_dll.def @@ -0,0 +1,10 @@ +; Copyright 2013 The Chromium Authors. All rights reserved. +; Use of this source code is governed by a BSD-style license that can be +; found in the LICENSE file. + +LIBRARY "blacklist_test_main_dll.dll" + +EXPORTS + TestDll_AddDllToBlacklist=AddDllToBlacklist + TestDll_RemoveDllFromBlacklist=RemoveDllFromBlacklist + InitBlacklistTestDll diff --git a/chrome_elf/blacklist/test/blacklist_test_main_dll.h b/chrome_elf/blacklist/test/blacklist_test_main_dll.h new file mode 100644 index 0000000..a004f9b --- /dev/null +++ b/chrome_elf/blacklist/test/blacklist_test_main_dll.h @@ -0,0 +1,10 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_ELF_BLACKLIST_TEST_BLACKLIST_TEST_MAIN_DLL_H_ +#define CHROME_ELF_BLACKLIST_TEST_BLACKLIST_TEST_MAIN_DLL_H_ + +extern "C" void InitBlacklistTestDll(); + +#endif // CHROME_ELF_BLACKLIST_TEST_BLACKLIST_TEST_MAIN_DLL_H_ diff --git a/chrome_elf/chrome_elf.def b/chrome_elf/chrome_elf.def index d3ca82f..d7d4c9c 100644 --- a/chrome_elf/chrome_elf.def +++ b/chrome_elf/chrome_elf.def @@ -5,4 +5,4 @@ LIBRARY "chrome_elf.dll" EXPORTS - InitChromeElf + SignalChromeElf diff --git a/chrome_elf/chrome_elf.gyp b/chrome_elf/chrome_elf.gyp index cf0a0e9..7227fe6 100644 --- a/chrome_elf/chrome_elf.gyp +++ b/chrome_elf/chrome_elf.gyp @@ -8,6 +8,7 @@ 'includes': [ '../build/win_precompile.gypi', '../chrome/version.gypi', + 'blacklist.gypi', ], 'targets': [ { @@ -22,6 +23,7 @@ 'chrome_elf_main.h', ], 'dependencies': [ + 'blacklist', 'chrome_elf_lib', ], 'msvs_settings': { @@ -29,6 +31,12 @@ 'BaseAddress': '0x01c20000', # Set /SUBSYSTEM:WINDOWS for chrome_elf.dll (for consistency). 'SubSystem': '2', + 'AdditionalDependencies!': [ + 'user32.lib', + ], + 'IgnoreDefaultLibraryNames': [ + 'user32.lib', + ], }, }, }, @@ -36,6 +44,7 @@ 'target_name': 'chrome_elf_unittests', 'type': 'executable', 'sources': [ + 'blacklist/test/blacklist_test.cc', 'ntdll_cache_unittest.cc', ], 'include_dirs': [ @@ -43,8 +52,16 @@ ], 'dependencies': [ 'chrome_elf_lib', - '<(DEPTH)/base/base.gyp:run_all_unittests', - '<(DEPTH)/testing/gtest.gyp:gtest', + '../base/base.gyp:base', + '../base/base.gyp:run_all_unittests', + '../base/base.gyp:test_support_base', + '../sandbox/sandbox.gyp:sandbox', + '../testing/gtest.gyp:gtest', + 'blacklist', + 'blacklist_test_dll_1', + 'blacklist_test_dll_2', + 'blacklist_test_dll_3', + 'blacklist_test_main_dll', ], }, { diff --git a/chrome_elf/chrome_elf_main.cc b/chrome_elf/chrome_elf_main.cc index 4291430..9ad8299 100644 --- a/chrome_elf/chrome_elf_main.cc +++ b/chrome_elf/chrome_elf_main.cc @@ -6,15 +6,21 @@ #include "chrome_elf/chrome_elf_main.h" +#include "chrome_elf/blacklist/blacklist.h" #include "chrome_elf/ntdll_cache.h" -void InitChromeElf() { - // This method is a no-op which may be called to force a load-time dependency - // on chrome_elf.dll. +void SignalChromeElf() { + blacklist::ClearBeacon(); } BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) { - if (reason == DLL_PROCESS_ATTACH) + if (reason == DLL_PROCESS_ATTACH) { InitCache(); + blacklist::Initialize(false); // Don't force, abort if beacon is present. + + // TODO(csharp): Move additions to the DLL blacklist to a sane place. + // blacklist::AddDllToBlacklist(L"foo.dll"); + } + return TRUE; } diff --git a/chrome_elf/chrome_elf_main.h b/chrome_elf/chrome_elf_main.h index 7d02ddd..52bf067 100644 --- a/chrome_elf/chrome_elf_main.h +++ b/chrome_elf/chrome_elf_main.h @@ -5,6 +5,6 @@ #ifndef CHROME_ELF_CHROME_ELF_MAIN_H_ #define CHROME_ELF_CHROME_ELF_MAIN_H_ -extern "C" void InitChromeElf(); +extern "C" void SignalChromeElf(); #endif // CHROME_ELF_CHROME_ELF_MAIN_H_ diff --git a/chrome_elf/version_assembly_manifest_action.gypi b/chrome_elf/version_assembly_manifest_action.gypi index 9c44315..37c2015 100644 --- a/chrome_elf/version_assembly_manifest_action.gypi +++ b/chrome_elf/version_assembly_manifest_action.gypi @@ -34,4 +34,4 @@ '<@(_outputs)', ], 'message': 'Generating <@(_outputs)', -}
\ No newline at end of file +} |