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