Add a stub __cxa_demangle to disable LLVM's demangler.
[chromium-blink-merge.git] / sandbox / win / src / interception.cc
blob60dd4400b8cfed24b08e971abef1c220d08678df
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.
5 // For information about interceptions as a whole see
6 // http://dev.chromium.org/developers/design-documents/sandbox .
8 #include <set>
10 #include "sandbox/win/src/interception.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/strings/string16.h"
15 #include "base/win/pe_image.h"
16 #include "base/win/windows_version.h"
17 #include "sandbox/win/src/interception_internal.h"
18 #include "sandbox/win/src/interceptors.h"
19 #include "sandbox/win/src/sandbox.h"
20 #include "sandbox/win/src/service_resolver.h"
21 #include "sandbox/win/src/target_interceptions.h"
22 #include "sandbox/win/src/target_process.h"
23 #include "sandbox/win/src/wow64.h"
25 namespace {
27 const char kMapViewOfSectionName[] = "NtMapViewOfSection";
28 const char kUnmapViewOfSectionName[] = "NtUnmapViewOfSection";
30 // Standard allocation granularity and page size for Windows.
31 const size_t kAllocGranularity = 65536;
32 const size_t kPageSize = 4096;
34 // Find a random offset within 64k and aligned to ceil(log2(size)).
35 size_t GetGranularAlignedRandomOffset(size_t size) {
36 CHECK_LE(size, kAllocGranularity);
37 unsigned int offset;
39 do {
40 rand_s(&offset);
41 offset &= (kAllocGranularity - 1);
42 } while (offset > (kAllocGranularity - size));
44 // Find an alignment between 64 and the page size (4096).
45 size_t align_size = kPageSize;
46 for (size_t new_size = align_size / 2; new_size >= size; new_size /= 2) {
47 align_size = new_size;
49 return offset & ~(align_size - 1);
52 } // namespace
54 namespace sandbox {
56 SANDBOX_INTERCEPT SharedMemory* g_interceptions;
58 // Table of the unpatched functions that we intercept. Mapped from the parent.
59 SANDBOX_INTERCEPT OriginalFunctions g_originals = { NULL };
61 // Magic constant that identifies that this function is not to be patched.
62 const char kUnloadDLLDummyFunction[] = "@";
64 InterceptionManager::InterceptionData::InterceptionData() {
67 InterceptionManager::InterceptionData::~InterceptionData() {
70 InterceptionManager::InterceptionManager(TargetProcess* child_process,
71 bool relaxed)
72 : child_(child_process), names_used_(false), relaxed_(relaxed) {
73 child_->AddRef();
75 InterceptionManager::~InterceptionManager() {
76 child_->Release();
79 bool InterceptionManager::AddToPatchedFunctions(
80 const wchar_t* dll_name, const char* function_name,
81 InterceptionType interception_type, const void* replacement_code_address,
82 InterceptorId id) {
83 InterceptionData function;
84 function.type = interception_type;
85 function.id = id;
86 function.dll = dll_name;
87 function.function = function_name;
88 function.interceptor_address = replacement_code_address;
90 interceptions_.push_back(function);
91 return true;
94 bool InterceptionManager::AddToPatchedFunctions(
95 const wchar_t* dll_name, const char* function_name,
96 InterceptionType interception_type, const char* replacement_function_name,
97 InterceptorId id) {
98 InterceptionData function;
99 function.type = interception_type;
100 function.id = id;
101 function.dll = dll_name;
102 function.function = function_name;
103 function.interceptor = replacement_function_name;
104 function.interceptor_address = NULL;
106 interceptions_.push_back(function);
107 names_used_ = true;
108 return true;
111 bool InterceptionManager::AddToUnloadModules(const wchar_t* dll_name) {
112 InterceptionData module_to_unload;
113 module_to_unload.type = INTERCEPTION_UNLOAD_MODULE;
114 module_to_unload.dll = dll_name;
115 // The next two are dummy values that make the structures regular, instead
116 // of having special cases. They should not be used.
117 module_to_unload.function = kUnloadDLLDummyFunction;
118 module_to_unload.interceptor_address = reinterpret_cast<void*>(1);
120 interceptions_.push_back(module_to_unload);
121 return true;
124 bool InterceptionManager::InitializeInterceptions() {
125 if (interceptions_.empty())
126 return true; // Nothing to do here
128 size_t buffer_bytes = GetBufferSize();
129 scoped_ptr<char[]> local_buffer(new char[buffer_bytes]);
131 if (!SetupConfigBuffer(local_buffer.get(), buffer_bytes))
132 return false;
134 void* remote_buffer;
135 if (!CopyDataToChild(local_buffer.get(), buffer_bytes, &remote_buffer))
136 return false;
138 bool hot_patch_needed = (0 != buffer_bytes);
139 if (!PatchNtdll(hot_patch_needed))
140 return false;
142 g_interceptions = reinterpret_cast<SharedMemory*>(remote_buffer);
143 ResultCode rc = child_->TransferVariable("g_interceptions",
144 &g_interceptions,
145 sizeof(g_interceptions));
146 return (SBOX_ALL_OK == rc);
149 size_t InterceptionManager::GetBufferSize() const {
150 std::set<base::string16> dlls;
151 size_t buffer_bytes = 0;
153 std::list<InterceptionData>::const_iterator it = interceptions_.begin();
154 for (; it != interceptions_.end(); ++it) {
155 // skip interceptions that are performed from the parent
156 if (!IsInterceptionPerformedByChild(*it))
157 continue;
159 if (!dlls.count(it->dll)) {
160 // NULL terminate the dll name on the structure
161 size_t dll_name_bytes = (it->dll.size() + 1) * sizeof(wchar_t);
163 // include the dll related size
164 buffer_bytes += RoundUpToMultiple(offsetof(DllPatchInfo, dll_name) +
165 dll_name_bytes, sizeof(size_t));
166 dlls.insert(it->dll);
169 // we have to NULL terminate the strings on the structure
170 size_t strings_chars = it->function.size() + it->interceptor.size() + 2;
172 // a new FunctionInfo is required per function
173 size_t record_bytes = offsetof(FunctionInfo, function) + strings_chars;
174 record_bytes = RoundUpToMultiple(record_bytes, sizeof(size_t));
175 buffer_bytes += record_bytes;
178 if (0 != buffer_bytes)
179 // add the part of SharedMemory that we have not counted yet
180 buffer_bytes += offsetof(SharedMemory, dll_list);
182 return buffer_bytes;
185 // Basically, walk the list of interceptions moving them to the config buffer,
186 // but keeping together all interceptions that belong to the same dll.
187 // The config buffer is a local buffer, not the one allocated on the child.
188 bool InterceptionManager::SetupConfigBuffer(void* buffer, size_t buffer_bytes) {
189 if (0 == buffer_bytes)
190 return true;
192 DCHECK(buffer_bytes > sizeof(SharedMemory));
194 SharedMemory* shared_memory = reinterpret_cast<SharedMemory*>(buffer);
195 DllPatchInfo* dll_info = shared_memory->dll_list;
196 int num_dlls = 0;
198 shared_memory->interceptor_base = names_used_ ? child_->MainModule() : NULL;
200 buffer_bytes -= offsetof(SharedMemory, dll_list);
201 buffer = dll_info;
203 std::list<InterceptionData>::iterator it = interceptions_.begin();
204 for (; it != interceptions_.end();) {
205 // skip interceptions that are performed from the parent
206 if (!IsInterceptionPerformedByChild(*it)) {
207 ++it;
208 continue;
211 const base::string16 dll = it->dll;
212 if (!SetupDllInfo(*it, &buffer, &buffer_bytes))
213 return false;
215 // walk the interceptions from this point, saving the ones that are
216 // performed on this dll, and removing the entry from the list.
217 // advance the iterator before removing the element from the list
218 std::list<InterceptionData>::iterator rest = it;
219 for (; rest != interceptions_.end();) {
220 if (rest->dll == dll) {
221 if (!SetupInterceptionInfo(*rest, &buffer, &buffer_bytes, dll_info))
222 return false;
223 if (it == rest)
224 ++it;
225 rest = interceptions_.erase(rest);
226 } else {
227 ++rest;
230 dll_info = reinterpret_cast<DllPatchInfo*>(buffer);
231 ++num_dlls;
234 shared_memory->num_intercepted_dlls = num_dlls;
235 return true;
238 // Fills up just the part that depends on the dll, not the info that depends on
239 // the actual interception.
240 bool InterceptionManager::SetupDllInfo(const InterceptionData& data,
241 void** buffer,
242 size_t* buffer_bytes) const {
243 DCHECK(buffer_bytes);
244 DCHECK(buffer);
245 DCHECK(*buffer);
247 DllPatchInfo* dll_info = reinterpret_cast<DllPatchInfo*>(*buffer);
249 // the strings have to be zero terminated
250 size_t required = offsetof(DllPatchInfo, dll_name) +
251 (data.dll.size() + 1) * sizeof(wchar_t);
252 required = RoundUpToMultiple(required, sizeof(size_t));
253 if (*buffer_bytes < required)
254 return false;
256 *buffer_bytes -= required;
257 *buffer = reinterpret_cast<char*>(*buffer) + required;
259 // set up the dll info to be what we know about it at this time
260 dll_info->unload_module = (data.type == INTERCEPTION_UNLOAD_MODULE);
261 dll_info->record_bytes = required;
262 dll_info->offset_to_functions = required;
263 dll_info->num_functions = 0;
264 data.dll._Copy_s(dll_info->dll_name, data.dll.size(), data.dll.size());
265 dll_info->dll_name[data.dll.size()] = L'\0';
267 return true;
270 bool InterceptionManager::SetupInterceptionInfo(const InterceptionData& data,
271 void** buffer,
272 size_t* buffer_bytes,
273 DllPatchInfo* dll_info) const {
274 DCHECK(buffer_bytes);
275 DCHECK(buffer);
276 DCHECK(*buffer);
278 if ((dll_info->unload_module) &&
279 (data.function != kUnloadDLLDummyFunction)) {
280 // Can't specify a dll for both patch and unload.
281 NOTREACHED();
284 FunctionInfo* function = reinterpret_cast<FunctionInfo*>(*buffer);
286 size_t name_bytes = data.function.size();
287 size_t interceptor_bytes = data.interceptor.size();
289 // the strings at the end of the structure are zero terminated
290 size_t required = offsetof(FunctionInfo, function) +
291 name_bytes + interceptor_bytes + 2;
292 required = RoundUpToMultiple(required, sizeof(size_t));
293 if (*buffer_bytes < required)
294 return false;
296 // update the caller's values
297 *buffer_bytes -= required;
298 *buffer = reinterpret_cast<char*>(*buffer) + required;
300 function->record_bytes = required;
301 function->type = data.type;
302 function->id = data.id;
303 function->interceptor_address = data.interceptor_address;
304 char* names = function->function;
306 data.function._Copy_s(names, name_bytes, name_bytes);
307 names += name_bytes;
308 *names++ = '\0';
310 // interceptor follows the function_name
311 data.interceptor._Copy_s(names, interceptor_bytes, interceptor_bytes);
312 names += interceptor_bytes;
313 *names++ = '\0';
315 // update the dll table
316 dll_info->num_functions++;
317 dll_info->record_bytes += required;
319 return true;
322 bool InterceptionManager::CopyDataToChild(const void* local_buffer,
323 size_t buffer_bytes,
324 void** remote_buffer) const {
325 DCHECK(NULL != remote_buffer);
326 if (0 == buffer_bytes) {
327 *remote_buffer = NULL;
328 return true;
331 HANDLE child = child_->Process();
333 // Allocate memory on the target process without specifying the address
334 void* remote_data = ::VirtualAllocEx(child, NULL, buffer_bytes,
335 MEM_COMMIT, PAGE_READWRITE);
336 if (NULL == remote_data)
337 return false;
339 SIZE_T bytes_written;
340 BOOL success = ::WriteProcessMemory(child, remote_data, local_buffer,
341 buffer_bytes, &bytes_written);
342 if (FALSE == success || bytes_written != buffer_bytes) {
343 ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE);
344 return false;
347 *remote_buffer = remote_data;
349 return true;
352 // Only return true if the child should be able to perform this interception.
353 bool InterceptionManager::IsInterceptionPerformedByChild(
354 const InterceptionData& data) const {
355 if (INTERCEPTION_INVALID == data.type)
356 return false;
358 if (INTERCEPTION_SERVICE_CALL == data.type)
359 return false;
361 if (data.type >= INTERCEPTION_LAST)
362 return false;
364 base::string16 ntdll(kNtdllName);
365 if (ntdll == data.dll)
366 return false; // ntdll has to be intercepted from the parent
368 return true;
371 bool InterceptionManager::PatchNtdll(bool hot_patch_needed) {
372 // Maybe there is nothing to do
373 if (!hot_patch_needed && interceptions_.empty())
374 return true;
376 if (hot_patch_needed) {
377 #if SANDBOX_EXPORTS
378 // Make sure the functions are not excluded by the linker.
379 #if defined(_WIN64)
380 #pragma comment(linker, "/include:TargetNtMapViewOfSection64")
381 #pragma comment(linker, "/include:TargetNtUnmapViewOfSection64")
382 #else
383 #pragma comment(linker, "/include:_TargetNtMapViewOfSection@44")
384 #pragma comment(linker, "/include:_TargetNtUnmapViewOfSection@12")
385 #endif
386 #endif
387 ADD_NT_INTERCEPTION(NtMapViewOfSection, MAP_VIEW_OF_SECTION_ID, 44);
388 ADD_NT_INTERCEPTION(NtUnmapViewOfSection, UNMAP_VIEW_OF_SECTION_ID, 12);
391 // Reserve a full 64k memory range in the child process.
392 HANDLE child = child_->Process();
393 BYTE* thunk_base = reinterpret_cast<BYTE*>(
394 ::VirtualAllocEx(child, NULL, kAllocGranularity,
395 MEM_RESERVE, PAGE_NOACCESS));
397 // Find an aligned, random location within the reserved range.
398 size_t thunk_bytes = interceptions_.size() * sizeof(ThunkData) +
399 sizeof(DllInterceptionData);
400 size_t thunk_offset = GetGranularAlignedRandomOffset(thunk_bytes);
402 // Split the base and offset along page boundaries.
403 thunk_base += thunk_offset & ~(kPageSize - 1);
404 thunk_offset &= kPageSize - 1;
406 // Make an aligned, padded allocation, and move the pointer to our chunk.
407 size_t thunk_bytes_padded = (thunk_bytes + kPageSize - 1) & ~(kPageSize - 1);
408 thunk_base = reinterpret_cast<BYTE*>(
409 ::VirtualAllocEx(child, thunk_base, thunk_bytes_padded,
410 MEM_COMMIT, PAGE_EXECUTE_READWRITE));
411 CHECK(thunk_base); // If this fails we'd crash anyway on an invalid access.
412 DllInterceptionData* thunks = reinterpret_cast<DllInterceptionData*>(
413 thunk_base + thunk_offset);
415 DllInterceptionData dll_data;
416 dll_data.data_bytes = thunk_bytes;
417 dll_data.num_thunks = 0;
418 dll_data.used_bytes = offsetof(DllInterceptionData, thunks);
420 // Reset all helpers for a new child.
421 memset(g_originals, 0, sizeof(g_originals));
423 // this should write all the individual thunks to the child's memory
424 if (!PatchClientFunctions(thunks, thunk_bytes, &dll_data))
425 return false;
427 // and now write the first part of the table to the child's memory
428 SIZE_T written;
429 bool ok = FALSE != ::WriteProcessMemory(child, thunks, &dll_data,
430 offsetof(DllInterceptionData, thunks),
431 &written);
433 if (!ok || (offsetof(DllInterceptionData, thunks) != written))
434 return false;
436 // Attempt to protect all the thunks, but ignore failure
437 DWORD old_protection;
438 ::VirtualProtectEx(child, thunks, thunk_bytes,
439 PAGE_EXECUTE_READ, &old_protection);
441 ResultCode ret = child_->TransferVariable("g_originals", g_originals,
442 sizeof(g_originals));
443 return (SBOX_ALL_OK == ret);
446 bool InterceptionManager::PatchClientFunctions(DllInterceptionData* thunks,
447 size_t thunk_bytes,
448 DllInterceptionData* dll_data) {
449 DCHECK(NULL != thunks);
450 DCHECK(NULL != dll_data);
452 HMODULE ntdll_base = ::GetModuleHandle(kNtdllName);
453 if (!ntdll_base)
454 return false;
456 base::win::PEImage ntdll_image(ntdll_base);
458 // Bypass purify's interception.
459 wchar_t* loader_get = reinterpret_cast<wchar_t*>(
460 ntdll_image.GetProcAddress("LdrGetDllHandle"));
461 if (loader_get) {
462 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
463 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
464 loader_get, &ntdll_base))
465 return false;
468 if (base::win::GetVersion() <= base::win::VERSION_VISTA) {
469 Wow64 WowHelper(child_, ntdll_base);
470 if (!WowHelper.WaitForNtdll())
471 return false;
474 char* interceptor_base = NULL;
476 #if SANDBOX_EXPORTS
477 interceptor_base = reinterpret_cast<char*>(child_->MainModule());
478 HMODULE local_interceptor = ::LoadLibrary(child_->Name());
479 #endif
481 ServiceResolverThunk* thunk;
482 #if defined(_WIN64)
483 thunk = new ServiceResolverThunk(child_->Process(), relaxed_);
484 #else
485 base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
486 if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) {
487 if (os_info->version() >= base::win::VERSION_WIN8)
488 thunk = new Wow64W8ResolverThunk(child_->Process(), relaxed_);
489 else
490 thunk = new Wow64ResolverThunk(child_->Process(), relaxed_);
491 } else if (os_info->version() >= base::win::VERSION_WIN8) {
492 thunk = new Win8ResolverThunk(child_->Process(), relaxed_);
493 } else {
494 thunk = new ServiceResolverThunk(child_->Process(), relaxed_);
496 #endif
498 std::list<InterceptionData>::iterator it = interceptions_.begin();
499 for (; it != interceptions_.end(); ++it) {
500 const base::string16 ntdll(kNtdllName);
501 if (it->dll != ntdll)
502 break;
504 if (INTERCEPTION_SERVICE_CALL != it->type)
505 break;
507 #if SANDBOX_EXPORTS
508 // We may be trying to patch by function name.
509 if (NULL == it->interceptor_address) {
510 const char* address;
511 NTSTATUS ret = thunk->ResolveInterceptor(local_interceptor,
512 it->interceptor.c_str(),
513 reinterpret_cast<const void**>(
514 &address));
515 if (!NT_SUCCESS(ret))
516 break;
518 // Translate the local address to an address on the child.
519 it->interceptor_address = interceptor_base + (address -
520 reinterpret_cast<char*>(local_interceptor));
522 #endif
523 NTSTATUS ret = thunk->Setup(ntdll_base,
524 interceptor_base,
525 it->function.c_str(),
526 it->interceptor.c_str(),
527 it->interceptor_address,
528 &thunks->thunks[dll_data->num_thunks],
529 thunk_bytes - dll_data->used_bytes,
530 NULL);
531 if (!NT_SUCCESS(ret))
532 break;
534 DCHECK(!g_originals[it->id]);
535 g_originals[it->id] = &thunks->thunks[dll_data->num_thunks];
537 dll_data->num_thunks++;
538 dll_data->used_bytes += sizeof(ThunkData);
541 delete(thunk);
543 #if SANDBOX_EXPORTS
544 if (NULL != local_interceptor)
545 ::FreeLibrary(local_interceptor);
546 #endif
548 if (it != interceptions_.end())
549 return false;
551 return true;
554 } // namespace sandbox