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/sandbox_nt_util.h" 6 7 #include "base/win/pe_image.h" 8 #include "sandbox/win/src/sandbox_factory.h" 9 #include "sandbox/win/src/target_services.h" 10 11 namespace sandbox { 12 13 // This is the list of all imported symbols from ntdll.dll. 14 SANDBOX_INTERCEPT NtExports g_nt; 15 16 } // namespace sandbox 17 18 namespace { 19 20 #if defined(_WIN64) 21 void* AllocateNearTo(void* source, size_t size) { 22 using sandbox::g_nt; 23 24 // Start with 1 GB above the source. 25 const size_t kOneGB = 0x40000000; 26 void* base = reinterpret_cast<char*>(source) + kOneGB; 27 SIZE_T actual_size = size; 28 ULONG_PTR zero_bits = 0; // Not the correct type if used. 29 ULONG type = MEM_RESERVE; 30 31 NTSTATUS ret; 32 int attempts = 0; 33 for (; attempts < 41; attempts++) { 34 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits, 35 &actual_size, type, PAGE_READWRITE); 36 if (NT_SUCCESS(ret)) { 37 if (base < source || 38 base >= reinterpret_cast<char*>(source) + 4 * kOneGB) { 39 // We won't be able to patch this dll. 40 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, 41 MEM_RELEASE)); 42 return NULL; 43 } 44 break; 45 } 46 47 if (attempts == 30) { 48 // Try the first GB. 49 base = reinterpret_cast<char*>(source); 50 } else if (attempts == 40) { 51 // Try the highest available address. 52 base = NULL; 53 type |= MEM_TOP_DOWN; 54 } 55 56 // Try 100 MB higher. 57 base = reinterpret_cast<char*>(base) + 100 * 0x100000; 58 }; 59 60 if (attempts == 41) 61 return NULL; 62 63 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits, 64 &actual_size, MEM_COMMIT, PAGE_READWRITE); 65 66 if (!NT_SUCCESS(ret)) { 67 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, 68 MEM_RELEASE)); 69 base = NULL; 70 } 71 72 return base; 73 } 74 #else // defined(_WIN64). 75 void* AllocateNearTo(void* source, size_t size) { 76 using sandbox::g_nt; 77 UNREFERENCED_PARAMETER(source); 78 79 // In 32-bit processes allocations below 512k are predictable, so mark 80 // anything in that range as reserved and retry until we get a good address. 81 const void* const kMinAddress = reinterpret_cast<void*>(512 * 1024); 82 NTSTATUS ret; 83 SIZE_T actual_size; 84 void* base; 85 do { 86 base = NULL; 87 actual_size = 64 * 1024; 88 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size, 89 MEM_RESERVE, PAGE_NOACCESS); 90 if (!NT_SUCCESS(ret)) 91 return NULL; 92 } while (base < kMinAddress); 93 94 actual_size = size; 95 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size, 96 MEM_COMMIT, PAGE_READWRITE); 97 if (!NT_SUCCESS(ret)) 98 return NULL; 99 return base; 100 } 101 #endif // defined(_WIN64). 102 103 } // namespace. 104 105 namespace sandbox { 106 107 // Handle for our private heap. 108 void* g_heap = NULL; 109 110 SANDBOX_INTERCEPT HANDLE g_shared_section; 111 SANDBOX_INTERCEPT size_t g_shared_IPC_size = 0; 112 SANDBOX_INTERCEPT size_t g_shared_policy_size = 0; 113 114 void* volatile g_shared_policy_memory = NULL; 115 void* volatile g_shared_IPC_memory = NULL; 116 117 // Both the IPC and the policy share a single region of memory in which the IPC 118 // memory is first and the policy memory is last. 119 bool MapGlobalMemory() { 120 if (NULL == g_shared_IPC_memory) { 121 void* memory = NULL; 122 SIZE_T size = 0; 123 // Map the entire shared section from the start. 124 NTSTATUS ret = g_nt.MapViewOfSection(g_shared_section, NtCurrentProcess, 125 &memory, 0, 0, NULL, &size, ViewUnmap, 126 0, PAGE_READWRITE); 127 128 if (!NT_SUCCESS(ret) || NULL == memory) { 129 NOTREACHED_NT(); 130 return false; 131 } 132 133 if (NULL != _InterlockedCompareExchangePointer(&g_shared_IPC_memory, 134 memory, NULL)) { 135 // Somebody beat us to the memory setup. 136 ret = g_nt.UnmapViewOfSection(NtCurrentProcess, memory); 137 VERIFY_SUCCESS(ret); 138 } 139 DCHECK_NT(g_shared_IPC_size > 0); 140 g_shared_policy_memory = reinterpret_cast<char*>(g_shared_IPC_memory) 141 + g_shared_IPC_size; 142 } 143 DCHECK_NT(g_shared_policy_memory); 144 DCHECK_NT(g_shared_policy_size > 0); 145 return true; 146 } 147 148 void* GetGlobalIPCMemory() { 149 if (!MapGlobalMemory()) 150 return NULL; 151 return g_shared_IPC_memory; 152 } 153 154 void* GetGlobalPolicyMemory() { 155 if (!MapGlobalMemory()) 156 return NULL; 157 return g_shared_policy_memory; 158 } 159 160 bool InitHeap() { 161 if (!g_heap) { 162 // Create a new heap using default values for everything. 163 void* heap = g_nt.RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL); 164 if (!heap) 165 return false; 166 167 if (NULL != _InterlockedCompareExchangePointer(&g_heap, heap, NULL)) { 168 // Somebody beat us to the memory setup. 169 g_nt.RtlDestroyHeap(heap); 170 } 171 } 172 return (g_heap != NULL); 173 } 174 175 // Physically reads or writes from memory to verify that (at this time), it is 176 // valid. Returns a dummy value. 177 int TouchMemory(void* buffer, size_t size_bytes, RequiredAccess intent) { 178 const int kPageSize = 4096; 179 int dummy = 0; 180 char* start = reinterpret_cast<char*>(buffer); 181 char* end = start + size_bytes - 1; 182 183 if (WRITE == intent) { 184 for (; start < end; start += kPageSize) { 185 *start = 0; 186 } 187 *end = 0; 188 } else { 189 for (; start < end; start += kPageSize) { 190 dummy += *start; 191 } 192 dummy += *end; 193 } 194 195 return dummy; 196 } 197 198 bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) { 199 DCHECK_NT(size); 200 __try { 201 TouchMemory(buffer, size, intent); 202 } __except(EXCEPTION_EXECUTE_HANDLER) { 203 return false; 204 } 205 return true; 206 } 207 208 NTSTATUS CopyData(void* destination, const void* source, size_t bytes) { 209 NTSTATUS ret = STATUS_SUCCESS; 210 __try { 211 g_nt.memcpy(destination, source, bytes); 212 } __except(EXCEPTION_EXECUTE_HANDLER) { 213 ret = GetExceptionCode(); 214 } 215 return ret; 216 } 217 218 // Hacky code... replace with AllocAndCopyObjectAttributes. 219 NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object, 220 wchar_t** out_name, uint32* attributes, 221 HANDLE* root) { 222 if (!InitHeap()) 223 return STATUS_NO_MEMORY; 224 225 DCHECK_NT(out_name); 226 *out_name = NULL; 227 NTSTATUS ret = STATUS_UNSUCCESSFUL; 228 __try { 229 do { 230 if (in_object->RootDirectory != static_cast<HANDLE>(0) && !root) 231 break; 232 if (NULL == in_object->ObjectName) 233 break; 234 if (NULL == in_object->ObjectName->Buffer) 235 break; 236 237 size_t size = in_object->ObjectName->Length + sizeof(wchar_t); 238 *out_name = new(NT_ALLOC) wchar_t[size/sizeof(wchar_t)]; 239 if (NULL == *out_name) 240 break; 241 242 ret = CopyData(*out_name, in_object->ObjectName->Buffer, 243 size - sizeof(wchar_t)); 244 if (!NT_SUCCESS(ret)) 245 break; 246 247 (*out_name)[size / sizeof(wchar_t) - 1] = L'\0'; 248 249 if (attributes) 250 *attributes = in_object->Attributes; 251 252 if (root) 253 *root = in_object->RootDirectory; 254 ret = STATUS_SUCCESS; 255 } while (false); 256 } __except(EXCEPTION_EXECUTE_HANDLER) { 257 ret = GetExceptionCode(); 258 } 259 260 if (!NT_SUCCESS(ret) && *out_name) { 261 operator delete(*out_name, NT_ALLOC); 262 *out_name = NULL; 263 } 264 265 return ret; 266 } 267 268 NTSTATUS GetProcessId(HANDLE process, ULONG *process_id) { 269 PROCESS_BASIC_INFORMATION proc_info; 270 ULONG bytes_returned; 271 272 NTSTATUS ret = g_nt.QueryInformationProcess(process, ProcessBasicInformation, 273 &proc_info, sizeof(proc_info), 274 &bytes_returned); 275 if (!NT_SUCCESS(ret) || sizeof(proc_info) != bytes_returned) 276 return ret; 277 278 *process_id = proc_info.UniqueProcessId; 279 return STATUS_SUCCESS; 280 } 281 282 bool IsSameProcess(HANDLE process) { 283 if (NtCurrentProcess == process) 284 return true; 285 286 static ULONG s_process_id = 0; 287 288 if (!s_process_id) { 289 NTSTATUS ret = GetProcessId(NtCurrentProcess, &s_process_id); 290 if (!NT_SUCCESS(ret)) 291 return false; 292 } 293 294 ULONG process_id; 295 NTSTATUS ret = GetProcessId(process, &process_id); 296 if (!NT_SUCCESS(ret)) 297 return false; 298 299 return (process_id == s_process_id); 300 } 301 302 bool IsValidImageSection(HANDLE section, PVOID *base, PLARGE_INTEGER offset, 303 PSIZE_T view_size) { 304 if (!section || !base || !view_size || offset) 305 return false; 306 307 HANDLE query_section; 308 309 NTSTATUS ret = g_nt.DuplicateObject(NtCurrentProcess, section, 310 NtCurrentProcess, &query_section, 311 SECTION_QUERY, 0, 0); 312 if (!NT_SUCCESS(ret)) 313 return false; 314 315 SECTION_BASIC_INFORMATION basic_info; 316 SIZE_T bytes_returned; 317 ret = g_nt.QuerySection(query_section, SectionBasicInformation, &basic_info, 318 sizeof(basic_info), &bytes_returned); 319 320 VERIFY_SUCCESS(g_nt.Close(query_section)); 321 322 if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned) 323 return false; 324 325 if (!(basic_info.Attributes & SEC_IMAGE)) 326 return false; 327 328 return true; 329 } 330 331 UNICODE_STRING* AnsiToUnicode(const char* string) { 332 ANSI_STRING ansi_string; 333 ansi_string.Length = static_cast<USHORT>(g_nt.strlen(string)); 334 ansi_string.MaximumLength = ansi_string.Length + 1; 335 ansi_string.Buffer = const_cast<char*>(string); 336 337 if (ansi_string.Length > ansi_string.MaximumLength) 338 return NULL; 339 340 size_t name_bytes = ansi_string.MaximumLength * sizeof(wchar_t) + 341 sizeof(UNICODE_STRING); 342 343 UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>( 344 new(NT_ALLOC) char[name_bytes]); 345 if (!out_string) 346 return NULL; 347 348 out_string->MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t); 349 out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]); 350 351 BOOLEAN alloc_destination = FALSE; 352 NTSTATUS ret = g_nt.RtlAnsiStringToUnicodeString(out_string, &ansi_string, 353 alloc_destination); 354 DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret); 355 if (!NT_SUCCESS(ret)) { 356 operator delete(out_string, NT_ALLOC); 357 return NULL; 358 } 359 360 return out_string; 361 } 362 363 UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags) { 364 UNICODE_STRING* out_name = NULL; 365 __try { 366 do { 367 *flags = 0; 368 base::win::PEImage pe(module); 369 370 if (!pe.VerifyMagic()) 371 break; 372 *flags |= MODULE_IS_PE_IMAGE; 373 374 PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory(); 375 if (exports) { 376 char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name)); 377 out_name = AnsiToUnicode(name); 378 } 379 380 PIMAGE_NT_HEADERS headers = pe.GetNTHeaders(); 381 if (headers) { 382 if (headers->OptionalHeader.AddressOfEntryPoint) 383 *flags |= MODULE_HAS_ENTRY_POINT; 384 if (headers->OptionalHeader.SizeOfCode) 385 *flags |= MODULE_HAS_CODE; 386 } 387 } while (false); 388 } __except(EXCEPTION_EXECUTE_HANDLER) { 389 } 390 391 return out_name; 392 } 393 394 UNICODE_STRING* GetBackingFilePath(PVOID address) { 395 // We'll start with something close to max_path charactes for the name. 396 ULONG buffer_bytes = MAX_PATH * 2; 397 398 for (;;) { 399 MEMORY_SECTION_NAME* section_name = reinterpret_cast<MEMORY_SECTION_NAME*>( 400 new(NT_ALLOC) char[buffer_bytes]); 401 402 if (!section_name) 403 return NULL; 404 405 ULONG returned_bytes; 406 NTSTATUS ret = g_nt.QueryVirtualMemory(NtCurrentProcess, address, 407 MemorySectionName, section_name, 408 buffer_bytes, &returned_bytes); 409 410 if (STATUS_BUFFER_OVERFLOW == ret) { 411 // Retry the call with the given buffer size. 412 operator delete(section_name, NT_ALLOC); 413 section_name = NULL; 414 buffer_bytes = returned_bytes; 415 continue; 416 } 417 if (!NT_SUCCESS(ret)) { 418 operator delete(section_name, NT_ALLOC); 419 return NULL; 420 } 421 422 return reinterpret_cast<UNICODE_STRING*>(section_name); 423 } 424 } 425 426 UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) { 427 if ((!module_path) || (!module_path->Buffer)) 428 return NULL; 429 430 wchar_t* sep = NULL; 431 int start_pos = module_path->Length / sizeof(wchar_t) - 1; 432 int ix = start_pos; 433 434 for (; ix >= 0; --ix) { 435 if (module_path->Buffer[ix] == L'\\') { 436 sep = &module_path->Buffer[ix]; 437 break; 438 } 439 } 440 441 // Ends with path separator. Not a valid module name. 442 if ((ix == start_pos) && sep) 443 return NULL; 444 445 // No path separator found. Use the entire name. 446 if (!sep) { 447 sep = &module_path->Buffer[-1]; 448 } 449 450 // Add one to the size so we can null terminate the string. 451 size_t size_bytes = (start_pos - ix + 1) * sizeof(wchar_t); 452 453 // Based on the code above, size_bytes should always be small enough 454 // to make the static_cast below safe. 455 DCHECK_NT(kuint16max > size_bytes); 456 char* str_buffer = new(NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)]; 457 if (!str_buffer) 458 return NULL; 459 460 UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(str_buffer); 461 out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]); 462 out_string->Length = static_cast<USHORT>(size_bytes - sizeof(wchar_t)); 463 out_string->MaximumLength = static_cast<USHORT>(size_bytes); 464 465 NTSTATUS ret = CopyData(out_string->Buffer, &sep[1], out_string->Length); 466 if (!NT_SUCCESS(ret)) { 467 operator delete(out_string, NT_ALLOC); 468 return NULL; 469 } 470 471 out_string->Buffer[out_string->Length / sizeof(wchar_t)] = L'\0'; 472 return out_string; 473 } 474 475 NTSTATUS AutoProtectMemory::ChangeProtection(void* address, size_t bytes, 476 ULONG protect) { 477 DCHECK_NT(!changed_); 478 SIZE_T new_bytes = bytes; 479 NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address, 480 &new_bytes, protect, &old_protect_); 481 if (NT_SUCCESS(ret)) { 482 changed_ = true; 483 address_ = address; 484 bytes_ = new_bytes; 485 } 486 487 return ret; 488 } 489 490 NTSTATUS AutoProtectMemory::RevertProtection() { 491 if (!changed_) 492 return STATUS_SUCCESS; 493 494 DCHECK_NT(address_); 495 DCHECK_NT(bytes_); 496 497 SIZE_T new_bytes = bytes_; 498 NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address_, 499 &new_bytes, old_protect_, 500 &old_protect_); 501 DCHECK_NT(NT_SUCCESS(ret)); 502 503 changed_ = false; 504 address_ = NULL; 505 bytes_ = 0; 506 old_protect_ = 0; 507 508 return ret; 509 } 510 511 bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length, 512 uint32 file_info_class) { 513 if (FileRenameInformation != file_info_class) 514 return false; 515 516 if (length < sizeof(FILE_RENAME_INFORMATION)) 517 return false; 518 519 // Make sure file name length doesn't exceed the message length 520 if (length - offsetof(FILE_RENAME_INFORMATION, FileName) < 521 file_info->FileNameLength) 522 return false; 523 524 // We don't support a root directory. 525 if (file_info->RootDirectory) 526 return false; 527 528 static const wchar_t kPathPrefix[] = { L'\\', L'?', L'?', L'\\'}; 529 530 // Check if it starts with \\??\\. We don't support relative paths. 531 if (file_info->FileNameLength < sizeof(kPathPrefix) || 532 file_info->FileNameLength > kuint16max) 533 return false; 534 535 if (file_info->FileName[0] != kPathPrefix[0] || 536 file_info->FileName[1] != kPathPrefix[1] || 537 file_info->FileName[2] != kPathPrefix[2] || 538 file_info->FileName[3] != kPathPrefix[3]) 539 return false; 540 541 return true; 542 } 543 544 } // namespace sandbox 545 546 void* operator new(size_t size, sandbox::AllocationType type, 547 void* near_to) { 548 using namespace sandbox; 549 550 void* result = NULL; 551 if (NT_ALLOC == type) { 552 if (InitHeap()) { 553 // Use default flags for the allocation. 554 result = g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size); 555 } 556 } else if (NT_PAGE == type) { 557 result = AllocateNearTo(near_to, size); 558 } else { 559 NOTREACHED_NT(); 560 } 561 562 // TODO: Returning NULL from operator new has undefined behavior, but 563 // the Allocate() functions called above can return NULL. Consider checking 564 // for NULL here and crashing or throwing. 565 566 return result; 567 } 568 569 void operator delete(void* memory, sandbox::AllocationType type) { 570 using namespace sandbox; 571 572 if (NT_ALLOC == type) { 573 // Use default flags. 574 VERIFY(g_nt.RtlFreeHeap(sandbox::g_heap, 0, memory)); 575 } else if (NT_PAGE == type) { 576 void* base = memory; 577 SIZE_T size = 0; 578 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, 579 MEM_RELEASE)); 580 } else { 581 NOTREACHED_NT(); 582 } 583 } 584 585 void operator delete(void* memory, sandbox::AllocationType type, 586 void* near_to) { 587 UNREFERENCED_PARAMETER(near_to); 588 operator delete(memory, type); 589 } 590 591 void* __cdecl operator new(size_t size, void* buffer, 592 sandbox::AllocationType type) { 593 UNREFERENCED_PARAMETER(size); 594 UNREFERENCED_PARAMETER(type); 595 return buffer; 596 } 597 598 void __cdecl operator delete(void* memory, void* buffer, 599 sandbox::AllocationType type) { 600 UNREFERENCED_PARAMETER(memory); 601 UNREFERENCED_PARAMETER(buffer); 602 UNREFERENCED_PARAMETER(type); 603 } 604