michael@0: // Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "sandbox/win/src/process_mitigations.h" michael@0: michael@0: #include michael@0: michael@0: #include "base/win/windows_version.h" michael@0: #include "sandbox/win/src/nt_internals.h" michael@0: #include "sandbox/win/src/sandbox_utils.h" michael@0: #include "sandbox/win/src/win_utils.h" michael@0: michael@0: namespace { michael@0: michael@0: // Functions for enabling policies. michael@0: typedef BOOL (WINAPI *SetProcessDEPPolicyFunction)(DWORD dwFlags); michael@0: michael@0: typedef BOOL (WINAPI *SetProcessMitigationPolicyFunction)( michael@0: PROCESS_MITIGATION_POLICY mitigation_policy, michael@0: PVOID buffer, michael@0: SIZE_T length); michael@0: michael@0: typedef BOOL (WINAPI *SetDefaultDllDirectoriesFunction)( michael@0: DWORD DirectoryFlags); michael@0: michael@0: } // namespace michael@0: michael@0: namespace sandbox { michael@0: michael@0: bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags) { michael@0: if (!CanSetProcessMitigationsPostStartup(flags)) michael@0: return false; michael@0: michael@0: // We can't apply anything before Win XP, so just return cleanly. michael@0: if (!IsXPSP2OrLater()) michael@0: return true; michael@0: michael@0: base::win::Version version = base::win::GetVersion(); michael@0: HMODULE module = ::GetModuleHandleA("kernel32.dll"); michael@0: michael@0: if (version >= base::win::VERSION_VISTA && michael@0: (flags & MITIGATION_DLL_SEARCH_ORDER)) { michael@0: SetDefaultDllDirectoriesFunction set_default_dll_directories = michael@0: reinterpret_cast( michael@0: ::GetProcAddress(module, "SetDefaultDllDirectories")); michael@0: michael@0: // Check for SetDefaultDllDirectories since it requires KB2533623. michael@0: if (set_default_dll_directories) { michael@0: if (!set_default_dll_directories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) && michael@0: ERROR_ACCESS_DENIED != ::GetLastError()) { michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Set the heap to terminate on corruption michael@0: if (version >= base::win::VERSION_VISTA && michael@0: (flags & MITIGATION_HEAP_TERMINATE)) { michael@0: if (!::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, michael@0: NULL, 0) && michael@0: ERROR_ACCESS_DENIED != ::GetLastError()) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: #if !defined(_WIN64) // DEP is always enabled on 64-bit. michael@0: if (flags & MITIGATION_DEP) { michael@0: DWORD dep_flags = PROCESS_DEP_ENABLE; michael@0: // DEP support is quirky on XP, so don't force a failure in that case. michael@0: const bool return_on_fail = version >= base::win::VERSION_VISTA; michael@0: michael@0: if (flags & MITIGATION_DEP_NO_ATL_THUNK) michael@0: dep_flags |= PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION; michael@0: michael@0: SetProcessDEPPolicyFunction set_process_dep_policy = michael@0: reinterpret_cast( michael@0: ::GetProcAddress(module, "SetProcessDEPPolicy")); michael@0: if (set_process_dep_policy) { michael@0: if (!set_process_dep_policy(dep_flags) && michael@0: ERROR_ACCESS_DENIED != ::GetLastError() && return_on_fail) { michael@0: return false; michael@0: } michael@0: } else { michael@0: // We're on XP sp2, so use the less standard approach. michael@0: // For reference: http://www.uninformed.org/?v=2&a=4 michael@0: const int MEM_EXECUTE_OPTION_ENABLE = 1; michael@0: const int MEM_EXECUTE_OPTION_DISABLE = 2; michael@0: const int MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION = 4; michael@0: const int MEM_EXECUTE_OPTION_PERMANENT = 8; michael@0: michael@0: NtSetInformationProcessFunction set_information_process = NULL; michael@0: ResolveNTFunctionPtr("NtSetInformationProcess", michael@0: &set_information_process); michael@0: if (!set_information_process) michael@0: return false; michael@0: ULONG dep = MEM_EXECUTE_OPTION_DISABLE | MEM_EXECUTE_OPTION_PERMANENT; michael@0: if (!(dep_flags & PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION)) michael@0: dep |= MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION; michael@0: if (!SUCCEEDED(set_information_process(GetCurrentProcess(), michael@0: ProcessExecuteFlags, michael@0: &dep, sizeof(dep))) && michael@0: ERROR_ACCESS_DENIED != ::GetLastError() && return_on_fail) { michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // This is all we can do in Win7 and below. michael@0: if (version < base::win::VERSION_WIN8) michael@0: return true; michael@0: michael@0: SetProcessMitigationPolicyFunction set_process_mitigation_policy = michael@0: reinterpret_cast( michael@0: ::GetProcAddress(module, "SetProcessMitigationPolicy")); michael@0: if (!set_process_mitigation_policy) michael@0: return false; michael@0: michael@0: // Enable ASLR policies. michael@0: if (flags & MITIGATION_RELOCATE_IMAGE) { michael@0: PROCESS_MITIGATION_ASLR_POLICY policy = { 0 }; michael@0: policy.EnableForceRelocateImages = true; michael@0: policy.DisallowStrippedImages = (flags & michael@0: MITIGATION_RELOCATE_IMAGE_REQUIRED) == michael@0: MITIGATION_RELOCATE_IMAGE_REQUIRED; michael@0: michael@0: if (!set_process_mitigation_policy(ProcessASLRPolicy, &policy, michael@0: sizeof(policy)) && michael@0: ERROR_ACCESS_DENIED != ::GetLastError()) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Enable strict handle policies. michael@0: if (flags & MITIGATION_STRICT_HANDLE_CHECKS) { michael@0: PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY policy = { 0 }; michael@0: policy.HandleExceptionsPermanentlyEnabled = michael@0: policy.RaiseExceptionOnInvalidHandleReference = true; michael@0: michael@0: if (!set_process_mitigation_policy(ProcessStrictHandleCheckPolicy, &policy, michael@0: sizeof(policy)) && michael@0: ERROR_ACCESS_DENIED != ::GetLastError()) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Enable system call policies. michael@0: if (flags & MITIGATION_WIN32K_DISABLE) { michael@0: PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = { 0 }; michael@0: policy.DisallowWin32kSystemCalls = true; michael@0: michael@0: if (!set_process_mitigation_policy(ProcessSystemCallDisablePolicy, &policy, michael@0: sizeof(policy)) && michael@0: ERROR_ACCESS_DENIED != ::GetLastError()) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Enable system call policies. michael@0: if (flags & MITIGATION_EXTENSION_DLL_DISABLE) { michael@0: PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY policy = { 0 }; michael@0: policy.DisableExtensionPoints = true; michael@0: michael@0: if (!set_process_mitigation_policy(ProcessExtensionPointDisablePolicy, michael@0: &policy, sizeof(policy)) && michael@0: ERROR_ACCESS_DENIED != ::GetLastError()) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void ConvertProcessMitigationsToPolicy(MitigationFlags flags, michael@0: DWORD64* policy_flags, size_t* size) { michael@0: base::win::Version version = base::win::GetVersion(); michael@0: michael@0: *policy_flags = 0; michael@0: #if defined(_WIN64) michael@0: *size = sizeof(*policy_flags); michael@0: #elif defined(_M_IX86) michael@0: // A 64-bit flags attribute is illegal on 32-bit Win 7 and below. michael@0: if (version < base::win::VERSION_WIN8) michael@0: *size = sizeof(DWORD); michael@0: else michael@0: *size = sizeof(*policy_flags); michael@0: #else michael@0: #error This platform is not supported. michael@0: #endif michael@0: michael@0: // Nothing for Win XP or Vista. michael@0: if (version <= base::win::VERSION_VISTA) michael@0: return; michael@0: michael@0: // DEP and SEHOP are not valid for 64-bit Windows michael@0: #if !defined(_WIN64) michael@0: if (flags & MITIGATION_DEP) { michael@0: *policy_flags |= PROCESS_CREATION_MITIGATION_POLICY_DEP_ENABLE; michael@0: if (!(flags & MITIGATION_DEP_NO_ATL_THUNK)) michael@0: *policy_flags |= PROCESS_CREATION_MITIGATION_POLICY_DEP_ATL_THUNK_ENABLE; michael@0: } michael@0: michael@0: if (flags & MITIGATION_SEHOP) michael@0: *policy_flags |= PROCESS_CREATION_MITIGATION_POLICY_SEHOP_ENABLE; michael@0: #endif michael@0: michael@0: // Win 7 michael@0: if (version < base::win::VERSION_WIN8) michael@0: return; michael@0: michael@0: if (flags & MITIGATION_RELOCATE_IMAGE) { michael@0: *policy_flags |= michael@0: PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON; michael@0: if (flags & MITIGATION_RELOCATE_IMAGE_REQUIRED) { michael@0: *policy_flags |= michael@0: PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON_REQ_RELOCS; michael@0: } michael@0: } michael@0: michael@0: if (flags & MITIGATION_HEAP_TERMINATE) { michael@0: *policy_flags |= michael@0: PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON; michael@0: } michael@0: michael@0: if (flags & MITIGATION_BOTTOM_UP_ASLR) { michael@0: *policy_flags |= michael@0: PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON; michael@0: } michael@0: michael@0: if (flags & MITIGATION_HIGH_ENTROPY_ASLR) { michael@0: *policy_flags |= michael@0: PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON; michael@0: } michael@0: michael@0: if (flags & MITIGATION_STRICT_HANDLE_CHECKS) { michael@0: *policy_flags |= michael@0: PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_ON; michael@0: } michael@0: michael@0: if (flags & MITIGATION_WIN32K_DISABLE) { michael@0: *policy_flags |= michael@0: PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_ON; michael@0: } michael@0: michael@0: if (flags & MITIGATION_EXTENSION_DLL_DISABLE) { michael@0: *policy_flags |= michael@0: PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON; michael@0: } michael@0: } michael@0: michael@0: MitigationFlags FilterPostStartupProcessMitigations(MitigationFlags flags) { michael@0: // Anything prior to XP SP2. michael@0: if (!IsXPSP2OrLater()) michael@0: return 0; michael@0: michael@0: base::win::Version version = base::win::GetVersion(); michael@0: michael@0: // Windows XP SP2+. michael@0: if (version < base::win::VERSION_VISTA) { michael@0: return flags & (MITIGATION_DEP | michael@0: MITIGATION_DEP_NO_ATL_THUNK); michael@0: michael@0: // Windows Vista michael@0: if (version < base::win::VERSION_WIN7) { michael@0: return flags & (MITIGATION_DEP | michael@0: MITIGATION_DEP_NO_ATL_THUNK | michael@0: MITIGATION_BOTTOM_UP_ASLR | michael@0: MITIGATION_DLL_SEARCH_ORDER | michael@0: MITIGATION_HEAP_TERMINATE); michael@0: } michael@0: michael@0: // Windows 7 and Vista. michael@0: } else if (version < base::win::VERSION_WIN8) { michael@0: return flags & (MITIGATION_BOTTOM_UP_ASLR | michael@0: MITIGATION_DLL_SEARCH_ORDER | michael@0: MITIGATION_HEAP_TERMINATE); michael@0: } michael@0: michael@0: // Windows 8 and above. michael@0: return flags & (MITIGATION_BOTTOM_UP_ASLR | michael@0: MITIGATION_DLL_SEARCH_ORDER); michael@0: } michael@0: michael@0: bool ApplyProcessMitigationsToSuspendedProcess(HANDLE process, michael@0: MitigationFlags flags) { michael@0: // This is a hack to fake a weak bottom-up ASLR on 32-bit Windows. michael@0: #if !defined(_WIN64) michael@0: if (flags & MITIGATION_BOTTOM_UP_ASLR) { michael@0: unsigned int limit; michael@0: rand_s(&limit); michael@0: char* ptr = 0; michael@0: const size_t kMask64k = 0xFFFF; michael@0: // Random range (512k-16.5mb) in 64k steps. michael@0: const char* end = ptr + ((((limit % 16384) + 512) * 1024) & ~kMask64k); michael@0: while (ptr < end) { michael@0: MEMORY_BASIC_INFORMATION memory_info; michael@0: if (!::VirtualQueryEx(process, ptr, &memory_info, sizeof(memory_info))) michael@0: break; michael@0: size_t size = std::min((memory_info.RegionSize + kMask64k) & ~kMask64k, michael@0: static_cast(end - ptr)); michael@0: if (ptr && memory_info.State == MEM_FREE) michael@0: ::VirtualAllocEx(process, ptr, size, MEM_RESERVE, PAGE_NOACCESS); michael@0: ptr += size; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool CanSetProcessMitigationsPostStartup(MitigationFlags flags) { michael@0: // All of these mitigations can be enabled after startup. michael@0: return !(flags & ~(MITIGATION_HEAP_TERMINATE | michael@0: MITIGATION_DEP | michael@0: MITIGATION_DEP_NO_ATL_THUNK | michael@0: MITIGATION_RELOCATE_IMAGE | michael@0: MITIGATION_RELOCATE_IMAGE_REQUIRED | michael@0: MITIGATION_BOTTOM_UP_ASLR | michael@0: MITIGATION_STRICT_HANDLE_CHECKS | michael@0: MITIGATION_WIN32K_DISABLE | michael@0: MITIGATION_EXTENSION_DLL_DISABLE | michael@0: MITIGATION_DLL_SEARCH_ORDER)); michael@0: } michael@0: michael@0: bool CanSetProcessMitigationsPreStartup(MitigationFlags flags) { michael@0: // These mitigations cannot be enabled prior to startup. michael@0: return !(flags & (MITIGATION_STRICT_HANDLE_CHECKS | michael@0: MITIGATION_WIN32K_DISABLE | michael@0: MITIGATION_DLL_SEARCH_ORDER)); michael@0: } michael@0: michael@0: } // namespace sandbox michael@0: