Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(141)

Side by Side Diff: sandbox/win/src/interception.cc

Issue 1851213002: Remove sandbox on Windows. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix nacl compile issues Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sandbox/win/src/interception.h ('k') | sandbox/win/src/interception_agent.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 // For information about interceptions as a whole see
6 // http://dev.chromium.org/developers/design-documents/sandbox .
7
8 #include <stddef.h>
9
10 #include <set>
11
12 #include "sandbox/win/src/interception.h"
13
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/strings/string16.h"
17 #include "base/win/pe_image.h"
18 #include "base/win/windows_version.h"
19 #include "sandbox/win/src/interception_internal.h"
20 #include "sandbox/win/src/interceptors.h"
21 #include "sandbox/win/src/sandbox.h"
22 #include "sandbox/win/src/sandbox_rand.h"
23 #include "sandbox/win/src/service_resolver.h"
24 #include "sandbox/win/src/target_interceptions.h"
25 #include "sandbox/win/src/target_process.h"
26
27 namespace sandbox {
28
29 namespace {
30
31 // Standard allocation granularity and page size for Windows.
32 const size_t kAllocGranularity = 65536;
33 const size_t kPageSize = 4096;
34
35 } // namespace
36
37 namespace internal {
38
39 // Find a random offset within 64k and aligned to ceil(log2(size)).
40 size_t GetGranularAlignedRandomOffset(size_t size) {
41 CHECK_LE(size, kAllocGranularity);
42 unsigned int offset;
43
44 do {
45 GetRandom(&offset);
46 offset &= (kAllocGranularity - 1);
47 } while (offset > (kAllocGranularity - size));
48
49 // Find an alignment between 64 and the page size (4096).
50 size_t align_size = kPageSize;
51 for (size_t new_size = align_size / 2; new_size >= size; new_size /= 2) {
52 align_size = new_size;
53 }
54 return offset & ~(align_size - 1);
55 }
56
57 } // namespace internal
58
59 SANDBOX_INTERCEPT SharedMemory* g_interceptions;
60
61 // Table of the unpatched functions that we intercept. Mapped from the parent.
62 SANDBOX_INTERCEPT OriginalFunctions g_originals = { NULL };
63
64 // Magic constant that identifies that this function is not to be patched.
65 const char kUnloadDLLDummyFunction[] = "@";
66
67 InterceptionManager::InterceptionData::InterceptionData() {
68 }
69
70 InterceptionManager::InterceptionData::~InterceptionData() {
71 }
72
73 InterceptionManager::InterceptionManager(TargetProcess* child_process,
74 bool relaxed)
75 : child_(child_process), names_used_(false), relaxed_(relaxed) {
76 child_->AddRef();
77 }
78 InterceptionManager::~InterceptionManager() {
79 child_->Release();
80 }
81
82 bool InterceptionManager::AddToPatchedFunctions(
83 const wchar_t* dll_name, const char* function_name,
84 InterceptionType interception_type, const void* replacement_code_address,
85 InterceptorId id) {
86 InterceptionData function;
87 function.type = interception_type;
88 function.id = id;
89 function.dll = dll_name;
90 function.function = function_name;
91 function.interceptor_address = replacement_code_address;
92
93 interceptions_.push_back(function);
94 return true;
95 }
96
97 bool InterceptionManager::AddToPatchedFunctions(
98 const wchar_t* dll_name, const char* function_name,
99 InterceptionType interception_type, const char* replacement_function_name,
100 InterceptorId id) {
101 InterceptionData function;
102 function.type = interception_type;
103 function.id = id;
104 function.dll = dll_name;
105 function.function = function_name;
106 function.interceptor = replacement_function_name;
107 function.interceptor_address = NULL;
108
109 interceptions_.push_back(function);
110 names_used_ = true;
111 return true;
112 }
113
114 bool InterceptionManager::AddToUnloadModules(const wchar_t* dll_name) {
115 InterceptionData module_to_unload;
116 module_to_unload.type = INTERCEPTION_UNLOAD_MODULE;
117 module_to_unload.dll = dll_name;
118 // The next two are dummy values that make the structures regular, instead
119 // of having special cases. They should not be used.
120 module_to_unload.function = kUnloadDLLDummyFunction;
121 module_to_unload.interceptor_address = reinterpret_cast<void*>(1);
122
123 interceptions_.push_back(module_to_unload);
124 return true;
125 }
126
127 bool InterceptionManager::InitializeInterceptions() {
128 if (interceptions_.empty())
129 return true; // Nothing to do here
130
131 size_t buffer_bytes = GetBufferSize();
132 scoped_ptr<char[]> local_buffer(new char[buffer_bytes]);
133
134 if (!SetupConfigBuffer(local_buffer.get(), buffer_bytes))
135 return false;
136
137 void* remote_buffer;
138 if (!CopyDataToChild(local_buffer.get(), buffer_bytes, &remote_buffer))
139 return false;
140
141 bool hot_patch_needed = (0 != buffer_bytes);
142 if (!PatchNtdll(hot_patch_needed))
143 return false;
144
145 g_interceptions = reinterpret_cast<SharedMemory*>(remote_buffer);
146 ResultCode rc = child_->TransferVariable("g_interceptions",
147 &g_interceptions,
148 sizeof(g_interceptions));
149 return (SBOX_ALL_OK == rc);
150 }
151
152 size_t InterceptionManager::GetBufferSize() const {
153 std::set<base::string16> dlls;
154 size_t buffer_bytes = 0;
155
156 std::list<InterceptionData>::const_iterator it = interceptions_.begin();
157 for (; it != interceptions_.end(); ++it) {
158 // skip interceptions that are performed from the parent
159 if (!IsInterceptionPerformedByChild(*it))
160 continue;
161
162 if (!dlls.count(it->dll)) {
163 // NULL terminate the dll name on the structure
164 size_t dll_name_bytes = (it->dll.size() + 1) * sizeof(wchar_t);
165
166 // include the dll related size
167 buffer_bytes += RoundUpToMultiple(offsetof(DllPatchInfo, dll_name) +
168 dll_name_bytes, sizeof(size_t));
169 dlls.insert(it->dll);
170 }
171
172 // we have to NULL terminate the strings on the structure
173 size_t strings_chars = it->function.size() + it->interceptor.size() + 2;
174
175 // a new FunctionInfo is required per function
176 size_t record_bytes = offsetof(FunctionInfo, function) + strings_chars;
177 record_bytes = RoundUpToMultiple(record_bytes, sizeof(size_t));
178 buffer_bytes += record_bytes;
179 }
180
181 if (0 != buffer_bytes)
182 // add the part of SharedMemory that we have not counted yet
183 buffer_bytes += offsetof(SharedMemory, dll_list);
184
185 return buffer_bytes;
186 }
187
188 // Basically, walk the list of interceptions moving them to the config buffer,
189 // but keeping together all interceptions that belong to the same dll.
190 // The config buffer is a local buffer, not the one allocated on the child.
191 bool InterceptionManager::SetupConfigBuffer(void* buffer, size_t buffer_bytes) {
192 if (0 == buffer_bytes)
193 return true;
194
195 DCHECK(buffer_bytes > sizeof(SharedMemory));
196
197 SharedMemory* shared_memory = reinterpret_cast<SharedMemory*>(buffer);
198 DllPatchInfo* dll_info = shared_memory->dll_list;
199 int num_dlls = 0;
200
201 shared_memory->interceptor_base = names_used_ ? child_->MainModule() : NULL;
202
203 buffer_bytes -= offsetof(SharedMemory, dll_list);
204 buffer = dll_info;
205
206 std::list<InterceptionData>::iterator it = interceptions_.begin();
207 for (; it != interceptions_.end();) {
208 // skip interceptions that are performed from the parent
209 if (!IsInterceptionPerformedByChild(*it)) {
210 ++it;
211 continue;
212 }
213
214 const base::string16 dll = it->dll;
215 if (!SetupDllInfo(*it, &buffer, &buffer_bytes))
216 return false;
217
218 // walk the interceptions from this point, saving the ones that are
219 // performed on this dll, and removing the entry from the list.
220 // advance the iterator before removing the element from the list
221 std::list<InterceptionData>::iterator rest = it;
222 for (; rest != interceptions_.end();) {
223 if (rest->dll == dll) {
224 if (!SetupInterceptionInfo(*rest, &buffer, &buffer_bytes, dll_info))
225 return false;
226 if (it == rest)
227 ++it;
228 rest = interceptions_.erase(rest);
229 } else {
230 ++rest;
231 }
232 }
233 dll_info = reinterpret_cast<DllPatchInfo*>(buffer);
234 ++num_dlls;
235 }
236
237 shared_memory->num_intercepted_dlls = num_dlls;
238 return true;
239 }
240
241 // Fills up just the part that depends on the dll, not the info that depends on
242 // the actual interception.
243 bool InterceptionManager::SetupDllInfo(const InterceptionData& data,
244 void** buffer,
245 size_t* buffer_bytes) const {
246 DCHECK(buffer_bytes);
247 DCHECK(buffer);
248 DCHECK(*buffer);
249
250 DllPatchInfo* dll_info = reinterpret_cast<DllPatchInfo*>(*buffer);
251
252 // the strings have to be zero terminated
253 size_t required = offsetof(DllPatchInfo, dll_name) +
254 (data.dll.size() + 1) * sizeof(wchar_t);
255 required = RoundUpToMultiple(required, sizeof(size_t));
256 if (*buffer_bytes < required)
257 return false;
258
259 *buffer_bytes -= required;
260 *buffer = reinterpret_cast<char*>(*buffer) + required;
261
262 // set up the dll info to be what we know about it at this time
263 dll_info->unload_module = (data.type == INTERCEPTION_UNLOAD_MODULE);
264 dll_info->record_bytes = required;
265 dll_info->offset_to_functions = required;
266 dll_info->num_functions = 0;
267 data.dll._Copy_s(dll_info->dll_name, data.dll.size(), data.dll.size());
268 dll_info->dll_name[data.dll.size()] = L'\0';
269
270 return true;
271 }
272
273 bool InterceptionManager::SetupInterceptionInfo(const InterceptionData& data,
274 void** buffer,
275 size_t* buffer_bytes,
276 DllPatchInfo* dll_info) const {
277 DCHECK(buffer_bytes);
278 DCHECK(buffer);
279 DCHECK(*buffer);
280
281 if ((dll_info->unload_module) &&
282 (data.function != kUnloadDLLDummyFunction)) {
283 // Can't specify a dll for both patch and unload.
284 NOTREACHED();
285 }
286
287 FunctionInfo* function = reinterpret_cast<FunctionInfo*>(*buffer);
288
289 size_t name_bytes = data.function.size();
290 size_t interceptor_bytes = data.interceptor.size();
291
292 // the strings at the end of the structure are zero terminated
293 size_t required = offsetof(FunctionInfo, function) +
294 name_bytes + interceptor_bytes + 2;
295 required = RoundUpToMultiple(required, sizeof(size_t));
296 if (*buffer_bytes < required)
297 return false;
298
299 // update the caller's values
300 *buffer_bytes -= required;
301 *buffer = reinterpret_cast<char*>(*buffer) + required;
302
303 function->record_bytes = required;
304 function->type = data.type;
305 function->id = data.id;
306 function->interceptor_address = data.interceptor_address;
307 char* names = function->function;
308
309 data.function._Copy_s(names, name_bytes, name_bytes);
310 names += name_bytes;
311 *names++ = '\0';
312
313 // interceptor follows the function_name
314 data.interceptor._Copy_s(names, interceptor_bytes, interceptor_bytes);
315 names += interceptor_bytes;
316 *names++ = '\0';
317
318 // update the dll table
319 dll_info->num_functions++;
320 dll_info->record_bytes += required;
321
322 return true;
323 }
324
325 bool InterceptionManager::CopyDataToChild(const void* local_buffer,
326 size_t buffer_bytes,
327 void** remote_buffer) const {
328 DCHECK(NULL != remote_buffer);
329 if (0 == buffer_bytes) {
330 *remote_buffer = NULL;
331 return true;
332 }
333
334 HANDLE child = child_->Process();
335
336 // Allocate memory on the target process without specifying the address
337 void* remote_data = ::VirtualAllocEx(child, NULL, buffer_bytes,
338 MEM_COMMIT, PAGE_READWRITE);
339 if (NULL == remote_data)
340 return false;
341
342 SIZE_T bytes_written;
343 BOOL success = ::WriteProcessMemory(child, remote_data, local_buffer,
344 buffer_bytes, &bytes_written);
345 if (FALSE == success || bytes_written != buffer_bytes) {
346 ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE);
347 return false;
348 }
349
350 *remote_buffer = remote_data;
351
352 return true;
353 }
354
355 // Only return true if the child should be able to perform this interception.
356 bool InterceptionManager::IsInterceptionPerformedByChild(
357 const InterceptionData& data) const {
358 if (INTERCEPTION_INVALID == data.type)
359 return false;
360
361 if (INTERCEPTION_SERVICE_CALL == data.type)
362 return false;
363
364 if (data.type >= INTERCEPTION_LAST)
365 return false;
366
367 base::string16 ntdll(kNtdllName);
368 if (ntdll == data.dll)
369 return false; // ntdll has to be intercepted from the parent
370
371 return true;
372 }
373
374 bool InterceptionManager::PatchNtdll(bool hot_patch_needed) {
375 // Maybe there is nothing to do
376 if (!hot_patch_needed && interceptions_.empty())
377 return true;
378
379 if (hot_patch_needed) {
380 #if SANDBOX_EXPORTS
381 // Make sure the functions are not excluded by the linker.
382 #if defined(_WIN64)
383 #pragma comment(linker, "/include:TargetNtMapViewOfSection64")
384 #pragma comment(linker, "/include:TargetNtUnmapViewOfSection64")
385 #else
386 #pragma comment(linker, "/include:_TargetNtMapViewOfSection@44")
387 #pragma comment(linker, "/include:_TargetNtUnmapViewOfSection@12")
388 #endif
389 #endif
390 ADD_NT_INTERCEPTION(NtMapViewOfSection, MAP_VIEW_OF_SECTION_ID, 44);
391 ADD_NT_INTERCEPTION(NtUnmapViewOfSection, UNMAP_VIEW_OF_SECTION_ID, 12);
392 }
393
394 // Reserve a full 64k memory range in the child process.
395 HANDLE child = child_->Process();
396 BYTE* thunk_base = reinterpret_cast<BYTE*>(
397 ::VirtualAllocEx(child, NULL, kAllocGranularity,
398 MEM_RESERVE, PAGE_NOACCESS));
399
400 // Find an aligned, random location within the reserved range.
401 size_t thunk_bytes = interceptions_.size() * sizeof(ThunkData) +
402 sizeof(DllInterceptionData);
403 size_t thunk_offset = internal::GetGranularAlignedRandomOffset(thunk_bytes);
404
405 // Split the base and offset along page boundaries.
406 thunk_base += thunk_offset & ~(kPageSize - 1);
407 thunk_offset &= kPageSize - 1;
408
409 // Make an aligned, padded allocation, and move the pointer to our chunk.
410 size_t thunk_bytes_padded = (thunk_bytes + kPageSize - 1) & ~(kPageSize - 1);
411 thunk_base = reinterpret_cast<BYTE*>(
412 ::VirtualAllocEx(child, thunk_base, thunk_bytes_padded,
413 MEM_COMMIT, PAGE_EXECUTE_READWRITE));
414 CHECK(thunk_base); // If this fails we'd crash anyway on an invalid access.
415 DllInterceptionData* thunks = reinterpret_cast<DllInterceptionData*>(
416 thunk_base + thunk_offset);
417
418 DllInterceptionData dll_data;
419 dll_data.data_bytes = thunk_bytes;
420 dll_data.num_thunks = 0;
421 dll_data.used_bytes = offsetof(DllInterceptionData, thunks);
422
423 // Reset all helpers for a new child.
424 memset(g_originals, 0, sizeof(g_originals));
425
426 // this should write all the individual thunks to the child's memory
427 if (!PatchClientFunctions(thunks, thunk_bytes, &dll_data))
428 return false;
429
430 // and now write the first part of the table to the child's memory
431 SIZE_T written;
432 bool ok = FALSE != ::WriteProcessMemory(child, thunks, &dll_data,
433 offsetof(DllInterceptionData, thunks),
434 &written);
435
436 if (!ok || (offsetof(DllInterceptionData, thunks) != written))
437 return false;
438
439 // Attempt to protect all the thunks, but ignore failure
440 DWORD old_protection;
441 ::VirtualProtectEx(child, thunks, thunk_bytes,
442 PAGE_EXECUTE_READ, &old_protection);
443
444 ResultCode ret = child_->TransferVariable("g_originals", g_originals,
445 sizeof(g_originals));
446 return (SBOX_ALL_OK == ret);
447 }
448
449 bool InterceptionManager::PatchClientFunctions(DllInterceptionData* thunks,
450 size_t thunk_bytes,
451 DllInterceptionData* dll_data) {
452 DCHECK(NULL != thunks);
453 DCHECK(NULL != dll_data);
454
455 HMODULE ntdll_base = ::GetModuleHandle(kNtdllName);
456 if (!ntdll_base)
457 return false;
458
459 base::win::PEImage ntdll_image(ntdll_base);
460
461 // Bypass purify's interception.
462 wchar_t* loader_get = reinterpret_cast<wchar_t*>(
463 ntdll_image.GetProcAddress("LdrGetDllHandle"));
464 if (loader_get) {
465 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
466 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
467 loader_get, &ntdll_base))
468 return false;
469 }
470
471 char* interceptor_base = NULL;
472
473 #if SANDBOX_EXPORTS
474 interceptor_base = reinterpret_cast<char*>(child_->MainModule());
475 HMODULE local_interceptor = ::LoadLibrary(child_->Name());
476 #endif
477
478 ServiceResolverThunk* thunk;
479 #if defined(_WIN64)
480 thunk = new ServiceResolverThunk(child_->Process(), relaxed_);
481 #else
482 base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
483 if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) {
484 if (os_info->version() >= base::win::VERSION_WIN10)
485 thunk = new Wow64W10ResolverThunk(child_->Process(), relaxed_);
486 else if (os_info->version() >= base::win::VERSION_WIN8)
487 thunk = new Wow64W8ResolverThunk(child_->Process(), relaxed_);
488 else
489 thunk = new Wow64ResolverThunk(child_->Process(), relaxed_);
490 } else if (os_info->version() >= base::win::VERSION_WIN8) {
491 thunk = new Win8ResolverThunk(child_->Process(), relaxed_);
492 } else {
493 thunk = new ServiceResolverThunk(child_->Process(), relaxed_);
494 }
495 #endif
496
497 std::list<InterceptionData>::iterator it = interceptions_.begin();
498 for (; it != interceptions_.end(); ++it) {
499 const base::string16 ntdll(kNtdllName);
500 if (it->dll != ntdll)
501 break;
502
503 if (INTERCEPTION_SERVICE_CALL != it->type)
504 break;
505
506 #if SANDBOX_EXPORTS
507 // We may be trying to patch by function name.
508 if (NULL == it->interceptor_address) {
509 const char* address;
510 NTSTATUS ret = thunk->ResolveInterceptor(local_interceptor,
511 it->interceptor.c_str(),
512 reinterpret_cast<const void**>(
513 &address));
514 if (!NT_SUCCESS(ret))
515 break;
516
517 // Translate the local address to an address on the child.
518 it->interceptor_address = interceptor_base + (address -
519 reinterpret_cast<char*>(local_interceptor));
520 }
521 #endif
522 NTSTATUS ret = thunk->Setup(ntdll_base,
523 interceptor_base,
524 it->function.c_str(),
525 it->interceptor.c_str(),
526 it->interceptor_address,
527 &thunks->thunks[dll_data->num_thunks],
528 thunk_bytes - dll_data->used_bytes,
529 NULL);
530 if (!NT_SUCCESS(ret))
531 break;
532
533 DCHECK(!g_originals[it->id]);
534 g_originals[it->id] = &thunks->thunks[dll_data->num_thunks];
535
536 dll_data->num_thunks++;
537 dll_data->used_bytes += sizeof(ThunkData);
538 }
539
540 delete(thunk);
541
542 #if SANDBOX_EXPORTS
543 if (NULL != local_interceptor)
544 ::FreeLibrary(local_interceptor);
545 #endif
546
547 if (it != interceptions_.end())
548 return false;
549
550 return true;
551 }
552
553 } // namespace sandbox
OLDNEW
« no previous file with comments | « sandbox/win/src/interception.h ('k') | sandbox/win/src/interception_agent.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698