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