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