diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/chrome_elf | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) | |
download | qtwebengine-chromium-ab0a50979b9eb4dfa3320eff7e187e41efedf7a9.tar.gz |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/chrome_elf')
39 files changed, 3021 insertions, 156 deletions
diff --git a/chromium/chrome_elf/DEPS b/chromium/chrome_elf/DEPS index 48e88750d4a..495665e10bf 100644 --- a/chromium/chrome_elf/DEPS +++ b/chromium/chrome_elf/DEPS @@ -1,2 +1,4 @@ include_rules = [ + "+sandbox", + "+breakpad/src/client", ] diff --git a/chromium/chrome_elf/OWNERS b/chromium/chrome_elf/OWNERS index d2e52d6c15f..3d69bab21a6 100644 --- a/chromium/chrome_elf/OWNERS +++ b/chromium/chrome_elf/OWNERS @@ -1,3 +1,2 @@ caitkp@chromium.org - gab@chromium.org - robertshield@chromium.org
\ No newline at end of file + robertshield@chromium.org diff --git a/chromium/chrome_elf/README b/chromium/chrome_elf/README deleted file mode 100644 index b6574373756..00000000000 --- a/chromium/chrome_elf/README +++ /dev/null @@ -1,15 +0,0 @@ -Chrome Early Loading Framework (aka ChromeELF) - -chrome_elf.dll is shipped in Chrome's version directory to ease updates, -and is loaded early in chrome.exe's lifetime. This is done by turning the -version directory into a private assembly which refers to chrome_elf.dll -(http://msdn.microsoft.com/library/aa374224.aspx). - -In an ideal world, this would be done by embedding an application config in -chrome.exe that would refer to the proper version directory via a -probing\privatePath attribute (http://msdn.microsoft.com/library/aa374182.aspx). -This would allow us to refer to dlls in the version directory without having to -make the version directory itself into an assembly. It would also avoid naming -conflicts (as the WinSxS dir and GAC both take precedence over private -assemblies when searching for dlls). Unfortunately, the probing\privatePath -attribute is only supported for Windows 7 and later. diff --git a/chromium/chrome_elf/blacklist.gypi b/chromium/chrome_elf/blacklist.gypi new file mode 100644 index 00000000000..7ab5be1aaac --- /dev/null +++ b/chromium/chrome_elf/blacklist.gypi @@ -0,0 +1,66 @@ +# 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', + 'include_dirs': [ + '..', + '<(SHARED_INTERMEDIATE_DIR)', + ], + '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', + '../chrome_elf/chrome_elf.gyp:chrome_elf_breakpad', + '../chrome_elf/chrome_elf.gyp:chrome_elf_constants', + '../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/chromium/chrome_elf/blacklist/OWNERS b/chromium/chrome_elf/blacklist/OWNERS new file mode 100644 index 00000000000..28a6a305588 --- /dev/null +++ b/chromium/chrome_elf/blacklist/OWNERS @@ -0,0 +1,3 @@ +caitkp@chromium.org +csharp@chromium.org +robertshield@chromium.org diff --git a/chromium/chrome_elf/blacklist/blacklist.cc b/chromium/chrome_elf/blacklist/blacklist.cc new file mode 100644 index 00000000000..efb9c97cd20 --- /dev/null +++ b/chromium/chrome_elf/blacklist/blacklist.cc @@ -0,0 +1,427 @@ +// 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 <assert.h> +#include <string.h> + +#include <vector> + +#include "base/basictypes.h" +#include "chrome_elf/blacklist/blacklist_interceptions.h" +#include "chrome_elf/chrome_elf_constants.h" +#include "chrome_elf/chrome_elf_util.h" +#include "chrome_elf/thunk_getter.h" +#include "sandbox/win/src/interception_internal.h" +#include "sandbox/win/src/internal_types.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{ + +// The DLLs listed here are known (or under strong suspicion) of causing crashes +// when they are loaded in the browser. DLLs should only be added to this list +// if there is nothing else Chrome can do to prevent those crashes. +// For more information about how this list is generated, and how to get off +// of it, see: +// https://sites.google.com/a/chromium.org/dev/Home/third-party-developers +const wchar_t* g_troublesome_dlls[kTroublesomeDllsMaxCount] = { + L"activedetect32.dll", // Lenovo One Key Theater. + // See crbug.com/379218. + L"activedetect64.dll", // Lenovo One Key Theater. + L"bitguard.dll", // Unknown (suspected malware). + L"chrmxtn.dll", // Unknown (keystroke logger). + L"datamngr.dll", // Unknown (suspected adware). + L"hk.dll", // Unknown (keystroke logger). + L"libsvn_tsvn32.dll", // TortoiseSVN. + L"lmrn.dll", // Unknown. + L"scdetour.dll", // Quick Heal Antivirus. + // See crbug.com/382561. + L"systemk.dll", // Unknown (suspected adware). + L"windowsapihookdll32.dll", // Lenovo One Key Theater. + // See crbug.com/379218. + L"windowsapihookdll64.dll", // Lenovo One Key Theater. + // Keep this null pointer here to mark the end of the list. + NULL, +}; + +bool g_blocked_dlls[kTroublesomeDllsMaxCount] = {}; +int g_num_blocked_dlls = 0; + +} // namespace blacklist + +// Allocate storage for thunks in a page of this module to save on doing +// an extra allocation at run time. +#pragma section(".crthunk",read,execute) +__declspec(allocate(".crthunk")) sandbox::ThunkData g_thunk_storage; + +namespace { + +// Record if the blacklist was successfully initialized so processes can easily +// determine if the blacklist is enabled for them. +bool g_blacklist_initialized = false; + +// Helper to set DWORD registry values. +DWORD SetDWValue(HKEY* key, const wchar_t* property, DWORD value) { + return ::RegSetValueEx(*key, + property, + 0, + REG_DWORD, + reinterpret_cast<LPBYTE>(&value), + sizeof(value)); +} + +bool GenerateStateFromBeaconAndAttemptCount(HKEY* key, DWORD blacklist_state) { + LONG result = 0; + if (blacklist_state == blacklist::BLACKLIST_SETUP_RUNNING) { + // Some part of the blacklist setup failed last time. If this has occured + // blacklist::kBeaconMaxAttempts times in a row we switch the state to + // failed and skip setting up the blacklist. + DWORD attempt_count = 0; + DWORD attempt_count_size = sizeof(attempt_count); + result = ::RegQueryValueEx(*key, + blacklist::kBeaconAttemptCount, + 0, + NULL, + reinterpret_cast<LPBYTE>(&attempt_count), + &attempt_count_size); + + if (result == ERROR_FILE_NOT_FOUND) + attempt_count = 0; + else if (result != ERROR_SUCCESS) + return false; + + ++attempt_count; + SetDWValue(key, blacklist::kBeaconAttemptCount, attempt_count); + + if (attempt_count >= blacklist::kBeaconMaxAttempts) { + blacklist_state = blacklist::BLACKLIST_SETUP_FAILED; + SetDWValue(key, blacklist::kBeaconState, blacklist_state); + return false; + } + } else if (blacklist_state == blacklist::BLACKLIST_ENABLED) { + // If the blacklist succeeded on the previous run reset the failure + // counter. + result = + SetDWValue(key, blacklist::kBeaconAttemptCount, static_cast<DWORD>(0)); + if (result != ERROR_SUCCESS) { + return false; + } + } + return true; +} + +} // namespace + +namespace blacklist { + +#if defined(_WIN64) + // Allocate storage for the pointer to the old NtMapViewOfSectionFunction. +#pragma section(".oldntmap",write,read) + __declspec(allocate(".oldntmap")) + NtMapViewOfSectionFunction g_nt_map_view_of_section_func = NULL; +#endif + +bool LeaveSetupBeacon() { + HKEY key = NULL; + DWORD disposition = 0; + LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, + kRegistryBeaconPath, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_QUERY_VALUE | KEY_SET_VALUE, + NULL, + &key, + &disposition); + if (result != ERROR_SUCCESS) + return false; + + // Retrieve the current blacklist state. + DWORD blacklist_state = BLACKLIST_STATE_MAX; + DWORD blacklist_state_size = sizeof(blacklist_state); + DWORD type = 0; + result = ::RegQueryValueEx(key, + kBeaconState, + 0, + &type, + reinterpret_cast<LPBYTE>(&blacklist_state), + &blacklist_state_size); + + if (blacklist_state == BLACKLIST_DISABLED || result != ERROR_SUCCESS || + type != REG_DWORD) { + ::RegCloseKey(key); + return false; + } + + if (!GenerateStateFromBeaconAndAttemptCount(&key, blacklist_state)) { + ::RegCloseKey(key); + return false; + } + + result = SetDWValue(&key, kBeaconState, BLACKLIST_SETUP_RUNNING); + ::RegCloseKey(key); + + return (result == ERROR_SUCCESS); +} + +bool ResetBeacon() { + HKEY key = NULL; + DWORD disposition = 0; + LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER, + kRegistryBeaconPath, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_QUERY_VALUE | KEY_SET_VALUE, + NULL, + &key, + &disposition); + if (result != ERROR_SUCCESS) + return false; + + DWORD blacklist_state = BLACKLIST_STATE_MAX; + DWORD blacklist_state_size = sizeof(blacklist_state); + DWORD type = 0; + result = ::RegQueryValueEx(key, + kBeaconState, + 0, + &type, + reinterpret_cast<LPBYTE>(&blacklist_state), + &blacklist_state_size); + + if (result != ERROR_SUCCESS || type != REG_DWORD) { + ::RegCloseKey(key); + return false; + } + + // Reaching this point with the setup running state means the setup did not + // crash, so we reset to enabled. Any other state indicates that setup was + // skipped; in that case we leave the state alone for later recording. + if (blacklist_state == BLACKLIST_SETUP_RUNNING) + result = SetDWValue(&key, kBeaconState, BLACKLIST_ENABLED); + + ::RegCloseKey(key); + return (result == ERROR_SUCCESS); +} + +int BlacklistSize() { + int size = -1; + while (blacklist::g_troublesome_dlls[++size] != NULL) {} + + return size; +} + +bool IsBlacklistInitialized() { + return g_blacklist_initialized; +} + +bool AddDllToBlacklist(const wchar_t* dll_name) { + int blacklist_size = BlacklistSize(); + // We need to leave one space at the end for the null pointer. + if (blacklist_size + 1 >= kTroublesomeDllsMaxCount) + return false; + for (int i = 0; i < blacklist_size; ++i) { + if (!_wcsicmp(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[blacklist_size] = str_buffer; + g_blocked_dlls[blacklist_size] = false; + return true; +} + +bool RemoveDllFromBlacklist(const wchar_t* dll_name) { + int blacklist_size = BlacklistSize(); + for (int i = 0; i < blacklist_size; ++i) { + if (!_wcsicmp(g_troublesome_dlls[i], dll_name)) { + // Found the thing to remove. Delete it then replace it with the last + // element. + delete[] g_troublesome_dlls[i]; + g_troublesome_dlls[i] = g_troublesome_dlls[blacklist_size - 1]; + g_troublesome_dlls[blacklist_size - 1] = NULL; + + // Also update the stats recording if we have blocked this dll or not. + if (g_blocked_dlls[i]) + --g_num_blocked_dlls; + g_blocked_dlls[i] = g_blocked_dlls[blacklist_size - 1]; + return true; + } + } + return false; +} + +// TODO(csharp): Maybe store these values in the registry so we can +// still report them if Chrome crashes early. +void SuccessfullyBlocked(const wchar_t** blocked_dlls, int* size) { + if (size == NULL) + return; + + // If the array isn't valid or big enough, just report the size it needs to + // be and return. + if (blocked_dlls == NULL && *size < g_num_blocked_dlls) { + *size = g_num_blocked_dlls; + return; + } + + *size = g_num_blocked_dlls; + + int strings_to_fill = 0; + for (int i = 0; strings_to_fill < g_num_blocked_dlls && g_troublesome_dlls[i]; + ++i) { + if (g_blocked_dlls[i]) { + blocked_dlls[strings_to_fill] = g_troublesome_dlls[i]; + ++strings_to_fill; + } + } +} + +void BlockedDll(size_t blocked_index) { + assert(blocked_index < kTroublesomeDllsMaxCount); + + if (!g_blocked_dlls[blocked_index] && + blocked_index < kTroublesomeDllsMaxCount) { + ++g_num_blocked_dlls; + g_blocked_dlls[blocked_index] = true; + } +} + +bool Initialize(bool force) { + // 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 the blacklist beacon is still set to running (indicating a + // failure) or disabled, and abort if so. + if (!force && !LeaveSetupBeacon()) + return false; + + // It is possible for other dlls to have already patched code by now and + // attempting to patch their code might result in crashes. + const bool kRelaxed = false; + + // Create a thunk via the appropriate ServiceResolver instance. + sandbox::ServiceResolverThunk* thunk = GetThunk(kRelaxed); + + // Don't try blacklisting on unsupported OS versions. + if (!thunk) + return false; + + BYTE* thunk_storage = reinterpret_cast<BYTE*>(&g_thunk_storage); + + // Mark the thunk storage as readable and writeable, since we + // ready to write to it. + DWORD old_protect = 0; + if (!VirtualProtect(&g_thunk_storage, + sizeof(g_thunk_storage), + PAGE_EXECUTE_READWRITE, + &old_protect)) { + return false; + } + + thunk->AllowLocalPatches(); + + // We declare this early so it can be used in the 64-bit block below and + // still work on 32-bit build when referenced at the end of the function. + BOOL page_executable = false; + + // Replace the default NtMapViewOfSection with our patched version. +#if defined(_WIN64) + NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName), + reinterpret_cast<void*>(&__ImageBase), + "NtMapViewOfSection", + NULL, + &blacklist::BlNtMapViewOfSection64, + thunk_storage, + sizeof(sandbox::ThunkData), + NULL); + + // Keep a pointer to the original code, we don't have enough space to + // add it directly to the call. + g_nt_map_view_of_section_func = reinterpret_cast<NtMapViewOfSectionFunction>( + thunk_storage); + + // Ensure that the pointer to the old function can't be changed. + page_executable = VirtualProtect(&g_nt_map_view_of_section_func, + sizeof(g_nt_map_view_of_section_func), + PAGE_EXECUTE_READ, + &old_protect); +#else + NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName), + reinterpret_cast<void*>(&__ImageBase), + "NtMapViewOfSection", + NULL, + &blacklist::BlNtMapViewOfSection, + thunk_storage, + sizeof(sandbox::ThunkData), + NULL); +#endif + delete thunk; + + // Record if we have initialized the blacklist. + g_blacklist_initialized = NT_SUCCESS(ret); + + // Mark the thunk storage as executable and prevent any future writes to it. + page_executable = page_executable && VirtualProtect(&g_thunk_storage, + sizeof(g_thunk_storage), + PAGE_EXECUTE_READ, + &old_protect); + + AddDllsFromRegistryToBlacklist(); + + return NT_SUCCESS(ret) && page_executable; +} + +bool AddDllsFromRegistryToBlacklist() { + HKEY key = NULL; + LONG result = ::RegOpenKeyEx(HKEY_CURRENT_USER, + kRegistryFinchListPath, + 0, + KEY_QUERY_VALUE | KEY_SET_VALUE, + &key); + + if (result != ERROR_SUCCESS) + return false; + + // We add dlls from the registry to the blacklist, and then clear registry. + DWORD value_len; + DWORD name_len = MAX_PATH; + std::vector<wchar_t> name_buffer(name_len); + for (int i = 0; result == ERROR_SUCCESS; ++i) { + name_len = MAX_PATH; + value_len = 0; + result = ::RegEnumValue( + key, i, &name_buffer[0], &name_len, NULL, NULL, NULL, &value_len); + name_len = name_len + 1; + value_len = value_len + 1; + std::vector<wchar_t> value_buffer(value_len); + result = ::RegEnumValue(key, i, &name_buffer[0], &name_len, NULL, NULL, + reinterpret_cast<BYTE*>(&value_buffer[0]), + &value_len); + value_buffer[value_len - 1] = L'\0'; + + if (result == ERROR_SUCCESS) { + AddDllToBlacklist(&value_buffer[0]); + } + } + + // Delete the finch registry key to clear the values. + result = ::RegDeleteKey(key, L""); + + ::RegCloseKey(key); + return result == ERROR_SUCCESS; +} + +} // namespace blacklist diff --git a/chromium/chrome_elf/blacklist/blacklist.h b/chromium/chrome_elf/blacklist/blacklist.h new file mode 100644 index 00000000000..58cc16b3b8f --- /dev/null +++ b/chromium/chrome_elf/blacklist/blacklist.h @@ -0,0 +1,78 @@ +// 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_ + +#if defined(_WIN64) +#include "sandbox/win/src/sandbox_nt_types.h" +#endif + +namespace blacklist { + +// Max size of the DLL blacklist. +const size_t kTroublesomeDllsMaxCount = 64; + +// The DLL blacklist. +extern const wchar_t* g_troublesome_dlls[kTroublesomeDllsMaxCount]; + +#if defined(_WIN64) +extern NtMapViewOfSectionFunction g_nt_map_view_of_section_func; +#endif + +// Attempts to leave a beacon in the current user's registry hive. If the +// blacklist beacon doesn't say it is enabled or there are any other errors 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 +// repeatedly fails to start during blacklist setup, it will skip blacklisting +// on the subsequent run. +bool LeaveSetupBeacon(); + +// Looks for the setup running beacon that LeaveSetupBeacon() creates and resets +// it to to show the setup was successful. +// Returns true if the beacon was successfully set to BLACKLIST_ENABLED. +bool ResetBeacon(); + +// Return the size of the current blacklist. +int BlacklistSize(); + +// Returns if true if the blacklist has been initialized. +extern "C" bool IsBlacklistInitialized(); + +// 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. +// Exposed for testing only, this shouldn't be exported from chrome_elf.dll. +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. +// Exposed for testing only, this shouldn't be exported from chrome_elf.dll. +extern "C" bool RemoveDllFromBlacklist(const wchar_t* dll_name); + +// Returns a list of all the dlls that have been successfully blocked by the +// blacklist via blocked_dlls, if there is enough space (according to |size|). +// |size| will always be modified to be the number of dlls that were blocked. +// The caller doesn't own the strings and isn't expected to free them. These +// strings won't be hanging unless RemoveDllFromBlacklist is called, but it +// is only exposed in tests (and should stay that way). +extern "C" void SuccessfullyBlocked(const wchar_t** blocked_dlls, int* size); + +// Add the dlls, originally passed in through finch, from the registry to the +// blacklist so that they will be blocked identically to those hard coded in. +extern "C" bool AddDllsFromRegistryToBlacklist(); + +// Record that the dll at the given index was blocked. +void BlockedDll(size_t blocked_index); + +// 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/chromium/chrome_elf/blacklist/blacklist_interceptions.cc b/chromium/chrome_elf/blacklist/blacklist_interceptions.cc new file mode 100644 index 00000000000..28eb692accc --- /dev/null +++ b/chromium/chrome_elf/blacklist/blacklist_interceptions.cc @@ -0,0 +1,276 @@ +// 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 "chrome_elf/breakpad.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); +} + +int DllMatch(const base::string16& module_name) { + for (int i = 0; blacklist::g_troublesome_dlls[i] != NULL; ++i) { + if (_wcsicmp(module_name.c_str(), blacklist::g_troublesome_dlls[i]) == 0) + return i; + } + return -1; +} + +// 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. +base::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 base::string16(section_string->Buffer, + section_string->Length / sizeof(wchar_t)); + } + + return base::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; +} + +base::string16 ExtractLoadedModuleName(const base::string16& module_path) { + if (module_path.empty() || module_path[module_path.size() - 1] == L'\\') + return base::string16(); + + size_t sep = module_path.find_last_of(L'\\'); + if (sep == base::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 || + GetExceptionCode() == EXCEPTION_GUARD_PAGE || + GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + out_name->clear(); + } +} + +base::string16 GetImageInfoFromLoadedModule(HMODULE module, uint32* flags) { + std::string out_name; + base::win::PEImage pe(module); + SafeGetImageInfo(pe, &out_name, flags); + return base::string16(out_name.begin(), out_name.end()); +} + +bool IsSameAsCurrentProcess(HANDLE process) { + return (NtCurrentProcess == process) || + (::GetProcessId(process) == ::GetCurrentProcessId()); +} + +NTSTATUS BlNtMapViewOfSectionImpl( + 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) || !IsSameAsCurrentProcess(process) || + !IsModuleValidImageSection(section, base, offset, view_size)) { + return ret; + } + + HMODULE module = reinterpret_cast<HMODULE>(*base); + if (module) { + UINT image_flags; + + base::string16 module_name(GetImageInfoFromLoadedModule( + reinterpret_cast<HMODULE>(*base), &image_flags)); + base::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()) { + int blocked_index = DllMatch(module_name); + if (blocked_index != -1) { + DCHECK_NT(g_nt_unmap_view_of_section_func); + g_nt_unmap_view_of_section_func(process, *base); + ret = STATUS_UNSUCCESSFUL; + + blacklist::BlockedDll(blocked_index); + } + } + } + + return ret; +} + +} // 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 = STATUS_UNSUCCESSFUL; + + __try { + ret = BlNtMapViewOfSectionImpl(orig_MapViewOfSection, section, process, + base, zero_bits, commit_size, offset, + view_size, inherit, allocation_type, + protect); + } __except(GenerateCrashDump(GetExceptionInformation())) { + } + + return ret; +} + +#if defined(_WIN64) +NTSTATUS WINAPI BlNtMapViewOfSection64( + 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) { + return BlNtMapViewOfSection(g_nt_map_view_of_section_func, section, process, + base, zero_bits, commit_size, offset, view_size, + inherit, allocation_type, protect); +} +#endif +} // namespace blacklist diff --git a/chromium/chrome_elf/blacklist/blacklist_interceptions.h b/chromium/chrome_elf/blacklist/blacklist_interceptions.h new file mode 100644 index 00000000000..2ee96b475f8 --- /dev/null +++ b/chromium/chrome_elf/blacklist/blacklist_interceptions.h @@ -0,0 +1,43 @@ +// 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); + +#if defined(_WIN64) +// 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 BlNtMapViewOfSection64( + 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); +#endif + +} // namespace blacklist + +#endif // CHROME_ELF_BLACKLIST_BLACKLIST_INTERCEPTIONS_H_ diff --git a/chromium/chrome_elf/breakpad.cc b/chromium/chrome_elf/breakpad.cc new file mode 100644 index 00000000000..4c20324b208 --- /dev/null +++ b/chromium/chrome_elf/breakpad.cc @@ -0,0 +1,178 @@ +// Copyright 2014 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. + +// This module contains the necessary code to register the Breakpad exception +// handler. This implementation is based on Chrome's crash reporting code. + +#include "chrome_elf/breakpad.h" + +#include <sddl.h> + +#include "base/macros.h" +#include "breakpad/src/client/windows/handler/exception_handler.h" +#include "chrome_elf/chrome_elf_util.h" +#include "version.h" // NOLINT + +google_breakpad::ExceptionHandler* g_elf_breakpad = NULL; + +namespace { + +const wchar_t kBreakpadProductName[] = L"Chrome"; +const wchar_t kBreakpadVersionEntry[] = L"ver"; +const wchar_t kBreakpadProdEntry[] = L"prod"; +const wchar_t kBreakpadPlatformEntry[] = L"plat"; +const wchar_t kBreakpadPlatformWin32[] = L"Win32"; + +// The protocol for connecting to the out-of-process Breakpad crash +// reporter is different for x86-32 and x86-64: the message sizes +// are different because the message struct contains a pointer. As +// a result, there are two different named pipes to connect to. The +// 64-bit one is distinguished with an "-x64" suffix. +const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices\\"; +const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\"; +const wchar_t kSystemPrincipalSid[] = L"S-1-5-18"; + +const wchar_t kNoErrorDialogs[] = L"noerrdialogs"; +const wchar_t kChromeHeadless[] = L"CHROME_HEADLESS"; + +google_breakpad::CustomClientInfo* GetCustomInfo() { + static google_breakpad::CustomInfoEntry ver_entry( + kBreakpadVersionEntry, TEXT(CHROME_VERSION_STRING)); + static google_breakpad::CustomInfoEntry prod_entry( + kBreakpadProdEntry, kBreakpadProductName); + static google_breakpad::CustomInfoEntry plat_entry( + kBreakpadPlatformEntry, kBreakpadPlatformWin32); + static google_breakpad::CustomInfoEntry entries[] = { + ver_entry, prod_entry, plat_entry }; + static google_breakpad::CustomClientInfo custom_info = { + entries, arraysize(entries) }; + return &custom_info; +} + +base::string16 GetUserSidString() { + // Get the current token. + HANDLE token = NULL; + base::string16 user_sid; + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)) + return user_sid; + + DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE; + BYTE user_bytes[sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE] = {}; + TOKEN_USER* user = reinterpret_cast<TOKEN_USER*>(user_bytes); + + wchar_t* sid_string = NULL; + if (::GetTokenInformation(token, TokenUser, user, size, &size) && + user->User.Sid && + ::ConvertSidToStringSid(user->User.Sid, &sid_string)) { + user_sid = sid_string; + ::LocalFree(sid_string); + } + + CloseHandle(token); + return user_sid; +} + +bool IsHeadless() { + DWORD ret = ::GetEnvironmentVariable(L"CHROME_HEADLESS", NULL, 0); + if (ret != 0) + return true; + + wchar_t* command_line = ::GetCommandLine(); + + // Note: Since this is a pure substring search rather than a check for a + // switch, there is a small chance that this code will match things that the + // Chrome code (which executes a similar check) does not. However, as long as + // no other switches contain the string "noerrdialogs", it should not be an + // issue. + return (command_line && wcsstr(command_line, kNoErrorDialogs)); +} + +} // namespace + +int GenerateCrashDump(EXCEPTION_POINTERS* exinfo) { + DWORD code = exinfo->ExceptionRecord->ExceptionCode; + if (code == EXCEPTION_BREAKPOINT || code == EXCEPTION_SINGLE_STEP) + return EXCEPTION_CONTINUE_SEARCH; + + if (g_elf_breakpad != NULL) + g_elf_breakpad->WriteMinidumpForException(exinfo); + return EXCEPTION_CONTINUE_SEARCH; +} + +void InitializeCrashReporting() { + wchar_t exe_path[MAX_PATH] = {}; + if (!::GetModuleFileName(NULL, exe_path, arraysize(exe_path))) + return; + + // Disable the message box for assertions. + _CrtSetReportMode(_CRT_ASSERT, 0); + + // Get the alternate dump directory. We use the temp path. + // N.B. We don't use base::GetTempDir() here to avoid running more code then + // necessary before crashes can be properly reported. + wchar_t temp_directory[MAX_PATH + 1] = {}; + DWORD length = GetTempPath(MAX_PATH, temp_directory); + if (length == 0) + return; + + // Minidump with stacks, PEB, TEBs and unloaded module list. + MINIDUMP_TYPE dump_type = static_cast<MINIDUMP_TYPE>( + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithUnloadedModules | // Get unloaded modules when available. + MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by + // stack. + +#if defined(GOOGLE_CHROME_BUILD) && defined(OFFICIAL_BUILD) + bool is_official_chrome_build = true; +#else + bool is_official_chrome_build = false; +#endif + + base::string16 pipe_name; + + bool enabled_by_policy = false; + bool use_policy = ReportingIsEnforcedByPolicy(&enabled_by_policy); + + if (!use_policy && IsHeadless()) { + pipe_name = kChromePipeName; + } else if (use_policy ? + enabled_by_policy : + (is_official_chrome_build && AreUsageStatsEnabled(exe_path))) { + // Build the pipe name. It can be one of: + // 32-bit system: \\.\pipe\GoogleCrashServices\S-1-5-18 + // 32-bit user: \\.\pipe\GoogleCrashServices\<user SID> + // 64-bit system: \\.\pipe\GoogleCrashServices\S-1-5-18-x64 + // 64-bit user: \\.\pipe\GoogleCrashServices\<user SID>-x64 + base::string16 user_sid = IsSystemInstall(exe_path) ? kSystemPrincipalSid : + GetUserSidString(); + if (user_sid.empty()) + return; + + pipe_name = kGoogleUpdatePipeName; + pipe_name += user_sid; + +#if defined(_WIN64) + pipe_name += L"-x64"; +#endif + } else { + // Either this is a Chromium build, reporting is disabled by policy or the + // user has not given consent. + return; + } + + g_elf_breakpad = new google_breakpad::ExceptionHandler( + temp_directory, + NULL, + NULL, + NULL, + google_breakpad::ExceptionHandler::HANDLER_ALL, + dump_type, + pipe_name.c_str(), + GetCustomInfo()); + + if (g_elf_breakpad->IsOutOfProcess()) { + // Tells breakpad to handle breakpoint and single step exceptions. + g_elf_breakpad->set_handle_debug_exceptions(true); + } +} diff --git a/chromium/chrome_elf/breakpad.h b/chromium/chrome_elf/breakpad.h new file mode 100644 index 00000000000..8067708b647 --- /dev/null +++ b/chromium/chrome_elf/breakpad.h @@ -0,0 +1,34 @@ +// Copyright 2014 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_BREAKPAD_H_ +#define CHROME_ELF_BREAKPAD_H_ + +#include <windows.h> + +namespace google_breakpad { +class ExceptionHandler; +} + +// Initializes collection and upload of crash reports. This will only be done if +// the user has agreed to crash dump reporting. +// +// Crash reporting has to be initialized as early as possible (e.g., the first +// thing in main()) to catch crashes occuring during process startup. +// Crashes which occur during the global static construction phase will not +// be caught and reported. This should not be a problem as static non-POD +// objects are not allowed by the style guide and exceptions to this rule are +// rare. +void InitializeCrashReporting(); + +// Generates a crashdump for the provided |exinfo|. This crashdump will be +// either be saved locally, or uploaded, depending on how the ExceptionHandler +// has been configured. +int GenerateCrashDump(EXCEPTION_POINTERS* exinfo); + +// Global pointer to the ExceptionHandler. This is initialized by +// InitializeCrashReporting() and used by GenerateCrashDump() to record dumps. +extern google_breakpad::ExceptionHandler* g_elf_breakpad; + +#endif // CHROME_ELF_BREAKPAD_H_ diff --git a/chromium/chrome_elf/chrome_elf.def b/chromium/chrome_elf/chrome_elf.def index d3ca82f80d2..566a8fb0657 100644 --- a/chromium/chrome_elf/chrome_elf.def +++ b/chromium/chrome_elf/chrome_elf.def @@ -5,4 +5,8 @@ LIBRARY "chrome_elf.dll" EXPORTS - InitChromeElf + CreateFileW=CreateFileWRedirect + GetRedirectCount + IsBlacklistInitialized + SignalChromeElf + SuccessfullyBlocked diff --git a/chromium/chrome_elf/chrome_elf.gyp b/chromium/chrome_elf/chrome_elf.gyp index cf0a0e95d68..0b612a87c6e 100644 --- a/chromium/chrome_elf/chrome_elf.gyp +++ b/chromium/chrome_elf/chrome_elf.gyp @@ -6,11 +6,38 @@ 'chromium_code': 1, }, 'includes': [ + '../build/util/version.gypi', '../build/win_precompile.gypi', - '../chrome/version.gypi', + 'blacklist.gypi', + 'dll_hash.gypi', ], 'targets': [ { + 'target_name': 'chrome_elf_resources', + 'type': 'none', + 'conditions': [ + ['branding == "Chrome"', { + 'variables': { + 'branding_path': '../chrome/app/theme/google_chrome/BRANDING', + }, + }, { # else branding!="Chrome" + 'variables': { + 'branding_path': '../chrome/app/theme/chromium/BRANDING', + }, + }], + ], + 'variables': { + 'output_dir': 'chrome_elf', + 'template_input_path': '../chrome/app/chrome_version.rc.version', + }, + 'sources': [ + 'chrome_elf.ver', + ], + 'includes': [ + '../chrome/version_resource_rules.gypi', + ], + }, + { 'target_name': 'chrome_elf', 'type': 'shared_library', 'include_dirs': [ @@ -20,31 +47,72 @@ 'chrome_elf.def', 'chrome_elf_main.cc', 'chrome_elf_main.h', + '<(SHARED_INTERMEDIATE_DIR)/chrome_elf/chrome_elf_version.rc', ], 'dependencies': [ + 'blacklist', + 'chrome_elf_breakpad', 'chrome_elf_lib', + 'chrome_elf_resources', ], 'msvs_settings': { 'VCLinkerTool': { - 'BaseAddress': '0x01c20000', - # Set /SUBSYSTEM:WINDOWS for chrome_elf.dll (for consistency). + 'conditions': [ + ['target_arch=="ia32"', { + # Don't set an x64 base address (to avoid breaking HE-ASLR). + 'BaseAddress': '0x01c20000', + }], + ], + # Set /SUBSYSTEM:WINDOWS. 'SubSystem': '2', + 'AdditionalDependencies!': [ + 'user32.lib', + ], + 'IgnoreDefaultLibraryNames': [ + 'user32.lib', + ], }, }, }, { - 'target_name': 'chrome_elf_unittests', + 'target_name': 'chrome_elf_unittests_exe', + 'product_name': 'chrome_elf_unittests', 'type': 'executable', 'sources': [ + 'blacklist/test/blacklist_test.cc', + 'chrome_elf_util_unittest.cc', + 'create_file/chrome_create_file_unittest.cc', + 'elf_imports_unittest.cc', 'ntdll_cache_unittest.cc', ], 'include_dirs': [ '..', + '<(SHARED_INTERMEDIATE_DIR)', ], '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', + ], + }, + { + # A dummy target to ensure that chrome_elf.dll and chrome.exe gets built + # when building chrome_elf_unittests.exe without introducing an + # explicit runtime dependency. + 'target_name': 'chrome_elf_unittests', + 'type': 'none', + 'dependencies': [ + '../chrome/chrome.gyp:chrome', + 'chrome_elf', + 'chrome_elf_unittests_exe', ], }, { @@ -54,10 +122,94 @@ '..', ], 'sources': [ - 'chrome_elf_types.h', + 'create_file/chrome_create_file.cc', + 'create_file/chrome_create_file.h', 'ntdll_cache.cc', 'ntdll_cache.h', ], + 'dependencies': [ + 'chrome_elf_common', + '../base/base.gyp:base_static', + '../sandbox/sandbox.gyp:sandbox', + ], + }, + { + 'target_name': 'chrome_elf_constants', + 'type': 'static_library', + 'include_dirs': [ + '..', + ], + 'sources': [ + 'chrome_elf_constants.cc', + 'chrome_elf_constants.h', + ], }, + { + 'target_name': 'chrome_elf_common', + 'type': 'static_library', + 'dependencies': [ + 'chrome_elf_constants', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'chrome_elf_types.h', + 'chrome_elf_util.cc', + 'chrome_elf_util.h', + 'thunk_getter.cc', + 'thunk_getter.h', + ], + }, + { + 'target_name': 'chrome_elf_breakpad', + 'type': 'static_library', + 'include_dirs': [ + '..', + '<(SHARED_INTERMEDIATE_DIR)', + ], + 'sources': [ + 'breakpad.cc', + 'breakpad.h', + ], + 'dependencies': [ + 'chrome_elf_common', + '../breakpad/breakpad.gyp:breakpad_handler', + '../chrome/chrome.gyp:chrome_version_header', + ], + }, + ], # targets + 'conditions': [ + ['component=="shared_library"', { + 'targets': [ + { + 'target_name': 'chrome_redirects', + 'type': 'shared_library', + 'include_dirs': [ + '..', + ], + 'sources': [ + 'chrome_redirects.def', + 'chrome_redirects_main.cc', + ], + 'dependencies': [ + 'chrome_elf_lib', + ], + 'msvs_settings': { + 'VCLinkerTool': { + 'conditions': [ + ['target_arch=="ia32"', { + # Don't set an x64 base address (to avoid breaking HE-ASLR). + 'BaseAddress': '0x01c20000', + }], + ], + # Set /SUBSYSTEM:WINDOWS. + 'SubSystem': '2', + }, + }, + }, + ], + }], ], } + diff --git a/chromium/chrome_elf/chrome_elf.ver b/chromium/chrome_elf/chrome_elf.ver new file mode 100644 index 00000000000..78f890e7ece --- /dev/null +++ b/chromium/chrome_elf/chrome_elf.ver @@ -0,0 +1,2 @@ +INTERNAL_NAME=chrome_elf_dll +ORIGINAL_FILENAME=chrome_elf.dll diff --git a/chromium/chrome_elf/chrome_elf_constants.cc b/chromium/chrome_elf/chrome_elf_constants.cc new file mode 100644 index 00000000000..a7ee1c929c2 --- /dev/null +++ b/chromium/chrome_elf/chrome_elf_constants.cc @@ -0,0 +1,28 @@ +// Copyright 2014 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/chrome_elf_constants.h" + +#if defined(GOOGLE_CHROME_BUILD) +const wchar_t kAppDataDirName[] = L"Google\\Chrome"; +#else +const wchar_t kAppDataDirName[] = L"Chromium"; +#endif +const wchar_t kCanaryAppDataDirName[] = L"Google\\Chrome SxS"; +const wchar_t kLocalStateFilename[] = L"Local State"; +const wchar_t kPreferencesFilename[] = L"Preferences"; +const wchar_t kUserDataDirName[] = L"User Data"; + +namespace blacklist { + +const wchar_t kRegistryBeaconPath[] = L"SOFTWARE\\Google\\Chrome\\BLBeacon"; +const wchar_t kRegistryFinchListPath[] = + L"SOFTWARE\\Google\\Chrome\\BLFinchList"; +const wchar_t kBeaconVersion[] = L"version"; +const wchar_t kBeaconState[] = L"state"; +const wchar_t kBeaconAttemptCount[] = L"failed_count"; + +const DWORD kBeaconMaxAttempts = 2; + +} // namespace blacklist diff --git a/chromium/chrome_elf/chrome_elf_constants.h b/chromium/chrome_elf/chrome_elf_constants.h new file mode 100644 index 00000000000..92e44cad7c4 --- /dev/null +++ b/chromium/chrome_elf/chrome_elf_constants.h @@ -0,0 +1,51 @@ +// Copyright 2014 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. + +// A handful of resource-like constants related to the ChromeELF. + +#ifndef CHROME_ELF_CHROME_ELF_CONSTANTS_H_ +#define CHROME_ELF_CHROME_ELF_CONSTANTS_H_ + +#include <windows.h> + +// directory names +extern const wchar_t kAppDataDirName[]; +extern const wchar_t kCanaryAppDataDirName[]; +extern const wchar_t kLocalStateFilename[]; +extern const wchar_t kPreferencesFilename[]; +extern const wchar_t kUserDataDirName[]; + +namespace blacklist { + +// The registry path of the blacklist beacon. +extern const wchar_t kRegistryBeaconPath[]; + +// The registry path of the finch blacklist dlls. +extern const wchar_t kRegistryFinchListPath[]; + +// The properties for the blacklist beacon. +extern const wchar_t kBeaconVersion[]; +extern const wchar_t kBeaconState[]; +extern const wchar_t kBeaconAttemptCount[]; + +// The number of failures that can occur on startup with the beacon enabled +// before we give up and turn off the blacklist. +extern const DWORD kBeaconMaxAttempts; + +// The states for the blacklist setup code. +enum BlacklistState { + BLACKLIST_DISABLED = 0, + BLACKLIST_ENABLED, + // The blacklist setup code is running. If this is the state at startup, it + // means the last setup crashed. + BLACKLIST_SETUP_RUNNING, + // If the last setup crashed, we reassign the state to failed. + BLACKLIST_SETUP_FAILED, + // Always keep this at the end. + BLACKLIST_STATE_MAX, +}; + +} // namespace blacklist + +#endif // CHROME_ELF_CHROME_ELF_CONSTANTS_H_ diff --git a/chromium/chrome_elf/chrome_elf_main.cc b/chromium/chrome_elf/chrome_elf_main.cc index 42914309704..989493a122e 100644 --- a/chromium/chrome_elf/chrome_elf_main.cc +++ b/chromium/chrome_elf/chrome_elf_main.cc @@ -6,15 +6,24 @@ #include "chrome_elf/chrome_elf_main.h" +#include "chrome_elf/blacklist/blacklist.h" +#include "chrome_elf/breakpad.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::ResetBeacon(); } BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) { - if (reason == DLL_PROCESS_ATTACH) - InitCache(); + if (reason == DLL_PROCESS_ATTACH) { + InitializeCrashReporting(); + + __try { + InitCache(); + blacklist::Initialize(false); // Don't force, abort if beacon is present. + } __except(GenerateCrashDump(GetExceptionInformation())) { + } + } + return TRUE; } diff --git a/chromium/chrome_elf/chrome_elf_main.h b/chromium/chrome_elf/chrome_elf_main.h index 7d02ddd9912..52bf067a579 100644 --- a/chromium/chrome_elf/chrome_elf_main.h +++ b/chromium/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/chromium/chrome_elf/chrome_elf_util.cc b/chromium/chrome_elf/chrome_elf_util.cc new file mode 100644 index 00000000000..a547d0bda04 --- /dev/null +++ b/chromium/chrome_elf/chrome_elf_util.cc @@ -0,0 +1,209 @@ +// Copyright 2014 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/chrome_elf_util.h" + +#include <windows.h> + +#include "base/macros.h" +#include "base/strings/string16.h" + +namespace { + +const wchar_t kRegPathClientState[] = L"Software\\Google\\Update\\ClientState"; +const wchar_t kRegPathClientStateMedium[] = + L"Software\\Google\\Update\\ClientStateMedium"; +#if defined(GOOGLE_CHROME_BUILD) +const wchar_t kRegPathChromePolicy[] = L"SOFTWARE\\Policies\\Google\\Chrome"; +#else +const wchar_t kRegPathChromePolicy[] = L"SOFTWARE\\Policies\\Chromium"; +#endif // defined(GOOGLE_CHROME_BUILD) + +const wchar_t kRegValueUsageStats[] = L"usagestats"; +const wchar_t kUninstallArgumentsField[] = L"UninstallArguments"; +const wchar_t kMetricsReportingEnabled[] =L"MetricsReportingEnabled"; + +const wchar_t kAppGuidCanary[] = + L"{4ea16ac7-fd5a-47c3-875b-dbf4a2008c20}"; +const wchar_t kAppGuidGoogleChrome[] = + L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; +const wchar_t kAppGuidGoogleBinaries[] = + L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}"; + +bool ReadKeyValueString(bool system_install, const wchar_t* key_path, + const wchar_t* guid, const wchar_t* value_to_read, + base::string16* value_out) { + HKEY key = NULL; + value_out->clear(); + + base::string16 full_key_path(key_path); + full_key_path.append(1, L'\\'); + full_key_path.append(guid); + + if (::RegOpenKeyEx(system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, + full_key_path.c_str(), 0, + KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key) != + ERROR_SUCCESS) { + return false; + } + + const size_t kMaxStringLength = 1024; + wchar_t raw_value[kMaxStringLength] = {}; + DWORD size = sizeof(raw_value); + DWORD type = REG_SZ; + LONG result = ::RegQueryValueEx(key, value_to_read, 0, &type, + reinterpret_cast<LPBYTE>(raw_value), &size); + + if (result == ERROR_SUCCESS) { + if (type != REG_SZ || (size & 1) != 0) { + result = ERROR_NOT_SUPPORTED; + } else if (size == 0) { + *raw_value = L'\0'; + } else if (raw_value[size / sizeof(wchar_t) - 1] != L'\0') { + if ((size / sizeof(wchar_t)) < kMaxStringLength) + raw_value[size / sizeof(wchar_t)] = L'\0'; + else + result = ERROR_MORE_DATA; + } + } + + if (result == ERROR_SUCCESS) + *value_out = raw_value; + + ::RegCloseKey(key); + + return result == ERROR_SUCCESS; +} + +bool ReadKeyValueDW(bool system_install, const wchar_t* key_path, + base::string16 guid, const wchar_t* value_to_read, + DWORD* value_out) { + HKEY key = NULL; + *value_out = 0; + + base::string16 full_key_path(key_path); + full_key_path.append(1, L'\\'); + full_key_path.append(guid); + + if (::RegOpenKeyEx(system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, + full_key_path.c_str(), 0, + KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key) != + ERROR_SUCCESS) { + return false; + } + + DWORD size = sizeof(*value_out); + DWORD type = REG_DWORD; + LONG result = ::RegQueryValueEx(key, value_to_read, 0, &type, + reinterpret_cast<BYTE*>(value_out), &size); + + ::RegCloseKey(key); + + return result == ERROR_SUCCESS && size == sizeof(*value_out); +} + +} // namespace + +bool IsCanary(const wchar_t* exe_path) { + return wcsstr(exe_path, L"Chrome SxS\\Application") != NULL; +} + +bool IsSystemInstall(const wchar_t* exe_path) { + wchar_t program_dir[MAX_PATH] = {}; + DWORD ret = ::GetEnvironmentVariable(L"PROGRAMFILES", program_dir, + arraysize(program_dir)); + if (ret && ret < MAX_PATH && !wcsncmp(exe_path, program_dir, ret)) + return true; + + ret = ::GetEnvironmentVariable(L"PROGRAMFILES(X86)", program_dir, + arraysize(program_dir)); + if (ret && ret < MAX_PATH && !wcsncmp(exe_path, program_dir, ret)) + return true; + + return false; +} + +bool IsMultiInstall(bool is_system_install) { + base::string16 args; + if (!ReadKeyValueString(is_system_install, kRegPathClientState, + kAppGuidGoogleChrome, kUninstallArgumentsField, + &args)) { + return false; + } + return args.find(L"--multi-install") != base::string16::npos; +} + +bool AreUsageStatsEnabled(const wchar_t* exe_path) { + bool enabled = true; + bool controlled_by_policy = ReportingIsEnforcedByPolicy(&enabled); + + if (controlled_by_policy && !enabled) + return false; + + bool system_install = IsSystemInstall(exe_path); + base::string16 app_guid; + + if (IsCanary(exe_path)) { + app_guid = kAppGuidCanary; + } else { + app_guid = IsMultiInstall(system_install) ? kAppGuidGoogleBinaries : + kAppGuidGoogleChrome; + } + + DWORD out_value = 0; + if (system_install && + ReadKeyValueDW(system_install, kRegPathClientStateMedium, app_guid, + kRegValueUsageStats, &out_value)) { + return out_value == 1; + } + + return ReadKeyValueDW(system_install, kRegPathClientState, app_guid, + kRegValueUsageStats, &out_value) && out_value == 1; +} + +bool ReportingIsEnforcedByPolicy(bool* breakpad_enabled) { + HKEY key = NULL; + DWORD value = 0; + BYTE* value_bytes = reinterpret_cast<BYTE*>(&value); + DWORD size = sizeof(value); + DWORD type = REG_DWORD; + + if (::RegOpenKeyEx(HKEY_LOCAL_MACHINE, kRegPathChromePolicy, 0, + KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) { + if (::RegQueryValueEx(key, kMetricsReportingEnabled, 0, &type, + value_bytes, &size) == ERROR_SUCCESS) { + *breakpad_enabled = value != 0; + } + ::RegCloseKey(key); + return size == sizeof(value); + } + + if (::RegOpenKeyEx(HKEY_CURRENT_USER, kRegPathChromePolicy, 0, + KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) { + if (::RegQueryValueEx(key, kMetricsReportingEnabled, 0, &type, + value_bytes, &size) == ERROR_SUCCESS) { + *breakpad_enabled = value != 0; + } + ::RegCloseKey(key); + return size == sizeof(value); + } + + return false; +} + +bool IsNonBrowserProcess() { + typedef bool (*IsSandboxedProcessFunc)(); + IsSandboxedProcessFunc is_sandboxed_process_func = + reinterpret_cast<IsSandboxedProcessFunc>( + GetProcAddress(GetModuleHandle(NULL), "IsSandboxedProcess")); + bool is_sandboxed_process = + is_sandboxed_process_func && is_sandboxed_process_func(); + + // TODO(robertshield): Drop the command line check when we drop support for + // enabling chrome_elf in unsandboxed processes. + wchar_t* command_line = GetCommandLine(); + bool has_process_type_flag = command_line && wcsstr(command_line, L"--type"); + + return (has_process_type_flag || is_sandboxed_process); +} diff --git a/chromium/chrome_elf/chrome_elf_util.h b/chromium/chrome_elf/chrome_elf_util.h new file mode 100644 index 00000000000..e87dc7fab70 --- /dev/null +++ b/chromium/chrome_elf/chrome_elf_util.h @@ -0,0 +1,31 @@ +// Copyright 2014 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_CHROME_ELF_UTIL_H_ +#define CHROME_ELF_CHROME_ELF_UTIL_H_ + +#include "base/strings/string16.h" + +// Returns true if |exe_path| points to a Chrome installed in an SxS +// installation. +bool IsCanary(const wchar_t* exe_path); + +// Returns true if |exe_path| points to a per-user level Chrome installation. +bool IsSystemInstall(const wchar_t* exe_path); + +// Returns true if current installation of Chrome is a multi-install. +bool IsMultiInstall(bool is_system_install); + +// Returns true if usage stats collecting is enabled for this user. +bool AreUsageStatsEnabled(const wchar_t* exe_path); + +// Returns true if a policy is in effect. |breakpad_enabled| will be set to true +// if stats collecting is permitted by this policy and false if not. +bool ReportingIsEnforcedByPolicy(bool* breakpad_enabled); + +// Returns true if invoked in a Chrome process other than the main browser +// process. False otherwise. +bool IsNonBrowserProcess(); + +#endif // CHROME_ELF_CHROME_ELF_UTIL_H_ diff --git a/chromium/chrome_elf/chrome_elf_util_unittest.cc b/chromium/chrome_elf/chrome_elf_util_unittest.cc new file mode 100644 index 00000000000..1cb6488ecaa --- /dev/null +++ b/chromium/chrome_elf/chrome_elf_util_unittest.cc @@ -0,0 +1,186 @@ +// Copyright 2014 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/chrome_elf_util.h" + +#include <tuple> + +#include "base/test/test_reg_util_win.h" +#include "base/win/registry.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace { + +const wchar_t kRegPathClientState[] = L"Software\\Google\\Update\\ClientState"; +const wchar_t kRegPathClientStateMedium[] = + L"Software\\Google\\Update\\ClientStateMedium"; +const wchar_t kRegValueUsageStats[] = L"usagestats"; +const wchar_t kUninstallArgumentsField[] = L"UninstallArguments"; + +const wchar_t kAppGuidCanary[] = + L"{4ea16ac7-fd5a-47c3-875b-dbf4a2008c20}"; +const wchar_t kAppGuidGoogleChrome[] = + L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; +const wchar_t kAppGuidGoogleBinaries[] = + L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}"; + +const wchar_t kCanaryExePath[] = + L"C:\\Users\\user\\AppData\\Local\\Google\\Chrome SxS\\Application" + L"\\chrome.exe"; +const wchar_t kChromeSystemExePath[] = + L"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"; +const wchar_t kChromeUserExePath[] = + L"C:\\Users\\user\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"; +const wchar_t kChromiumExePath[] = + L"C:\\Users\\user\\AppData\\Local\\Chromium\\Application\\chrome.exe"; + + +TEST(ChromeElfUtilTest, CanaryTest) { + EXPECT_TRUE(IsCanary(kCanaryExePath)); + EXPECT_FALSE(IsCanary(kChromeUserExePath)); + EXPECT_FALSE(IsCanary(kChromiumExePath)); +} + +TEST(ChromeElfUtilTest, SystemInstallTest) { + EXPECT_TRUE(IsSystemInstall(kChromeSystemExePath)); + EXPECT_FALSE(IsSystemInstall(kChromeUserExePath)); +} + +// Parameterized test with paramters: +// 1: product: "canary" or "google" +// 2: install level: "user" or "system" +// 3: install mode: "single" or "multi" +class ChromeElfUtilTest : + public testing::TestWithParam<std::tuple<const char*, + const char*, + const char*> > { + protected: + virtual void SetUp() OVERRIDE { + override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE, + L"chrome_elf_test_local"); + override_manager_.OverrideRegistry(HKEY_CURRENT_USER, + L"chrome_elf_test_current"); + const char* app; + const char* level; + const char* mode; + std::tie(app, level, mode) = GetParam(); + is_canary_ = (std::string(app) == "canary"); + system_level_ = (std::string(level) != "user"); + multi_install_ = (std::string(mode) != "single"); + if (is_canary_) { + ASSERT_FALSE(system_level_); + ASSERT_FALSE(multi_install_); + app_guid_ = kAppGuidCanary; + chrome_path_ = kCanaryExePath; + } else { + app_guid_ = kAppGuidGoogleChrome; + chrome_path_ = (system_level_ ? kChromeSystemExePath : + kChromeUserExePath); + } + if (multi_install_) { + SetMultiInstallStateInRegistry(system_level_, true); + app_guid_ = kAppGuidGoogleBinaries; + } + } + + base::string16 BuildKey(const wchar_t* path, const wchar_t* guid) { + base::string16 full_key_path(path); + full_key_path.append(1, L'\\'); + full_key_path.append(guid); + return full_key_path; + } + + void SetUsageStat(DWORD value, bool state_medium) { + LONG result = base::win::RegKey( + system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, + BuildKey(state_medium ? kRegPathClientStateMedium : kRegPathClientState, + app_guid_).c_str(), + KEY_SET_VALUE).WriteValue(kRegValueUsageStats, value); + ASSERT_EQ(ERROR_SUCCESS, result); + } + + void SetMultiInstallStateInRegistry(bool system_install, bool multi) { + base::win::RegKey key( + system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, + BuildKey(kRegPathClientState, kAppGuidGoogleChrome).c_str(), + KEY_SET_VALUE); + LONG result; + if (multi) { + result = key.WriteValue(kUninstallArgumentsField, + L"yadda yadda --multi-install yadda yadda"); + } else { + result = key.DeleteValue(kUninstallArgumentsField); + } + ASSERT_EQ(ERROR_SUCCESS, result); + } + + const wchar_t* app_guid_; + const wchar_t* chrome_path_; + bool system_level_; + bool multi_install_; + bool is_canary_; + registry_util::RegistryOverrideManager override_manager_; +}; + +TEST_P(ChromeElfUtilTest, MultiInstallTest) { + if (is_canary_) + return; + SetMultiInstallStateInRegistry(system_level_, true); + EXPECT_TRUE(IsMultiInstall(system_level_)); + + SetMultiInstallStateInRegistry(system_level_, false); + EXPECT_FALSE(IsMultiInstall(system_level_)); +} + +TEST_P(ChromeElfUtilTest, UsageStatsAbsent) { + EXPECT_FALSE(AreUsageStatsEnabled(chrome_path_)); +} + +TEST_P(ChromeElfUtilTest, UsageStatsZero) { + SetUsageStat(0, false); + EXPECT_FALSE(AreUsageStatsEnabled(chrome_path_)); +} + +TEST_P(ChromeElfUtilTest, UsageStatsOne) { + SetUsageStat(1, false); + EXPECT_TRUE(AreUsageStatsEnabled(chrome_path_)); + if (is_canary_) { + EXPECT_FALSE(AreUsageStatsEnabled(kChromeUserExePath)); + EXPECT_FALSE(AreUsageStatsEnabled(kChromeSystemExePath)); + } else if (system_level_) { + EXPECT_FALSE(AreUsageStatsEnabled(kCanaryExePath)); + EXPECT_FALSE(AreUsageStatsEnabled(kChromeUserExePath)); + } else { + EXPECT_FALSE(AreUsageStatsEnabled(kCanaryExePath)); + EXPECT_FALSE(AreUsageStatsEnabled(kChromeSystemExePath)); + } +} + +TEST_P(ChromeElfUtilTest, UsageStatsZeroInStateMedium) { + if (!system_level_) + return; + SetUsageStat(0, true); + EXPECT_FALSE(AreUsageStatsEnabled(chrome_path_)); +} + +TEST_P(ChromeElfUtilTest, UsageStatsOneInStateMedium) { + if (!system_level_) + return; + SetUsageStat(1, true); + EXPECT_TRUE(AreUsageStatsEnabled(chrome_path_)); + EXPECT_FALSE(AreUsageStatsEnabled(kCanaryExePath)); + EXPECT_FALSE(AreUsageStatsEnabled(kChromeUserExePath)); +} + +INSTANTIATE_TEST_CASE_P(Canary, ChromeElfUtilTest, + testing::Combine(testing::Values("canary"), + testing::Values("user"), + testing::Values("single"))); +INSTANTIATE_TEST_CASE_P(GoogleChrome, ChromeElfUtilTest, + testing::Combine(testing::Values("google"), + testing::Values("user", "system"), + testing::Values("single", "multi"))); + +} // namespace diff --git a/chromium/chrome_elf/chrome_exe_manifest.template b/chromium/chrome_elf/chrome_exe_manifest.template deleted file mode 100644 index 7c66328e206..00000000000 --- a/chromium/chrome_elf/chrome_exe_manifest.template +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> - <dependency> - <dependentAssembly> - <assemblyIdentity type='win32' - name='@MAJOR@.@MINOR@.@BUILD@.@PATCH@' - version='@MAJOR@.@MINOR@.@BUILD@.@PATCH@' language='*'/> - </dependentAssembly> - </dependency> -</assembly>
\ No newline at end of file diff --git a/chromium/chrome_elf/chrome_exe_manifest_action.gypi b/chromium/chrome_elf/chrome_exe_manifest_action.gypi deleted file mode 100644 index 89453dbc952..00000000000 --- a/chromium/chrome_elf/chrome_exe_manifest_action.gypi +++ /dev/null @@ -1,34 +0,0 @@ -# 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. - -# This file contains an action which can be used to construct a manifest file -# declaring a dependency on chrome_elf.dll. This manifest can then be merged -# into the manifest of the executable and embedded into it when it is built. - -# To use this the following variables need to be defined: -# version_path: string: path to file containing version data (e.g. -# chrome/VERSION). -# version_py_path: string: path to file containing version script (e.g. -# chrome/tools/build/version.py). - -{ - 'variables': { - 'template_input_path': - '<(DEPTH)/chrome_elf/chrome_exe_manifest.template', - }, - 'inputs': [ - '<(template_input_path)', - '<(version_path)', - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/chrome_elf/version_assembly.manifest', - ], - 'action': [ - 'python', '<(version_py_path)', - '-f', '<(version_path)', - '<(template_input_path)', - '<@(_outputs)', - ], - 'message': 'Generating <@(_outputs)', -}
\ No newline at end of file diff --git a/chromium/chrome_elf/chrome_redirects.def b/chromium/chrome_elf/chrome_redirects.def new file mode 100644 index 00000000000..1238dc6c4cf --- /dev/null +++ b/chromium/chrome_elf/chrome_redirects.def @@ -0,0 +1,9 @@ +; Copyright 2014 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 "chrome_redirects.dll" + +EXPORTS + CreateFileW=CreateFileWRedirect + GetRedirectCount diff --git a/chromium/chrome_elf/chrome_redirects_main.cc b/chromium/chrome_elf/chrome_redirects_main.cc new file mode 100644 index 00000000000..960d07c8806 --- /dev/null +++ b/chromium/chrome_elf/chrome_redirects_main.cc @@ -0,0 +1,14 @@ +// Copyright 2014 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/ntdll_cache.h" + +BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) { + if (reason == DLL_PROCESS_ATTACH) + InitCache(); + + return TRUE; +} diff --git a/chromium/chrome_elf/create_file/chrome_create_file.cc b/chromium/chrome_elf/create_file/chrome_create_file.cc new file mode 100644 index 00000000000..2db6f8d8372 --- /dev/null +++ b/chromium/chrome_elf/create_file/chrome_create_file.cc @@ -0,0 +1,330 @@ +// Copyright 2014 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/create_file/chrome_create_file.h" + +#include <string> + +#include "base/strings/string16.h" +#include "chrome_elf/chrome_elf_constants.h" +#include "chrome_elf/chrome_elf_util.h" +#include "chrome_elf/ntdll_cache.h" +#include "sandbox/win/src/interception_internal.h" +#include "sandbox/win/src/nt_internals.h" + +namespace { + +// From ShlObj.h in the Windows SDK. +#define CSIDL_LOCAL_APPDATA 0x001c + +typedef BOOL (WINAPI *PathIsUNCFunction)( + IN LPCWSTR path); + +typedef BOOL (WINAPI *PathAppendFunction)( + IN LPWSTR path, + IN LPCWSTR more); + +typedef BOOL (WINAPI *PathIsPrefixFunction)( + IN LPCWSTR prefix, + IN LPCWSTR path); + +typedef LPCWSTR (WINAPI *PathFindFileName)( + IN LPCWSTR path); + +typedef HRESULT (WINAPI *SHGetFolderPathFunction)( + IN HWND hwnd_owner, + IN int folder, + IN HANDLE token, + IN DWORD flags, + OUT LPWSTR path); + +PathIsUNCFunction g_path_is_unc_func; +PathAppendFunction g_path_append_func; +PathIsPrefixFunction g_path_is_prefix_func; +PathFindFileName g_path_find_filename_func; +SHGetFolderPathFunction g_get_folder_func; + +// Record the number of calls we've redirected so far. +int g_redirect_count = 0; + +// Populates the g_*_func pointers to functions which will be used in +// ShouldBypass(). Chrome_elf cannot have a load-time dependency on shell32 or +// shlwapi as this would induce a load-time dependency on user32.dll. Instead, +// the addresses of the functions we need are retrieved the first time this +// method is called, and cached to avoid subsequent calls to GetProcAddress(). +// It is assumed that the host process will never unload these functions. +// Returns true if all the functions needed are present. +bool PopulateShellFunctions() { + // Early exit if functions have already been populated. + if (g_path_is_unc_func && g_path_append_func && + g_path_is_prefix_func && g_get_folder_func) { + return true; + } + + // Get the addresses of the functions we need and store them for future use. + // These handles are intentionally leaked to ensure that these modules do not + // get unloaded. + HMODULE shell32 = ::LoadLibrary(L"shell32.dll"); + HMODULE shlwapi = ::LoadLibrary(L"shlwapi.dll"); + + if (!shlwapi || !shell32) + return false; + + g_path_is_unc_func = reinterpret_cast<PathIsUNCFunction>( + ::GetProcAddress(shlwapi, "PathIsUNCW")); + g_path_append_func = reinterpret_cast<PathAppendFunction>( + ::GetProcAddress(shlwapi, "PathAppendW")); + g_path_is_prefix_func = reinterpret_cast<PathIsPrefixFunction>( + ::GetProcAddress(shlwapi, "PathIsPrefixW")); + g_path_find_filename_func = reinterpret_cast<PathFindFileName>( + ::GetProcAddress(shlwapi, "PathFindFileNameW")); + g_get_folder_func = reinterpret_cast<SHGetFolderPathFunction>( + ::GetProcAddress(shell32, "SHGetFolderPathW")); + + return g_path_is_unc_func && g_path_append_func && g_path_is_prefix_func && + g_path_find_filename_func && g_get_folder_func; +} + +} // namespace + +// Turn off optimization to make sure these calls don't get inlined. +#pragma optimize("", off) +// Wrapper method for kernel32!CreateFile, to avoid setting off caller +// mitigation detectors. +HANDLE CreateFileWImpl(LPCWSTR file_name, + DWORD desired_access, + DWORD share_mode, + LPSECURITY_ATTRIBUTES security_attributes, + DWORD creation_disposition, + DWORD flags_and_attributes, + HANDLE template_file) { + return CreateFile(file_name, + desired_access, + share_mode, + security_attributes, + creation_disposition, + flags_and_attributes, + template_file); + +} + +HANDLE WINAPI CreateFileWRedirect( + LPCWSTR file_name, + DWORD desired_access, + DWORD share_mode, + LPSECURITY_ATTRIBUTES security_attributes, + DWORD creation_disposition, + DWORD flags_and_attributes, + HANDLE template_file) { + if (ShouldBypass(file_name)) { + ++g_redirect_count; + return CreateFileNTDLL(file_name, + desired_access, + share_mode, + security_attributes, + creation_disposition, + flags_and_attributes, + template_file); + } + return CreateFileWImpl(file_name, + desired_access, + share_mode, + security_attributes, + creation_disposition, + flags_and_attributes, + template_file); +} +#pragma optimize("", on) + +int GetRedirectCount() { + return g_redirect_count; +} + +HANDLE CreateFileNTDLL( + LPCWSTR file_name, + DWORD desired_access, + DWORD share_mode, + LPSECURITY_ATTRIBUTES security_attributes, + DWORD creation_disposition, + DWORD flags_and_attributes, + HANDLE template_file) { + HANDLE file_handle = INVALID_HANDLE_VALUE; + NTSTATUS result = STATUS_UNSUCCESSFUL; + IO_STATUS_BLOCK io_status_block = {}; + ULONG flags = 0; + + // Convert from Win32 domain to to NT creation disposition values. + switch (creation_disposition) { + case CREATE_NEW: + creation_disposition = FILE_CREATE; + break; + case CREATE_ALWAYS: + creation_disposition = FILE_OVERWRITE_IF; + break; + case OPEN_EXISTING: + creation_disposition = FILE_OPEN; + break; + case OPEN_ALWAYS: + creation_disposition = FILE_OPEN_IF; + break; + case TRUNCATE_EXISTING: + creation_disposition = FILE_OVERWRITE; + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + + // Translate the flags that need no validation: + if (!(flags_and_attributes & FILE_FLAG_OVERLAPPED)) + flags |= FILE_SYNCHRONOUS_IO_NONALERT; + + if (flags_and_attributes & FILE_FLAG_WRITE_THROUGH) + flags |= FILE_WRITE_THROUGH; + + if (flags_and_attributes & FILE_FLAG_RANDOM_ACCESS) + flags |= FILE_RANDOM_ACCESS; + + if (flags_and_attributes & FILE_FLAG_SEQUENTIAL_SCAN) + flags |= FILE_SEQUENTIAL_ONLY; + + if (flags_and_attributes & FILE_FLAG_DELETE_ON_CLOSE) { + flags |= FILE_DELETE_ON_CLOSE; + desired_access |= DELETE; + } + + if (flags_and_attributes & FILE_FLAG_BACKUP_SEMANTICS) + flags |= FILE_OPEN_FOR_BACKUP_INTENT; + else + flags |= FILE_NON_DIRECTORY_FILE; + + + if (flags_and_attributes & FILE_FLAG_OPEN_REPARSE_POINT) + flags |= FILE_OPEN_REPARSE_POINT; + + if (flags_and_attributes & FILE_FLAG_OPEN_NO_RECALL) + flags |= FILE_OPEN_NO_RECALL; + + if (!g_ntdll_lookup["RtlInitUnicodeString"]) + return INVALID_HANDLE_VALUE; + + NtCreateFileFunction create_file; + char thunk_buffer[sizeof(sandbox::ThunkData)] = {}; + + if (g_nt_thunk_storage.data[0] != 0) { + create_file = reinterpret_cast<NtCreateFileFunction>(&g_nt_thunk_storage); + // Copy the thunk data to a buffer on the stack for debugging purposes. + memcpy(&thunk_buffer, &g_nt_thunk_storage, sizeof(sandbox::ThunkData)); + } else if (g_ntdll_lookup["NtCreateFile"]) { + create_file = + reinterpret_cast<NtCreateFileFunction>(g_ntdll_lookup["NtCreateFile"]); + } else { + return INVALID_HANDLE_VALUE; + } + + RtlInitUnicodeStringFunction init_unicode_string = + reinterpret_cast<RtlInitUnicodeStringFunction>( + g_ntdll_lookup["RtlInitUnicodeString"]); + + UNICODE_STRING path_unicode_string; + + // Format the path into an NT path. Arguably this should be done with + // RtlDosPathNameToNtPathName_U, but afaict this is equivalent for + // local paths. Using this with a UNC path name will almost certainly + // break in interesting ways. + base::string16 filename_string(L"\\??\\"); + filename_string += file_name; + + init_unicode_string(&path_unicode_string, filename_string.c_str()); + + OBJECT_ATTRIBUTES path_attributes = {}; + InitializeObjectAttributes(&path_attributes, + &path_unicode_string, + OBJ_CASE_INSENSITIVE, + NULL, // No Root Directory + NULL); // No Security Descriptor + + // Set desired_access, and flags_and_attributes to match those + // set by kernel32!CreateFile. + desired_access |= 0x100080; + flags_and_attributes &= 0x2FFA7; + + result = create_file(&file_handle, + desired_access, + &path_attributes, + &io_status_block, + 0, // Allocation size + flags_and_attributes, + share_mode, + creation_disposition, + flags, + NULL, + 0); + + if (result != STATUS_SUCCESS) { + if (result == STATUS_OBJECT_NAME_COLLISION && + creation_disposition == FILE_CREATE) { + SetLastError(ERROR_FILE_EXISTS); + } + return INVALID_HANDLE_VALUE; + } + + if (creation_disposition == FILE_OPEN_IF) { + SetLastError(io_status_block.Information == FILE_OPENED ? + ERROR_ALREADY_EXISTS : ERROR_SUCCESS); + } else if (creation_disposition == FILE_OVERWRITE_IF) { + SetLastError(io_status_block.Information == FILE_OVERWRITTEN ? + ERROR_ALREADY_EXISTS : ERROR_SUCCESS); + } else { + SetLastError(ERROR_SUCCESS); + } + + return file_handle; +} + +bool ShouldBypass(LPCWSTR file_path) { + // Do not redirect in non-browser processes. + if (IsNonBrowserProcess()) + return false; + + // If the shell functions are not present, forward the call to kernel32. + if (!PopulateShellFunctions()) + return false; + + // Forward all UNC filepaths to kernel32. + if (g_path_is_unc_func(file_path)) + return false; + + wchar_t local_appdata_path[MAX_PATH]; + + // Get the %LOCALAPPDATA% Path and append the location of our UserData + // directory to it. + HRESULT appdata_result = g_get_folder_func( + NULL, CSIDL_LOCAL_APPDATA, NULL, 0, local_appdata_path); + + wchar_t buffer[MAX_PATH] = {}; + if (!GetModuleFileNameW(NULL, buffer, MAX_PATH)) + return false; + + bool is_canary = IsCanary(buffer); + + // If getting the %LOCALAPPDATA% path or appending to it failed, then forward + // the call to kernel32. + if (!SUCCEEDED(appdata_result) || + !g_path_append_func(local_appdata_path, is_canary ? + kCanaryAppDataDirName : kAppDataDirName) || + !g_path_append_func(local_appdata_path, kUserDataDirName)) { + return false; + } + + LPCWSTR file_name = g_path_find_filename_func(file_path); + + bool in_userdata_dir = !!g_path_is_prefix_func(local_appdata_path, file_path); + bool is_settings_file = wcscmp(file_name, kPreferencesFilename) == 0 || + wcscmp(file_name, kLocalStateFilename) == 0; + + // Check if we are trying to access the Preferences in the UserData dir. If + // so, then redirect the call to bypass kernel32. + return in_userdata_dir && is_settings_file; +} diff --git a/chromium/chrome_elf/create_file/chrome_create_file.h b/chromium/chrome_elf/create_file/chrome_create_file.h new file mode 100644 index 00000000000..f9f15c35634 --- /dev/null +++ b/chromium/chrome_elf/create_file/chrome_create_file.h @@ -0,0 +1,41 @@ +// Copyright 2014 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_CREATE_FILE_CHROME_CREATE_FILE_H_ +#define CHROME_ELF_CREATE_FILE_CHROME_CREATE_FILE_H_ + +#include <windows.h> + +#include "chrome_elf/chrome_elf_types.h" + +// A CreateFileW replacement that will call NTCreateFile directly when the +// criteria defined in ShouldBypass() are satisfied for |lp_file_name|. +extern "C" HANDLE WINAPI CreateFileWRedirect( + LPCWSTR file_name, + DWORD desired_access, + DWORD share_mode, + LPSECURITY_ATTRIBUTES security_attributes, + DWORD creation_disposition, + DWORD flags_and_attributes, + HANDLE template_file); + +// Returns the count of CreateFile calls redirected so far. +extern "C" int GetRedirectCount(); + +// Partial reimplementation of kernel32!CreateFile (very partial: only handles +// reading and writing to files in the User Data directory). +HANDLE CreateFileNTDLL( + LPCWSTR file_name, + DWORD desired_access, + DWORD share_mode, + LPSECURITY_ATTRIBUTES security_attributes, + DWORD creation_disposition, + DWORD flags_and_attributes, + HANDLE template_file); + +// Determines whether or not we should use our version of CreateFile, or the +// system version (only uses ours if we're writing to the user data directory). +bool ShouldBypass(LPCWSTR file_name); + +#endif // CHROME_ELF_CREATE_FILE_CHROME_CREATE_FILE_H_ diff --git a/chromium/chrome_elf/create_file/chrome_create_file_unittest.cc b/chromium/chrome_elf/create_file/chrome_create_file_unittest.cc new file mode 100644 index 00000000000..5b776073681 --- /dev/null +++ b/chromium/chrome_elf/create_file/chrome_create_file_unittest.cc @@ -0,0 +1,408 @@ +// Copyright 2014 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/create_file/chrome_create_file.h" + +#include <windows.h> + +#include <bitset> +#include <string> + +#include "base/base_paths_win.h" +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/files/scoped_temp_dir.h" +#include "base/path_service.h" +#include "base/threading/platform_thread.h" +#include "base/win/iat_patch_function.h" +#include "base/win/scoped_handle.h" +#include "base/win/windows_version.h" +#include "chrome_elf/chrome_elf_constants.h" +#include "chrome_elf/ntdll_cache.h" +#include "sandbox/win/src/interception_internal.h" +#include "sandbox/win/src/nt_internals.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + + +namespace { + +// Test fixtures ------------------------------------------------------------- + +class ChromeCreateFileTest : public PlatformTest { + protected: + struct NtCreateFileParams { + ACCESS_MASK desired_access; + OBJECT_ATTRIBUTES object_attributes; + PLARGE_INTEGER allocation_size; + ULONG file_attributes; + ULONG share_access; + ULONG create_disposition; + ULONG create_options; + PVOID ea_buffer; + ULONG ea_length; + }; + + enum CallPath { + ELF, + KERNEL + }; + + template<CallPath path> + static NTSTATUS WINAPI FakeNtCreateFile( + PHANDLE file_handle, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + PIO_STATUS_BLOCK io_status_block, + PLARGE_INTEGER allocation_size, + ULONG file_attributes, + ULONG share_access, + ULONG create_disposition, + ULONG create_options, + PVOID ea_buffer, + ULONG ea_length) { + return self_->HandleCreateFileCall(file_handle, + desired_access, + object_attributes, + io_status_block, + allocation_size, + file_attributes, + share_access, + create_disposition, + create_options, + ea_buffer, + ea_length, + path); + } + + virtual void SetUp() OVERRIDE { + original_thread_ = base::PlatformThread::CurrentId(); + InitCache(); + PlatformTest::SetUp(); + + base::FilePath user_data_dir; + PathService::Get(base::DIR_LOCAL_APP_DATA, &user_data_dir); + ASSERT_TRUE(temp_dir_.CreateUniqueTempDirUnderPath(user_data_dir)); + ASSERT_TRUE(temp_dir2_.CreateUniqueTempDir()); + self_ = this; + } + + void UnsetThunkStorage() { + DWORD old_protect = 0; + EXPECT_TRUE(::VirtualProtect(&g_nt_thunk_storage, + sizeof(g_nt_thunk_storage), + PAGE_EXECUTE_READWRITE, + &old_protect)); + memset(&g_nt_thunk_storage, 0, sizeof(g_nt_thunk_storage)); + + EXPECT_TRUE(::VirtualProtect(&g_nt_thunk_storage, + sizeof(g_nt_thunk_storage), + PAGE_EXECUTE_READ, + &old_protect)); + } + + void RedirectNtCreateFileCalls() { + UnsetThunkStorage(); + old_func_ptr_ = + reinterpret_cast<NtCreateFileFunction>(g_ntdll_lookup["NtCreateFile"]); + + // KernelBase.dll only exists for Win7 and later, prior to that, kernel32 + // imports from ntdll directly. + if (base::win::GetVersion() < base::win::VERSION_WIN7) { + patcher_.Patch(L"kernel32.dll", "ntdll.dll", "NtCreateFile", + reinterpret_cast<void(*)()>(&FakeNtCreateFile<KERNEL>)); + } else { + patcher_.Patch(L"kernelbase.dll", "ntdll.dll", "NtCreateFile", + reinterpret_cast<void(*)()>(&FakeNtCreateFile<KERNEL>)); + } + + g_ntdll_lookup["NtCreateFile"] = reinterpret_cast<void(*)()>( + &ChromeCreateFileTest::FakeNtCreateFile<ELF>); + } + + void ResetNtCreateFileCalls() { + g_ntdll_lookup["NtCreateFile"] = reinterpret_cast<void*>(old_func_ptr_); + patcher_.Unpatch(); + } + + NTSTATUS HandleCreateFileCall(PHANDLE file_handle, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + PIO_STATUS_BLOCK io_status_block, + PLARGE_INTEGER allocation_size, + ULONG file_attributes, + ULONG share_access, + ULONG create_disposition, + ULONG create_options, + PVOID ea_buffer, + ULONG ea_length, + CallPath call_path) { + if (original_thread_ == base::PlatformThread::CurrentId()) { + SetParams(desired_access, + object_attributes, + allocation_size, + file_attributes, + share_access, + create_disposition, + create_options, + ea_buffer, + ea_length, + call_path == ELF ? &elf_params_ : &kernel_params_); + } + + // Forward the call to the real NTCreateFile. + return old_func_ptr_(file_handle, + desired_access, + object_attributes, + io_status_block, + allocation_size, + file_attributes, + share_access, + create_disposition, + create_options, + ea_buffer, + ea_length); + } + + void SetParams(ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + PLARGE_INTEGER allocation_size, + ULONG file_attributes, + ULONG share_access, + ULONG create_disposition, + ULONG create_options, + PVOID ea_buffer, + ULONG ea_length, + NtCreateFileParams* params) { + params->desired_access = desired_access; + params->object_attributes.Length = object_attributes->Length; + params->object_attributes.ObjectName = object_attributes->ObjectName; + params->object_attributes.RootDirectory = object_attributes->RootDirectory; + params->object_attributes.Attributes = object_attributes->Attributes; + params->object_attributes.SecurityDescriptor = + object_attributes->SecurityDescriptor; + params->object_attributes.SecurityQualityOfService = + object_attributes->SecurityQualityOfService; + params->allocation_size = allocation_size; + params->file_attributes = file_attributes; + params->share_access = share_access; + params->create_disposition = create_disposition; + params->create_options = create_options; + params->ea_buffer = ea_buffer; + params->ea_length = ea_length; + } + + void CheckParams() { + std::bitset<32> elf((int) elf_params_.desired_access); + std::bitset<32> ker((int) kernel_params_.desired_access); + + EXPECT_EQ(kernel_params_.desired_access, elf_params_.desired_access) + << elf << "\n" << ker; + EXPECT_EQ(kernel_params_.object_attributes.Length, + elf_params_.object_attributes.Length); + EXPECT_EQ(kernel_params_.object_attributes.RootDirectory, + elf_params_.object_attributes.RootDirectory); + EXPECT_EQ(kernel_params_.object_attributes.Attributes, + elf_params_.object_attributes.Attributes); + EXPECT_EQ(kernel_params_.object_attributes.SecurityDescriptor, + elf_params_.object_attributes.SecurityDescriptor); + EXPECT_EQ(kernel_params_.allocation_size, elf_params_.allocation_size); + EXPECT_EQ(kernel_params_.file_attributes, elf_params_.file_attributes); + EXPECT_EQ(kernel_params_.share_access, elf_params_.share_access); + EXPECT_EQ(kernel_params_.create_disposition, + elf_params_.create_disposition); + EXPECT_EQ(kernel_params_.create_options, elf_params_.create_options); + EXPECT_EQ(kernel_params_.ea_buffer, elf_params_.ea_buffer); + EXPECT_EQ(kernel_params_.ea_length, elf_params_.ea_length); + } + + void DoWriteCheck(const base::FilePath& path, DWORD flag, bool is_system) { + base::win::ScopedHandle file_handle; + const char kTestData[] = "0123456789"; + int buffer_size = sizeof(kTestData) - 1; + DWORD bytes_written; + + if (is_system) { + file_handle.Set(::CreateFileW(path.value().c_str(), + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | flag, + NULL)); + } else { + file_handle.Set(CreateFileNTDLL(path.value().c_str(), + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | flag, + NULL)); + } + + + EXPECT_FALSE(file_handle == INVALID_HANDLE_VALUE); + ::WriteFile(file_handle, kTestData, buffer_size, &bytes_written, NULL); + EXPECT_EQ(buffer_size, bytes_written); + } + + void DoReadCheck(const base::FilePath& path, DWORD flag, bool is_system) { + base::win::ScopedHandle file_handle; + const char kTestData[] = "0123456789"; + int buffer_size = sizeof(kTestData) - 1; + DWORD bytes_read; + char read_buffer[10]; + + if (is_system) { + file_handle.Set(::CreateFileW(path.value().c_str(), + GENERIC_READ, + 0, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | flag, + NULL)); + } else { + file_handle.Set(CreateFileNTDLL(path.value().c_str(), + GENERIC_READ, + 0, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | flag, + NULL)); + } + + EXPECT_FALSE(file_handle == INVALID_HANDLE_VALUE); + ::ReadFile(file_handle, read_buffer, buffer_size, &bytes_read, NULL); + EXPECT_EQ(buffer_size, bytes_read); + EXPECT_EQ(0, memcmp(kTestData, read_buffer, bytes_read)); + } + + void RunChecks(DWORD flag, bool check_reads) { + // Make sure we can write to this file handle when called via the system. + base::FilePath junk_path_1 = temp_dir_.path().Append(L"junk_1.txt"); + base::FilePath junk_path_2 = temp_dir_.path().Append(L"junk_2.txt"); + DoWriteCheck(junk_path_1, flag, true); + DoWriteCheck(junk_path_2, flag, false); + CheckParams(); + + if (check_reads) { + // Make sure we can read from this file handle when called via the system. + DoReadCheck(junk_path_1, flag, true); + DoReadCheck(junk_path_2, flag, false); + CheckParams(); + } + base::DeleteFile(junk_path_1, false); + base::DeleteFile(junk_path_2, false); + + } + + static ChromeCreateFileTest* self_; + + NtCreateFileFunction old_func_ptr_; + base::ScopedTempDir temp_dir_; + base::ScopedTempDir temp_dir2_; + base::win::IATPatchFunction patcher_; + NtCreateFileParams kernel_params_; + NtCreateFileParams elf_params_; + base::PlatformThreadId original_thread_; +}; + +ChromeCreateFileTest* ChromeCreateFileTest::self_ = NULL; + +// Tests --------------------------------------------------------------------- +TEST_F(ChromeCreateFileTest, CheckParams_FILE_ATTRIBUTE_NORMAL) { + RedirectNtCreateFileCalls(); + RunChecks(FILE_ATTRIBUTE_NORMAL, true); + ResetNtCreateFileCalls(); +} + +TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_WRITE_THROUGH) { + RedirectNtCreateFileCalls(); + RunChecks(FILE_FLAG_WRITE_THROUGH, true); + ResetNtCreateFileCalls(); +} + +TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_RANDOM_ACCESS) { + RedirectNtCreateFileCalls(); + RunChecks(FILE_FLAG_RANDOM_ACCESS, true); + ResetNtCreateFileCalls(); +} + +TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_SEQUENTIAL_SCAN) { + RedirectNtCreateFileCalls(); + RunChecks(FILE_FLAG_SEQUENTIAL_SCAN, true); + ResetNtCreateFileCalls(); +} + +TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_DELETE_ON_CLOSE) { + RedirectNtCreateFileCalls(); + RunChecks(FILE_FLAG_DELETE_ON_CLOSE, false); + ResetNtCreateFileCalls(); +} + +TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_BACKUP_SEMANTICS) { + RedirectNtCreateFileCalls(); + RunChecks(FILE_FLAG_BACKUP_SEMANTICS, true); + ResetNtCreateFileCalls(); +} + +TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_REPARSE_POINT) { + RedirectNtCreateFileCalls(); + RunChecks(FILE_FLAG_OPEN_REPARSE_POINT, true); + ResetNtCreateFileCalls(); +} + +TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_NO_RECALL) { + RedirectNtCreateFileCalls(); + RunChecks(FILE_FLAG_OPEN_NO_RECALL, true); + ResetNtCreateFileCalls(); +} + +TEST_F(ChromeCreateFileTest, BypassTest) { + std::wstring UNC_filepath_file(L"\\\\.\\some_file.txt"); + + base::FilePath local_path; + PathService::Get(base::DIR_LOCAL_APP_DATA, &local_path); + + base::FilePath local_prefs_path = local_path.Append(kAppDataDirName).Append( + kUserDataDirName).Append(L"default\\Preferences"); + base::FilePath local_state_path = local_path.Append(kAppDataDirName).Append( + kUserDataDirName).Append(L"ninja\\Local State"); + base::FilePath local_junk_path = local_path.Append(kAppDataDirName).Append( + kUserDataDirName).Append(L"default\\Junk"); + + base::FilePath desktop_path; + PathService::Get(base::DIR_USER_DESKTOP, &desktop_path); + base::FilePath desktop_junk_path = + desktop_path.Append(L"Downloads\\junk.txt"); + base::FilePath desktop_prefs_path = + desktop_path.Append(L"Downloads\\Preferences"); + + // Don't redirect UNC files. + EXPECT_FALSE(ShouldBypass(UNC_filepath_file.c_str())); + + // Don't redirect if file is not in UserData directory. + EXPECT_FALSE(ShouldBypass(desktop_junk_path.value().c_str())); + EXPECT_FALSE(ShouldBypass(desktop_prefs_path.value().c_str())); + + // Only redirect "Preferences" and "Local State" files. + EXPECT_TRUE(ShouldBypass(local_prefs_path.value().c_str())); + EXPECT_TRUE(ShouldBypass(local_state_path.value().c_str())); + EXPECT_FALSE(ShouldBypass(local_junk_path.value().c_str())); +} + +TEST_F(ChromeCreateFileTest, ReadWriteFromNtDll) { + UnsetThunkStorage(); + base::FilePath file_name = temp_dir_.path().Append(L"some_file.txt"); + DoWriteCheck(file_name, FILE_ATTRIBUTE_NORMAL, false); + DoReadCheck(file_name, FILE_ATTRIBUTE_NORMAL, false); +} + +TEST_F(ChromeCreateFileTest, ReadWriteFromThunk) { + base::FilePath file_name = temp_dir_.path().Append(L"some_file.txt"); + DoWriteCheck(file_name, FILE_ATTRIBUTE_NORMAL, false); + DoReadCheck(file_name, FILE_ATTRIBUTE_NORMAL, false); +} + +} // namespace diff --git a/chromium/chrome_elf/dll_hash.gypi b/chromium/chrome_elf/dll_hash.gypi new file mode 100644 index 00000000000..9e89a241e42 --- /dev/null +++ b/chromium/chrome_elf/dll_hash.gypi @@ -0,0 +1,31 @@ +# Copyright 2014 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': 'dll_hash', + 'type': 'static_library', + 'dependencies': [ + '../base/base.gyp:base', + ], + 'sources': [ + 'dll_hash/dll_hash.cc', + 'dll_hash/dll_hash.h', + ], + }, + { + 'target_name': 'dll_hash_main', + 'type': 'executable', + 'dependencies': [ + 'dll_hash', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'dll_hash/dll_hash_main.cc', + ], + } + ] +}
\ No newline at end of file diff --git a/chromium/chrome_elf/dll_hash/dll_hash.cc b/chromium/chrome_elf/dll_hash/dll_hash.cc new file mode 100644 index 00000000000..ecd1491f64f --- /dev/null +++ b/chromium/chrome_elf/dll_hash/dll_hash.cc @@ -0,0 +1,14 @@ +// Copyright 2014 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/hash.h" +#include "chrome_elf/dll_hash/dll_hash.h" + +int DllNameToHash(std::string dll_name) { + uint32 data = base::Hash(dll_name); + + // Strip off the signed bit because UMA doesn't support negative values, + // but takes a signed int as input. + return static_cast<int>(data & 0x7fffffff); +} diff --git a/chromium/chrome_elf/dll_hash/dll_hash.h b/chromium/chrome_elf/dll_hash/dll_hash.h new file mode 100644 index 00000000000..82bec8af1cd --- /dev/null +++ b/chromium/chrome_elf/dll_hash/dll_hash.h @@ -0,0 +1,13 @@ +// Copyright 2014 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_DLL_HASH_DLL_HASH_H_ +#define CHROME_ELF_DLL_HASH_DLL_HASH_H_ + +#include <string> + +// Convert a dll name to a hash that can be sent via UMA. +int DllNameToHash(std::string dll_name); + +#endif // CHROME_ELF_DLL_HASH_DLL_HASH_H_ diff --git a/chromium/chrome_elf/dll_hash/dll_hash_main.cc b/chromium/chrome_elf/dll_hash/dll_hash_main.cc new file mode 100644 index 00000000000..a360263af43 --- /dev/null +++ b/chromium/chrome_elf/dll_hash/dll_hash_main.cc @@ -0,0 +1,28 @@ +// Copyright 2014 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. +// +// This is a utility executable used for generating hashes for dll names +// for inclusion in tools/metrics/histograms/histograms.xml. Every +// dll name must have a corresponding entry in the enum there. + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "chrome_elf/dll_hash/dll_hash.h" + +int main(int argc, char** argv) { + if (argc < 2) { + fprintf(stderr, "Usage: %s <dll name> <dll name> <...>\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "Prints hashes for dll names.\n"); + fprintf(stderr, "Example: %s \"my_dll.dll\" \"user32.dll\"\n", argv[0]); + return 1; + } + for (int i = 1; i < argc; i++) { + int hash = DllNameToHash(std::string(argv[i])); + printf("<int value=\"%d\" label=\"%s\"/>\n", hash, argv[i]); + } + return 0; +} diff --git a/chromium/chrome_elf/elf_imports_unittest.cc b/chromium/chrome_elf/elf_imports_unittest.cc new file mode 100644 index 00000000000..136912beac9 --- /dev/null +++ b/chromium/chrome_elf/elf_imports_unittest.cc @@ -0,0 +1,101 @@ +// Copyright 2014 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 <stdint.h> +#include <windows.h> + +#include <algorithm> +#include <vector> + +#include "base/base_paths.h" +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/files/memory_mapped_file.h" +#include "base/path_service.h" +#include "base/strings/string_util.h" +#include "base/win/pe_image.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class ELFImportsTest : public testing::Test { + protected: + static bool ImportsCallback(const base::win::PEImage &image, + LPCSTR module, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, + PVOID cookie) { + std::vector<std::string>* import_list = + reinterpret_cast<std::vector<std::string>*>(cookie); + import_list->push_back(module); + return true; + } + + void GetImports(const base::FilePath& module_path, + std::vector<std::string>* imports) { + ASSERT_TRUE(imports != NULL); + + base::MemoryMappedFile module_mmap; + + ASSERT_TRUE(module_mmap.Initialize(module_path)); + base::win::PEImageAsData pe_image_data( + reinterpret_cast<HMODULE>(const_cast<uint8*>(module_mmap.data()))); + pe_image_data.EnumImportChunks(ELFImportsTest::ImportsCallback, imports); + } +}; + +TEST_F(ELFImportsTest, ChromeElfSanityCheck) { + std::vector<std::string> elf_imports; + + base::FilePath dll; + ASSERT_TRUE(PathService::Get(base::DIR_EXE, &dll)); + dll = dll.Append(L"chrome_elf.dll"); + GetImports(dll, &elf_imports); + + // Check that ELF has imports. + ASSERT_LT(0u, elf_imports.size()) << "Ensure the chrome_elf_unittests " + "target was built, instead of chrome_elf_unittests.exe"; + + std::vector<std::string>::iterator it(elf_imports.begin()); + + static const char* const kValidFilePatterns[] = { + "KERNEL32.dll", + "MSVC*", +#if defined(SYZYASAN) + "syzyasan_rtl.dll", +#endif + "ADVAPI32.dll" + }; + + // Make sure all of ELF's imports are in the valid imports list. + for (; it != elf_imports.end(); it++) { + bool match = false; + for (int i = 0; i < arraysize(kValidFilePatterns); ++i) { + if (MatchPattern(*it, kValidFilePatterns[i])) + match = true; + } + ASSERT_TRUE(match) << "Illegal import in chrome_elf.dll."; + } +} + +TEST_F(ELFImportsTest, ChromeExeSanityCheck) { + std::vector<std::string> exe_imports; + + base::FilePath exe; + ASSERT_TRUE(PathService::Get(base::DIR_EXE, &exe)); + exe = exe.Append(L"chrome.exe"); + GetImports(exe, &exe_imports); + + // Check that chrome.exe has imports. + ASSERT_LT(0u, exe_imports.size()) << "Ensure the chrome_elf_unittests " + "target was built, instead of chrome_elf_unittests.exe"; + + // Chrome.exe's first import must be ELF. + EXPECT_EQ("chrome_elf.dll", exe_imports[0]) << + "Illegal import order in chrome.exe (ensure the chrome_elf_unittest " + "target was built, instead of just chrome_elf_unittests.exe)"; +} + +} // namespace diff --git a/chromium/chrome_elf/ntdll_cache.cc b/chromium/chrome_elf/ntdll_cache.cc index e5504421ae4..a0429a45aed 100644 --- a/chromium/chrome_elf/ntdll_cache.cc +++ b/chromium/chrome_elf/ntdll_cache.cc @@ -2,50 +2,88 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome_elf/ntdll_cache.h" + #include <stdint.h> #include <windows.h> -#include "chrome_elf/ntdll_cache.h" +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/win/pe_image.h" +#include "chrome_elf/thunk_getter.h" +#include "sandbox/win/src/interception_internal.h" +#include "sandbox/win/src/internal_types.h" +#include "sandbox/win/src/service_resolver.h" FunctionLookupTable g_ntdll_lookup; +// Allocate storage for thunks in a page of this module to save on doing +// an extra allocation at run time. +#pragma section(".crthunk",read,execute) +__declspec(allocate(".crthunk")) sandbox::ThunkData g_nt_thunk_storage; + + + +namespace { + +bool EnumExportsCallback(const base::win::PEImage& image, + DWORD ordinal, + DWORD hint, + LPCSTR name, + PVOID function_addr, + LPCSTR forward, + PVOID cookie) { + // Our lookup only cares about named functions that are in ntdll, so skip + // unnamed or forwarded exports. + if (name && function_addr) + g_ntdll_lookup[std::string(name)] = function_addr; + + return true; +} + +} // namespace + void InitCache() { HMODULE ntdll_handle = ::GetModuleHandle(L"ntdll.dll"); - // To find the Export Address Table address, we start from the DOS header. - // The module handle is actually the address of the header. - IMAGE_DOS_HEADER* dos_header = - reinterpret_cast<IMAGE_DOS_HEADER*>(ntdll_handle); - // The e_lfanew is an offset from the DOS header to the NT header. It should - // never be 0. - IMAGE_NT_HEADERS* nt_headers = reinterpret_cast<IMAGE_NT_HEADERS*>( - ntdll_handle + dos_header->e_lfanew / sizeof(uint32_t)); - // For modules that have an import address table, its offset from the - // DOS header is stored in the second data directory's VirtualAddress. - if (!nt_headers->OptionalHeader.DataDirectory[0].VirtualAddress) - return; - - BYTE* base_addr = reinterpret_cast<BYTE*>(ntdll_handle); - - IMAGE_DATA_DIRECTORY* exports_data_dir = - &nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; - - IMAGE_EXPORT_DIRECTORY* exports = reinterpret_cast<IMAGE_EXPORT_DIRECTORY*>( - base_addr + exports_data_dir->VirtualAddress); - - WORD* ordinals = reinterpret_cast<WORD*>( - base_addr + exports->AddressOfNameOrdinals); - DWORD* names = reinterpret_cast<DWORD*>( - base_addr + exports->AddressOfNames); - DWORD* funcs = reinterpret_cast<DWORD*>( - base_addr + exports->AddressOfFunctions); - int num_entries = exports->NumberOfNames; - - for (int i = 0; i < num_entries; i++) { - char* name = reinterpret_cast<char*>(base_addr + names[i]); - WORD ord = ordinals[i]; - DWORD func = funcs[ord]; - FARPROC func_addr = reinterpret_cast<FARPROC>(func + base_addr); - g_ntdll_lookup[std::string(name)] = func_addr; + base::win::PEImage ntdll_image(ntdll_handle); + + ntdll_image.EnumExports(EnumExportsCallback, NULL); + + // If ntdll has already been patched, don't copy it. + const bool kRelaxed = false; + + // Create a thunk via the appropriate ServiceResolver instance. + scoped_ptr<sandbox::ServiceResolverThunk> thunk(GetThunk(kRelaxed)); + + if (thunk.get()) { + BYTE* thunk_storage = reinterpret_cast<BYTE*>(&g_nt_thunk_storage); + + // Mark the thunk storage as readable and writeable, since we + // are ready to write to it. + DWORD old_protect = 0; + if (!::VirtualProtect(&g_nt_thunk_storage, + sizeof(g_nt_thunk_storage), + PAGE_EXECUTE_READWRITE, + &old_protect)) { + return; + } + + size_t storage_used = 0; + NTSTATUS ret = thunk->CopyThunk(::GetModuleHandle(sandbox::kNtdllName), + "NtCreateFile", + thunk_storage, + sizeof(sandbox::ThunkData), + &storage_used); + + if (!NT_SUCCESS(ret)) { + memset(&g_nt_thunk_storage, 0, sizeof(g_nt_thunk_storage)); + } + + // Ensure that the pointer to the old function can't be changed. + ::VirtualProtect(&g_nt_thunk_storage, + sizeof(g_nt_thunk_storage), + PAGE_EXECUTE_READ, + &old_protect); } } diff --git a/chromium/chrome_elf/ntdll_cache.h b/chromium/chrome_elf/ntdll_cache.h index 4608cf19367..5e4fb2bf7a3 100644 --- a/chromium/chrome_elf/ntdll_cache.h +++ b/chromium/chrome_elf/ntdll_cache.h @@ -7,9 +7,15 @@ #include "chrome_elf/chrome_elf_types.h" +namespace sandbox { +struct ThunkData; +} + // Caches the addresses of all functions exported by ntdll in |g_ntdll_lookup|. void InitCache(); extern FunctionLookupTable g_ntdll_lookup; +extern sandbox::ThunkData g_nt_thunk_storage; + #endif // CHROME_ELF_NTDLL_CACHE_H_ diff --git a/chromium/chrome_elf/thunk_getter.cc b/chromium/chrome_elf/thunk_getter.cc new file mode 100644 index 00000000000..8421e5eecf1 --- /dev/null +++ b/chromium/chrome_elf/thunk_getter.cc @@ -0,0 +1,142 @@ +// Copyright 2014 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 <stdint.h> +#include <windows.h> + +#include "base/basictypes.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" + +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); +}; + +} // namespace + +sandbox::ServiceResolverThunk* GetThunk(bool relaxed) { + // Create a thunk via the appropriate ServiceResolver instance. + sandbox::ServiceResolverThunk* thunk = NULL; + + // No thunks for unsupported OS versions. + OSInfo os_info; + if (os_info.version() <= VERSION_PRE_XP_SP2) + return thunk; + + // Pseudo-handle, no need to close. + HANDLE current_process = ::GetCurrentProcess(); + +#if defined(_WIN64) + // ServiceResolverThunk can handle all the formats in 64-bit (instead only + // handling one like it does in 32-bit versions). + thunk = new sandbox::ServiceResolverThunk(current_process, relaxed); +#else + if (GetWOW64StatusForCurrentProcess() == WOW64_ENABLED) { + if (os_info.version() >= VERSION_WIN8) + thunk = new sandbox::Wow64W8ResolverThunk(current_process, relaxed); + else + thunk = new sandbox::Wow64ResolverThunk(current_process, relaxed); + } else if (os_info.version() >= VERSION_WIN8) { + thunk = new sandbox::Win8ResolverThunk(current_process, relaxed); + } else { + thunk = new sandbox::ServiceResolverThunk(current_process, relaxed); + } +#endif + + return thunk; +} diff --git a/chromium/chrome_elf/thunk_getter.h b/chromium/chrome_elf/thunk_getter.h new file mode 100644 index 00000000000..5bc20fe81e5 --- /dev/null +++ b/chromium/chrome_elf/thunk_getter.h @@ -0,0 +1,16 @@ +// Copyright 2014 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_THUNK_GETTER_H_ +#define CHROME_ELF_THUNK_GETTER_H_ + +namespace sandbox { +class ServiceResolverThunk; +} + +// Creates a |ServiceResolverThunk| based on the OS version. Ownership of the +// resulting thunk is passed to the caller. +sandbox::ServiceResolverThunk* GetThunk(bool relaxed); + +#endif // CHROME_ELF_THUNK_GETTER_H_ diff --git a/chromium/chrome_elf/version_assembly_manifest.template b/chromium/chrome_elf/version_assembly_manifest.template deleted file mode 100644 index 153194c3d31..00000000000 --- a/chromium/chrome_elf/version_assembly_manifest.template +++ /dev/null @@ -1,8 +0,0 @@ -<assembly - xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'> - <assemblyIdentity - name='@MAJOR@.@MINOR@.@BUILD@.@PATCH@' - version='@MAJOR@.@MINOR@.@BUILD@.@PATCH@' - type='win32'/> - <file name='chrome_elf.dll'/> -</assembly>
\ No newline at end of file diff --git a/chromium/chrome_elf/version_assembly_manifest_action.gypi b/chromium/chrome_elf/version_assembly_manifest_action.gypi deleted file mode 100644 index 9c443153321..00000000000 --- a/chromium/chrome_elf/version_assembly_manifest_action.gypi +++ /dev/null @@ -1,37 +0,0 @@ -# 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. - -# This file contains an action which can be used to construct a manifest file -# with the same name as the version directory so that chrome.exe identifies the -# version directory as an assembly. This will be copied over to the version -# directory by the installer script. - -# To use this the following variables need to be defined: -# version_path: string: path to file containing version data (e.g. -# chrome/VERSION). -# version_py_path: string: path to file containing version script (e.g. -# chrome/tools/build/version.py). -# version_full: string: version string in W.X.Y.Z form. - - -{ - 'variables': { - 'template_input_path': - '<(DEPTH)/chrome_elf/version_assembly_manifest.template', - }, - 'inputs': [ - '<(template_input_path)', - '<(version_path)', - ], - 'outputs': [ - '<(PRODUCT_DIR)/<(version_full).manifest', - ], - 'action': [ - 'python', '<(version_py_path)', - '-f', '<(version_path)', - '<(template_input_path)', - '<@(_outputs)', - ], - 'message': 'Generating <@(_outputs)', -}
\ No newline at end of file |