1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/sandbox/win/src/interception.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,550 @@ 1.4 +// Copyright (c) 2012 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +// For information about interceptions as a whole see 1.9 +// http://dev.chromium.org/developers/design-documents/sandbox . 1.10 + 1.11 +#include <set> 1.12 + 1.13 +#include "sandbox/win/src/interception.h" 1.14 + 1.15 +#include "base/logging.h" 1.16 +#include "base/memory/scoped_ptr.h" 1.17 +#include "base/win/pe_image.h" 1.18 +#include "base/win/windows_version.h" 1.19 +#include "sandbox/win/src/interception_internal.h" 1.20 +#include "sandbox/win/src/interceptors.h" 1.21 +#include "sandbox/win/src/sandbox.h" 1.22 +#include "sandbox/win/src/sandbox_utils.h" 1.23 +#include "sandbox/win/src/service_resolver.h" 1.24 +#include "sandbox/win/src/target_interceptions.h" 1.25 +#include "sandbox/win/src/target_process.h" 1.26 +#include "sandbox/win/src/wow64.h" 1.27 + 1.28 +namespace { 1.29 + 1.30 +const char kMapViewOfSectionName[] = "NtMapViewOfSection"; 1.31 +const char kUnmapViewOfSectionName[] = "NtUnmapViewOfSection"; 1.32 + 1.33 +// Standard allocation granularity and page size for Windows. 1.34 +const size_t kAllocGranularity = 65536; 1.35 +const size_t kPageSize = 4096; 1.36 + 1.37 +// Find a random offset within 64k and aligned to ceil(log2(size)). 1.38 +size_t GetGranularAlignedRandomOffset(size_t size) { 1.39 + CHECK_LE(size, kAllocGranularity); 1.40 + unsigned int offset; 1.41 + 1.42 + do { 1.43 + rand_s(&offset); 1.44 + offset &= (kAllocGranularity - 1); 1.45 + } while (offset > (kAllocGranularity - size)); 1.46 + 1.47 + // Find an alignment between 64 and the page size (4096). 1.48 + size_t align_size = kPageSize; 1.49 + for (size_t new_size = align_size / 2; new_size >= size; new_size /= 2) { 1.50 + align_size = new_size; 1.51 + } 1.52 + return offset & ~(align_size - 1); 1.53 +} 1.54 + 1.55 +} // namespace 1.56 + 1.57 +namespace sandbox { 1.58 + 1.59 +SANDBOX_INTERCEPT SharedMemory* g_interceptions; 1.60 + 1.61 +// Table of the unpatched functions that we intercept. Mapped from the parent. 1.62 +SANDBOX_INTERCEPT OriginalFunctions g_originals = { NULL }; 1.63 + 1.64 +// Magic constant that identifies that this function is not to be patched. 1.65 +const char kUnloadDLLDummyFunction[] = "@"; 1.66 + 1.67 +InterceptionManager::InterceptionManager(TargetProcess* child_process, 1.68 + bool relaxed) 1.69 + : child_(child_process), names_used_(false), relaxed_(relaxed) { 1.70 + child_->AddRef(); 1.71 +} 1.72 +InterceptionManager::~InterceptionManager() { 1.73 + child_->Release(); 1.74 +} 1.75 + 1.76 +bool InterceptionManager::AddToPatchedFunctions( 1.77 + const wchar_t* dll_name, const char* function_name, 1.78 + InterceptionType interception_type, const void* replacement_code_address, 1.79 + InterceptorId id) { 1.80 + InterceptionData function; 1.81 + function.type = interception_type; 1.82 + function.id = id; 1.83 + function.dll = dll_name; 1.84 + function.function = function_name; 1.85 + function.interceptor_address = replacement_code_address; 1.86 + 1.87 + interceptions_.push_back(function); 1.88 + return true; 1.89 +} 1.90 + 1.91 +bool InterceptionManager::AddToPatchedFunctions( 1.92 + const wchar_t* dll_name, const char* function_name, 1.93 + InterceptionType interception_type, const char* replacement_function_name, 1.94 + InterceptorId id) { 1.95 + InterceptionData function; 1.96 + function.type = interception_type; 1.97 + function.id = id; 1.98 + function.dll = dll_name; 1.99 + function.function = function_name; 1.100 + function.interceptor = replacement_function_name; 1.101 + function.interceptor_address = NULL; 1.102 + 1.103 + interceptions_.push_back(function); 1.104 + names_used_ = true; 1.105 + return true; 1.106 +} 1.107 + 1.108 +bool InterceptionManager::AddToUnloadModules(const wchar_t* dll_name) { 1.109 + InterceptionData module_to_unload; 1.110 + module_to_unload.type = INTERCEPTION_UNLOAD_MODULE; 1.111 + module_to_unload.dll = dll_name; 1.112 + // The next two are dummy values that make the structures regular, instead 1.113 + // of having special cases. They should not be used. 1.114 + module_to_unload.function = kUnloadDLLDummyFunction; 1.115 + module_to_unload.interceptor_address = reinterpret_cast<void*>(1); 1.116 + 1.117 + interceptions_.push_back(module_to_unload); 1.118 + return true; 1.119 +} 1.120 + 1.121 +bool InterceptionManager::InitializeInterceptions() { 1.122 + if (interceptions_.empty()) 1.123 + return true; // Nothing to do here 1.124 + 1.125 + size_t buffer_bytes = GetBufferSize(); 1.126 + scoped_ptr<char[]> local_buffer(new char[buffer_bytes]); 1.127 + 1.128 + if (!SetupConfigBuffer(local_buffer.get(), buffer_bytes)) 1.129 + return false; 1.130 + 1.131 + void* remote_buffer; 1.132 + if (!CopyDataToChild(local_buffer.get(), buffer_bytes, &remote_buffer)) 1.133 + return false; 1.134 + 1.135 + bool hot_patch_needed = (0 != buffer_bytes); 1.136 + if (!PatchNtdll(hot_patch_needed)) 1.137 + return false; 1.138 + 1.139 + g_interceptions = reinterpret_cast<SharedMemory*>(remote_buffer); 1.140 + ResultCode rc = child_->TransferVariable("g_interceptions", 1.141 + &g_interceptions, 1.142 + sizeof(g_interceptions)); 1.143 + return (SBOX_ALL_OK == rc); 1.144 +} 1.145 + 1.146 +size_t InterceptionManager::GetBufferSize() const { 1.147 + std::set<std::wstring> dlls; 1.148 + size_t buffer_bytes = 0; 1.149 + 1.150 + std::list<InterceptionData>::const_iterator it = interceptions_.begin(); 1.151 + for (; it != interceptions_.end(); ++it) { 1.152 + // skip interceptions that are performed from the parent 1.153 + if (!IsInterceptionPerformedByChild(*it)) 1.154 + continue; 1.155 + 1.156 + if (!dlls.count(it->dll)) { 1.157 + // NULL terminate the dll name on the structure 1.158 + size_t dll_name_bytes = (it->dll.size() + 1) * sizeof(wchar_t); 1.159 + 1.160 + // include the dll related size 1.161 + buffer_bytes += RoundUpToMultiple(offsetof(DllPatchInfo, dll_name) + 1.162 + dll_name_bytes, sizeof(size_t)); 1.163 + dlls.insert(it->dll); 1.164 + } 1.165 + 1.166 + // we have to NULL terminate the strings on the structure 1.167 + size_t strings_chars = it->function.size() + it->interceptor.size() + 2; 1.168 + 1.169 + // a new FunctionInfo is required per function 1.170 + size_t record_bytes = offsetof(FunctionInfo, function) + strings_chars; 1.171 + record_bytes = RoundUpToMultiple(record_bytes, sizeof(size_t)); 1.172 + buffer_bytes += record_bytes; 1.173 + } 1.174 + 1.175 + if (0 != buffer_bytes) 1.176 + // add the part of SharedMemory that we have not counted yet 1.177 + buffer_bytes += offsetof(SharedMemory, dll_list); 1.178 + 1.179 + return buffer_bytes; 1.180 +} 1.181 + 1.182 +// Basically, walk the list of interceptions moving them to the config buffer, 1.183 +// but keeping together all interceptions that belong to the same dll. 1.184 +// The config buffer is a local buffer, not the one allocated on the child. 1.185 +bool InterceptionManager::SetupConfigBuffer(void* buffer, size_t buffer_bytes) { 1.186 + if (0 == buffer_bytes) 1.187 + return true; 1.188 + 1.189 + DCHECK(buffer_bytes > sizeof(SharedMemory)); 1.190 + 1.191 + SharedMemory* shared_memory = reinterpret_cast<SharedMemory*>(buffer); 1.192 + DllPatchInfo* dll_info = shared_memory->dll_list; 1.193 + int num_dlls = 0; 1.194 + 1.195 + shared_memory->interceptor_base = names_used_ ? child_->MainModule() : NULL; 1.196 + 1.197 + buffer_bytes -= offsetof(SharedMemory, dll_list); 1.198 + buffer = dll_info; 1.199 + 1.200 + std::list<InterceptionData>::iterator it = interceptions_.begin(); 1.201 + for (; it != interceptions_.end();) { 1.202 + // skip interceptions that are performed from the parent 1.203 + if (!IsInterceptionPerformedByChild(*it)) { 1.204 + ++it; 1.205 + continue; 1.206 + } 1.207 + 1.208 + const std::wstring dll = it->dll; 1.209 + if (!SetupDllInfo(*it, &buffer, &buffer_bytes)) 1.210 + return false; 1.211 + 1.212 + // walk the interceptions from this point, saving the ones that are 1.213 + // performed on this dll, and removing the entry from the list. 1.214 + // advance the iterator before removing the element from the list 1.215 + std::list<InterceptionData>::iterator rest = it; 1.216 + for (; rest != interceptions_.end();) { 1.217 + if (rest->dll == dll) { 1.218 + if (!SetupInterceptionInfo(*rest, &buffer, &buffer_bytes, dll_info)) 1.219 + return false; 1.220 + if (it == rest) 1.221 + ++it; 1.222 + rest = interceptions_.erase(rest); 1.223 + } else { 1.224 + ++rest; 1.225 + } 1.226 + } 1.227 + dll_info = reinterpret_cast<DllPatchInfo*>(buffer); 1.228 + ++num_dlls; 1.229 + } 1.230 + 1.231 + shared_memory->num_intercepted_dlls = num_dlls; 1.232 + return true; 1.233 +} 1.234 + 1.235 +// Fills up just the part that depends on the dll, not the info that depends on 1.236 +// the actual interception. 1.237 +bool InterceptionManager::SetupDllInfo(const InterceptionData& data, 1.238 + void** buffer, 1.239 + size_t* buffer_bytes) const { 1.240 + DCHECK(buffer_bytes); 1.241 + DCHECK(buffer); 1.242 + DCHECK(*buffer); 1.243 + 1.244 + DllPatchInfo* dll_info = reinterpret_cast<DllPatchInfo*>(*buffer); 1.245 + 1.246 + // the strings have to be zero terminated 1.247 + size_t required = offsetof(DllPatchInfo, dll_name) + 1.248 + (data.dll.size() + 1) * sizeof(wchar_t); 1.249 + required = RoundUpToMultiple(required, sizeof(size_t)); 1.250 + if (*buffer_bytes < required) 1.251 + return false; 1.252 + 1.253 + *buffer_bytes -= required; 1.254 + *buffer = reinterpret_cast<char*>(*buffer) + required; 1.255 + 1.256 + // set up the dll info to be what we know about it at this time 1.257 + dll_info->unload_module = (data.type == INTERCEPTION_UNLOAD_MODULE); 1.258 + dll_info->record_bytes = required; 1.259 + dll_info->offset_to_functions = required; 1.260 + dll_info->num_functions = 0; 1.261 + data.dll._Copy_s(dll_info->dll_name, data.dll.size(), data.dll.size()); 1.262 + dll_info->dll_name[data.dll.size()] = L'\0'; 1.263 + 1.264 + return true; 1.265 +} 1.266 + 1.267 +bool InterceptionManager::SetupInterceptionInfo(const InterceptionData& data, 1.268 + void** buffer, 1.269 + size_t* buffer_bytes, 1.270 + DllPatchInfo* dll_info) const { 1.271 + DCHECK(buffer_bytes); 1.272 + DCHECK(buffer); 1.273 + DCHECK(*buffer); 1.274 + 1.275 + if ((dll_info->unload_module) && 1.276 + (data.function != kUnloadDLLDummyFunction)) { 1.277 + // Can't specify a dll for both patch and unload. 1.278 + NOTREACHED(); 1.279 + } 1.280 + 1.281 + FunctionInfo* function = reinterpret_cast<FunctionInfo*>(*buffer); 1.282 + 1.283 + size_t name_bytes = data.function.size(); 1.284 + size_t interceptor_bytes = data.interceptor.size(); 1.285 + 1.286 + // the strings at the end of the structure are zero terminated 1.287 + size_t required = offsetof(FunctionInfo, function) + 1.288 + name_bytes + interceptor_bytes + 2; 1.289 + required = RoundUpToMultiple(required, sizeof(size_t)); 1.290 + if (*buffer_bytes < required) 1.291 + return false; 1.292 + 1.293 + // update the caller's values 1.294 + *buffer_bytes -= required; 1.295 + *buffer = reinterpret_cast<char*>(*buffer) + required; 1.296 + 1.297 + function->record_bytes = required; 1.298 + function->type = data.type; 1.299 + function->id = data.id; 1.300 + function->interceptor_address = data.interceptor_address; 1.301 + char* names = function->function; 1.302 + 1.303 + data.function._Copy_s(names, name_bytes, name_bytes); 1.304 + names += name_bytes; 1.305 + *names++ = '\0'; 1.306 + 1.307 + // interceptor follows the function_name 1.308 + data.interceptor._Copy_s(names, interceptor_bytes, interceptor_bytes); 1.309 + names += interceptor_bytes; 1.310 + *names++ = '\0'; 1.311 + 1.312 + // update the dll table 1.313 + dll_info->num_functions++; 1.314 + dll_info->record_bytes += required; 1.315 + 1.316 + return true; 1.317 +} 1.318 + 1.319 +bool InterceptionManager::CopyDataToChild(const void* local_buffer, 1.320 + size_t buffer_bytes, 1.321 + void** remote_buffer) const { 1.322 + DCHECK(NULL != remote_buffer); 1.323 + if (0 == buffer_bytes) { 1.324 + *remote_buffer = NULL; 1.325 + return true; 1.326 + } 1.327 + 1.328 + HANDLE child = child_->Process(); 1.329 + 1.330 + // Allocate memory on the target process without specifying the address 1.331 + void* remote_data = ::VirtualAllocEx(child, NULL, buffer_bytes, 1.332 + MEM_COMMIT, PAGE_READWRITE); 1.333 + if (NULL == remote_data) 1.334 + return false; 1.335 + 1.336 + SIZE_T bytes_written; 1.337 + BOOL success = ::WriteProcessMemory(child, remote_data, local_buffer, 1.338 + buffer_bytes, &bytes_written); 1.339 + if (FALSE == success || bytes_written != buffer_bytes) { 1.340 + ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE); 1.341 + return false; 1.342 + } 1.343 + 1.344 + *remote_buffer = remote_data; 1.345 + 1.346 + return true; 1.347 +} 1.348 + 1.349 +// Only return true if the child should be able to perform this interception. 1.350 +bool InterceptionManager::IsInterceptionPerformedByChild( 1.351 + const InterceptionData& data) const { 1.352 + if (INTERCEPTION_INVALID == data.type) 1.353 + return false; 1.354 + 1.355 + if (INTERCEPTION_SERVICE_CALL == data.type) 1.356 + return false; 1.357 + 1.358 + if (data.type >= INTERCEPTION_LAST) 1.359 + return false; 1.360 + 1.361 + std::wstring ntdll(kNtdllName); 1.362 + if (ntdll == data.dll) 1.363 + return false; // ntdll has to be intercepted from the parent 1.364 + 1.365 + return true; 1.366 +} 1.367 + 1.368 +bool InterceptionManager::PatchNtdll(bool hot_patch_needed) { 1.369 + // Maybe there is nothing to do 1.370 + if (!hot_patch_needed && interceptions_.empty()) 1.371 + return true; 1.372 + 1.373 + if (hot_patch_needed) { 1.374 +#if SANDBOX_EXPORTS 1.375 + // Make sure the functions are not excluded by the linker. 1.376 +#if defined(_WIN64) 1.377 + #pragma comment(linker, "/include:TargetNtMapViewOfSection64") 1.378 + #pragma comment(linker, "/include:TargetNtUnmapViewOfSection64") 1.379 +#else 1.380 + #pragma comment(linker, "/include:_TargetNtMapViewOfSection@44") 1.381 + #pragma comment(linker, "/include:_TargetNtUnmapViewOfSection@12") 1.382 +#endif 1.383 +#endif 1.384 + ADD_NT_INTERCEPTION(NtMapViewOfSection, MAP_VIEW_OF_SECTION_ID, 44); 1.385 + ADD_NT_INTERCEPTION(NtUnmapViewOfSection, UNMAP_VIEW_OF_SECTION_ID, 12); 1.386 + } 1.387 + 1.388 + // Reserve a full 64k memory range in the child process. 1.389 + HANDLE child = child_->Process(); 1.390 + BYTE* thunk_base = reinterpret_cast<BYTE*>( 1.391 + ::VirtualAllocEx(child, NULL, kAllocGranularity, 1.392 + MEM_RESERVE, PAGE_NOACCESS)); 1.393 + 1.394 + // Find an aligned, random location within the reserved range. 1.395 + size_t thunk_bytes = interceptions_.size() * sizeof(ThunkData) + 1.396 + sizeof(DllInterceptionData); 1.397 + size_t thunk_offset = GetGranularAlignedRandomOffset(thunk_bytes); 1.398 + 1.399 + // Split the base and offset along page boundaries. 1.400 + thunk_base += thunk_offset & ~(kPageSize - 1); 1.401 + thunk_offset &= kPageSize - 1; 1.402 + 1.403 + // Make an aligned, padded allocation, and move the pointer to our chunk. 1.404 + size_t thunk_bytes_padded = (thunk_bytes + kPageSize - 1) & kPageSize; 1.405 + thunk_base = reinterpret_cast<BYTE*>( 1.406 + ::VirtualAllocEx(child, thunk_base, thunk_bytes_padded, 1.407 + MEM_COMMIT, PAGE_EXECUTE_READWRITE)); 1.408 + CHECK(thunk_base); // If this fails we'd crash anyway on an invalid access. 1.409 + DllInterceptionData* thunks = reinterpret_cast<DllInterceptionData*>( 1.410 + thunk_base + thunk_offset); 1.411 + 1.412 + DllInterceptionData dll_data; 1.413 + dll_data.data_bytes = thunk_bytes; 1.414 + dll_data.num_thunks = 0; 1.415 + dll_data.used_bytes = offsetof(DllInterceptionData, thunks); 1.416 + 1.417 + // Reset all helpers for a new child. 1.418 + memset(g_originals, 0, sizeof(g_originals)); 1.419 + 1.420 + // this should write all the individual thunks to the child's memory 1.421 + if (!PatchClientFunctions(thunks, thunk_bytes, &dll_data)) 1.422 + return false; 1.423 + 1.424 + // and now write the first part of the table to the child's memory 1.425 + SIZE_T written; 1.426 + bool ok = FALSE != ::WriteProcessMemory(child, thunks, &dll_data, 1.427 + offsetof(DllInterceptionData, thunks), 1.428 + &written); 1.429 + 1.430 + if (!ok || (offsetof(DllInterceptionData, thunks) != written)) 1.431 + return false; 1.432 + 1.433 + // Attempt to protect all the thunks, but ignore failure 1.434 + DWORD old_protection; 1.435 + ::VirtualProtectEx(child, thunks, thunk_bytes, 1.436 + PAGE_EXECUTE_READ, &old_protection); 1.437 + 1.438 + ResultCode ret = child_->TransferVariable("g_originals", g_originals, 1.439 + sizeof(g_originals)); 1.440 + return (SBOX_ALL_OK == ret); 1.441 +} 1.442 + 1.443 +bool InterceptionManager::PatchClientFunctions(DllInterceptionData* thunks, 1.444 + size_t thunk_bytes, 1.445 + DllInterceptionData* dll_data) { 1.446 + DCHECK(NULL != thunks); 1.447 + DCHECK(NULL != dll_data); 1.448 + 1.449 + HMODULE ntdll_base = ::GetModuleHandle(kNtdllName); 1.450 + if (!ntdll_base) 1.451 + return false; 1.452 + 1.453 + base::win::PEImage ntdll_image(ntdll_base); 1.454 + 1.455 + // Bypass purify's interception. 1.456 + wchar_t* loader_get = reinterpret_cast<wchar_t*>( 1.457 + ntdll_image.GetProcAddress("LdrGetDllHandle")); 1.458 + if (loader_get) { 1.459 + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | 1.460 + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 1.461 + loader_get, &ntdll_base)) 1.462 + return false; 1.463 + } 1.464 + 1.465 + if (base::win::GetVersion() <= base::win::VERSION_VISTA) { 1.466 + Wow64 WowHelper(child_, ntdll_base); 1.467 + if (!WowHelper.WaitForNtdll()) 1.468 + return false; 1.469 + } 1.470 + 1.471 + char* interceptor_base = NULL; 1.472 + 1.473 +#if SANDBOX_EXPORTS 1.474 + interceptor_base = reinterpret_cast<char*>(child_->MainModule()); 1.475 + HMODULE local_interceptor = ::LoadLibrary(child_->Name()); 1.476 +#endif 1.477 + 1.478 + ServiceResolverThunk* thunk; 1.479 +#if defined(_WIN64) 1.480 + thunk = new ServiceResolverThunk(child_->Process(), relaxed_); 1.481 +#else 1.482 + base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); 1.483 + if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) { 1.484 + if (os_info->version() >= base::win::VERSION_WIN8) 1.485 + thunk = new Wow64W8ResolverThunk(child_->Process(), relaxed_); 1.486 + else 1.487 + thunk = new Wow64ResolverThunk(child_->Process(), relaxed_); 1.488 + } else if (!IsXPSP2OrLater()) { 1.489 + thunk = new Win2kResolverThunk(child_->Process(), relaxed_); 1.490 + } else if (os_info->version() >= base::win::VERSION_WIN8) { 1.491 + thunk = new Win8ResolverThunk(child_->Process(), relaxed_); 1.492 + } else { 1.493 + thunk = new ServiceResolverThunk(child_->Process(), relaxed_); 1.494 + } 1.495 +#endif 1.496 + 1.497 + std::list<InterceptionData>::iterator it = interceptions_.begin(); 1.498 + for (; it != interceptions_.end(); ++it) { 1.499 + const std::wstring ntdll(kNtdllName); 1.500 + if (it->dll != ntdll) 1.501 + break; 1.502 + 1.503 + if (INTERCEPTION_SERVICE_CALL != it->type) 1.504 + break; 1.505 + 1.506 +#if SANDBOX_EXPORTS 1.507 + // We may be trying to patch by function name. 1.508 + if (NULL == it->interceptor_address) { 1.509 + const char* address; 1.510 + NTSTATUS ret = thunk->ResolveInterceptor(local_interceptor, 1.511 + it->interceptor.c_str(), 1.512 + reinterpret_cast<const void**>( 1.513 + &address)); 1.514 + if (!NT_SUCCESS(ret)) 1.515 + break; 1.516 + 1.517 + // Translate the local address to an address on the child. 1.518 + it->interceptor_address = interceptor_base + (address - 1.519 + reinterpret_cast<char*>(local_interceptor)); 1.520 + } 1.521 +#endif 1.522 + NTSTATUS ret = thunk->Setup(ntdll_base, 1.523 + interceptor_base, 1.524 + it->function.c_str(), 1.525 + it->interceptor.c_str(), 1.526 + it->interceptor_address, 1.527 + &thunks->thunks[dll_data->num_thunks], 1.528 + thunk_bytes - dll_data->used_bytes, 1.529 + NULL); 1.530 + if (!NT_SUCCESS(ret)) 1.531 + break; 1.532 + 1.533 + DCHECK(!g_originals[it->id]); 1.534 + g_originals[it->id] = &thunks->thunks[dll_data->num_thunks]; 1.535 + 1.536 + dll_data->num_thunks++; 1.537 + dll_data->used_bytes += sizeof(ThunkData); 1.538 + } 1.539 + 1.540 + delete(thunk); 1.541 + 1.542 +#if SANDBOX_EXPORTS 1.543 + if (NULL != local_interceptor) 1.544 + ::FreeLibrary(local_interceptor); 1.545 +#endif 1.546 + 1.547 + if (it != interceptions_.end()) 1.548 + return false; 1.549 + 1.550 + return true; 1.551 +} 1.552 + 1.553 +} // namespace sandbox