Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "sandbox/win/src/process_mitigations.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/win/windows_version.h"
     10 #include "sandbox/win/src/nt_internals.h"
     11 #include "sandbox/win/src/win_utils.h"
     12 
     13 namespace {
     14 
     15 // Functions for enabling policies.
     16 typedef BOOL (WINAPI *SetProcessDEPPolicyFunction)(DWORD dwFlags);
     17 
     18 typedef BOOL (WINAPI *SetProcessMitigationPolicyFunction)(
     19     PROCESS_MITIGATION_POLICY mitigation_policy,
     20     PVOID buffer,
     21     SIZE_T length);
     22 
     23 typedef BOOL (WINAPI *SetDefaultDllDirectoriesFunction)(
     24     DWORD DirectoryFlags);
     25 
     26 }  // namespace
     27 
     28 namespace sandbox {
     29 
     30 bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags) {
     31   if (!CanSetProcessMitigationsPostStartup(flags))
     32     return false;
     33 
     34   base::win::Version version = base::win::GetVersion();
     35   HMODULE module = ::GetModuleHandleA("kernel32.dll");
     36 
     37   if (version >= base::win::VERSION_VISTA &&
     38       (flags & MITIGATION_DLL_SEARCH_ORDER)) {
     39     SetDefaultDllDirectoriesFunction set_default_dll_directories =
     40         reinterpret_cast<SetDefaultDllDirectoriesFunction>(
     41             ::GetProcAddress(module, "SetDefaultDllDirectories"));
     42 
     43     // Check for SetDefaultDllDirectories since it requires KB2533623.
     44     if (set_default_dll_directories) {
     45       if (!set_default_dll_directories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) &&
     46           ERROR_ACCESS_DENIED != ::GetLastError()) {
     47         return false;
     48       }
     49     }
     50   }
     51 
     52   // Set the heap to terminate on corruption
     53   if (version >= base::win::VERSION_VISTA &&
     54       (flags & MITIGATION_HEAP_TERMINATE)) {
     55     if (!::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption,
     56                               NULL, 0) &&
     57         ERROR_ACCESS_DENIED != ::GetLastError()) {
     58       return false;
     59     }
     60   }
     61 
     62 #if !defined(_WIN64)  // DEP is always enabled on 64-bit.
     63   if (flags & MITIGATION_DEP) {
     64     DWORD dep_flags = PROCESS_DEP_ENABLE;
     65     // DEP support is quirky on XP, so don't force a failure in that case.
     66     const bool return_on_fail = version >= base::win::VERSION_VISTA;
     67 
     68     if (flags & MITIGATION_DEP_NO_ATL_THUNK)
     69       dep_flags |= PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION;
     70 
     71     SetProcessDEPPolicyFunction set_process_dep_policy =
     72         reinterpret_cast<SetProcessDEPPolicyFunction>(
     73             ::GetProcAddress(module, "SetProcessDEPPolicy"));
     74     if (set_process_dep_policy) {
     75       if (!set_process_dep_policy(dep_flags) &&
     76           ERROR_ACCESS_DENIED != ::GetLastError() && return_on_fail) {
     77         return false;
     78       }
     79     } else {
     80       // We're on XP sp2, so use the less standard approach.
     81       // For reference: http://www.uninformed.org/?v=2&a=4
     82       const int MEM_EXECUTE_OPTION_ENABLE = 1;
     83       const int MEM_EXECUTE_OPTION_DISABLE = 2;
     84       const int MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION = 4;
     85       const int MEM_EXECUTE_OPTION_PERMANENT = 8;
     86 
     87       NtSetInformationProcessFunction set_information_process = NULL;
     88       ResolveNTFunctionPtr("NtSetInformationProcess",
     89                            &set_information_process);
     90       if (!set_information_process)
     91         return false;
     92       ULONG dep = MEM_EXECUTE_OPTION_DISABLE | MEM_EXECUTE_OPTION_PERMANENT;
     93       if (!(dep_flags & PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION))
     94         dep |= MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION;
     95       if (!SUCCEEDED(set_information_process(GetCurrentProcess(),
     96                                              ProcessExecuteFlags,
     97                                              &dep, sizeof(dep))) &&
     98           ERROR_ACCESS_DENIED != ::GetLastError() && return_on_fail) {
     99         return false;
    100       }
    101     }
    102   }
    103 #endif
    104 
    105   // This is all we can do in Win7 and below.
    106   if (version < base::win::VERSION_WIN8)
    107     return true;
    108 
    109   SetProcessMitigationPolicyFunction set_process_mitigation_policy =
    110       reinterpret_cast<SetProcessMitigationPolicyFunction>(
    111           ::GetProcAddress(module, "SetProcessMitigationPolicy"));
    112   if (!set_process_mitigation_policy)
    113     return false;
    114 
    115   // Enable ASLR policies.
    116   if (flags & MITIGATION_RELOCATE_IMAGE) {
    117     PROCESS_MITIGATION_ASLR_POLICY policy = { 0 };
    118     policy.EnableForceRelocateImages = true;
    119     policy.DisallowStrippedImages = (flags &
    120         MITIGATION_RELOCATE_IMAGE_REQUIRED) ==
    121         MITIGATION_RELOCATE_IMAGE_REQUIRED;
    122 
    123     if (!set_process_mitigation_policy(ProcessASLRPolicy, &policy,
    124                                        sizeof(policy)) &&
    125         ERROR_ACCESS_DENIED != ::GetLastError()) {
    126       return false;
    127     }
    128   }
    129 
    130   // Enable strict handle policies.
    131   if (flags & MITIGATION_STRICT_HANDLE_CHECKS) {
    132     PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY policy = { 0 };
    133     policy.HandleExceptionsPermanentlyEnabled =
    134         policy.RaiseExceptionOnInvalidHandleReference = true;
    135 
    136     if (!set_process_mitigation_policy(ProcessStrictHandleCheckPolicy, &policy,
    137                                        sizeof(policy)) &&
    138         ERROR_ACCESS_DENIED != ::GetLastError()) {
    139       return false;
    140     }
    141   }
    142 
    143   // Enable system call policies.
    144   if (flags & MITIGATION_WIN32K_DISABLE) {
    145     PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = { 0 };
    146     policy.DisallowWin32kSystemCalls = true;
    147 
    148     if (!set_process_mitigation_policy(ProcessSystemCallDisablePolicy, &policy,
    149                                        sizeof(policy)) &&
    150         ERROR_ACCESS_DENIED != ::GetLastError()) {
    151       return false;
    152     }
    153   }
    154 
    155   // Enable system call policies.
    156   if (flags & MITIGATION_EXTENSION_DLL_DISABLE) {
    157     PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY policy = { 0 };
    158     policy.DisableExtensionPoints = true;
    159 
    160     if (!set_process_mitigation_policy(ProcessExtensionPointDisablePolicy,
    161                                        &policy, sizeof(policy)) &&
    162         ERROR_ACCESS_DENIED != ::GetLastError()) {
    163       return false;
    164     }
    165   }
    166 
    167   return true;
    168 }
    169 
    170 void ConvertProcessMitigationsToPolicy(MitigationFlags flags,
    171                                        DWORD64* policy_flags, size_t* size) {
    172   base::win::Version version = base::win::GetVersion();
    173 
    174   *policy_flags = 0;
    175 #if defined(_WIN64)
    176   *size = sizeof(*policy_flags);
    177 #elif defined(_M_IX86)
    178   // A 64-bit flags attribute is illegal on 32-bit Win 7 and below.
    179   if (version < base::win::VERSION_WIN8)
    180     *size = sizeof(DWORD);
    181   else
    182     *size = sizeof(*policy_flags);
    183 #else
    184 #error This platform is not supported.
    185 #endif
    186 
    187   // Nothing for Win XP or Vista.
    188   if (version <= base::win::VERSION_VISTA)
    189     return;
    190 
    191   // DEP and SEHOP are not valid for 64-bit Windows
    192 #if !defined(_WIN64)
    193   if (flags & MITIGATION_DEP) {
    194     *policy_flags |= PROCESS_CREATION_MITIGATION_POLICY_DEP_ENABLE;
    195     if (!(flags & MITIGATION_DEP_NO_ATL_THUNK))
    196       *policy_flags |= PROCESS_CREATION_MITIGATION_POLICY_DEP_ATL_THUNK_ENABLE;
    197   }
    198 
    199   if (flags & MITIGATION_SEHOP)
    200     *policy_flags |= PROCESS_CREATION_MITIGATION_POLICY_SEHOP_ENABLE;
    201 #endif
    202 
    203   // Win 7
    204   if (version < base::win::VERSION_WIN8)
    205     return;
    206 
    207   if (flags & MITIGATION_RELOCATE_IMAGE) {
    208     *policy_flags |=
    209         PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON;
    210     if (flags & MITIGATION_RELOCATE_IMAGE_REQUIRED) {
    211       *policy_flags |=
    212           PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON_REQ_RELOCS;
    213     }
    214   }
    215 
    216   if (flags & MITIGATION_HEAP_TERMINATE) {
    217     *policy_flags |=
    218         PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON;
    219   }
    220 
    221   if (flags & MITIGATION_BOTTOM_UP_ASLR) {
    222     *policy_flags |=
    223         PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON;
    224   }
    225 
    226   if (flags & MITIGATION_HIGH_ENTROPY_ASLR) {
    227     *policy_flags |=
    228         PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON;
    229   }
    230 
    231   if (flags & MITIGATION_STRICT_HANDLE_CHECKS) {
    232     *policy_flags |=
    233         PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_ON;
    234   }
    235 
    236   if (flags & MITIGATION_WIN32K_DISABLE) {
    237     *policy_flags |=
    238         PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_ON;
    239   }
    240 
    241   if (flags & MITIGATION_EXTENSION_DLL_DISABLE) {
    242     *policy_flags |=
    243         PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON;
    244   }
    245 }
    246 
    247 MitigationFlags FilterPostStartupProcessMitigations(MitigationFlags flags) {
    248   base::win::Version version = base::win::GetVersion();
    249 
    250   // Windows XP SP2+.
    251   if (version < base::win::VERSION_VISTA) {
    252     return flags & (MITIGATION_DEP |
    253                     MITIGATION_DEP_NO_ATL_THUNK);
    254   }
    255 
    256   // Windows Vista
    257   if (version < base::win::VERSION_WIN7) {
    258     return flags & (MITIGATION_BOTTOM_UP_ASLR |
    259                     MITIGATION_DLL_SEARCH_ORDER |
    260                     MITIGATION_HEAP_TERMINATE);
    261   }
    262 
    263   // Windows 7.
    264   if (version < base::win::VERSION_WIN8) {
    265     return flags & (MITIGATION_BOTTOM_UP_ASLR |
    266                     MITIGATION_DLL_SEARCH_ORDER |
    267                     MITIGATION_HEAP_TERMINATE);
    268   }
    269 
    270   // Windows 8 and above.
    271   return flags & (MITIGATION_BOTTOM_UP_ASLR |
    272                   MITIGATION_DLL_SEARCH_ORDER);
    273 }
    274 
    275 bool ApplyProcessMitigationsToSuspendedProcess(HANDLE process,
    276                                                MitigationFlags flags) {
    277 // This is a hack to fake a weak bottom-up ASLR on 32-bit Windows.
    278 #if !defined(_WIN64)
    279   if (flags & MITIGATION_BOTTOM_UP_ASLR) {
    280     unsigned int limit;
    281     rand_s(&limit);
    282     char* ptr = 0;
    283     const size_t kMask64k = 0xFFFF;
    284     // Random range (512k-16.5mb) in 64k steps.
    285     const char* end = ptr + ((((limit % 16384) + 512) * 1024) & ~kMask64k);
    286     while (ptr < end) {
    287       MEMORY_BASIC_INFORMATION memory_info;
    288       if (!::VirtualQueryEx(process, ptr, &memory_info, sizeof(memory_info)))
    289         break;
    290       size_t size = std::min((memory_info.RegionSize + kMask64k) & ~kMask64k,
    291                              static_cast<SIZE_T>(end - ptr));
    292       if (ptr && memory_info.State == MEM_FREE)
    293         ::VirtualAllocEx(process, ptr, size, MEM_RESERVE, PAGE_NOACCESS);
    294       ptr += size;
    295     }
    296   }
    297 #endif
    298 
    299   return true;
    300 }
    301 
    302 bool CanSetProcessMitigationsPostStartup(MitigationFlags flags) {
    303   // All of these mitigations can be enabled after startup.
    304   return !(flags & ~(MITIGATION_HEAP_TERMINATE |
    305                      MITIGATION_DEP |
    306                      MITIGATION_DEP_NO_ATL_THUNK |
    307                      MITIGATION_RELOCATE_IMAGE |
    308                      MITIGATION_RELOCATE_IMAGE_REQUIRED |
    309                      MITIGATION_BOTTOM_UP_ASLR |
    310                      MITIGATION_STRICT_HANDLE_CHECKS |
    311                      MITIGATION_EXTENSION_DLL_DISABLE |
    312                      MITIGATION_DLL_SEARCH_ORDER));
    313 }
    314 
    315 bool CanSetProcessMitigationsPreStartup(MitigationFlags flags) {
    316   // These mitigations cannot be enabled prior to startup.
    317   return !(flags & (MITIGATION_STRICT_HANDLE_CHECKS |
    318                     MITIGATION_DLL_SEARCH_ORDER));
    319 }
    320 
    321 }  // namespace sandbox
    322 
    323