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