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.
5 #include "sandbox/win/src/sandbox_nt_util.h"
7 #include "base/win/pe_image.h"
8 #include "sandbox/win/src/sandbox_factory.h"
9 #include "sandbox/win/src/target_services.h"
13 // This is the list of all imported symbols from ntdll.dll.
14 SANDBOX_INTERCEPT NtExports g_nt = { NULL };
16 } // namespace sandbox
21 void* AllocateNearTo(void* source, size_t size) {
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;
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)) {
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,
49 base = reinterpret_cast<char*>(source);
50 } else if (attempts == 40) {
51 // Try the highest available address.
57 base = reinterpret_cast<char*>(base) + 100 * 0x100000;
63 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits,
64 &actual_size, MEM_COMMIT, PAGE_READWRITE);
66 if (!NT_SUCCESS(ret)) {
67 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
74 #else // defined(_WIN64).
75 void* AllocateNearTo(void* source, size_t size) {
77 UNREFERENCED_PARAMETER(source);
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);
87 actual_size = 64 * 1024;
88 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
89 MEM_RESERVE, PAGE_NOACCESS);
92 } while (base < kMinAddress);
95 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
96 MEM_COMMIT, PAGE_READWRITE);
101 #endif // defined(_WIN64).
107 // Handle for our private heap.
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;
114 void* volatile g_shared_policy_memory = NULL;
115 void* volatile g_shared_IPC_memory = NULL;
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) {
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,
128 if (!NT_SUCCESS(ret) || NULL == memory) {
133 if (NULL != _InterlockedCompareExchangePointer(&g_shared_IPC_memory,
135 // Somebody beat us to the memory setup.
136 ret = g_nt.UnmapViewOfSection(NtCurrentProcess, memory);
139 DCHECK_NT(g_shared_IPC_size > 0);
140 g_shared_policy_memory = reinterpret_cast<char*>(g_shared_IPC_memory)
143 DCHECK_NT(g_shared_policy_memory);
144 DCHECK_NT(g_shared_policy_size > 0);
148 void* GetGlobalIPCMemory() {
149 if (!MapGlobalMemory())
151 return g_shared_IPC_memory;
154 void* GetGlobalPolicyMemory() {
155 if (!MapGlobalMemory())
157 return g_shared_policy_memory;
162 // Create a new heap using default values for everything.
163 void* heap = g_nt.RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL);
167 if (NULL != _InterlockedCompareExchangePointer(&g_heap, heap, NULL)) {
168 // Somebody beat us to the memory setup.
169 g_nt.RtlDestroyHeap(heap);
172 return (g_heap != NULL);
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;
180 char* start = reinterpret_cast<char*>(buffer);
181 char* end = start + size_bytes - 1;
183 if (WRITE == intent) {
184 for (; start < end; start += kPageSize) {
189 for (; start < end; start += kPageSize) {
198 bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) {
201 TouchMemory(buffer, size, intent);
202 } __except(EXCEPTION_EXECUTE_HANDLER) {
208 NTSTATUS CopyData(void* destination, const void* source, size_t bytes) {
209 NTSTATUS ret = STATUS_SUCCESS;
211 if (SandboxFactory::GetTargetServices()->GetState()->InitCalled()) {
212 memcpy(destination, source, bytes);
214 const char* from = reinterpret_cast<const char*>(source);
215 char* to = reinterpret_cast<char*>(destination);
216 for (size_t i = 0; i < bytes; i++) {
220 } __except(EXCEPTION_EXECUTE_HANDLER) {
221 ret = GetExceptionCode();
226 // Hacky code... replace with AllocAndCopyObjectAttributes.
227 NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object,
228 wchar_t** out_name, uint32* attributes,
231 return STATUS_NO_MEMORY;
235 NTSTATUS ret = STATUS_UNSUCCESSFUL;
238 if (in_object->RootDirectory != static_cast<HANDLE>(0) && !root)
240 if (NULL == in_object->ObjectName)
242 if (NULL == in_object->ObjectName->Buffer)
245 size_t size = in_object->ObjectName->Length + sizeof(wchar_t);
246 *out_name = new(NT_ALLOC) wchar_t[size/sizeof(wchar_t)];
247 if (NULL == *out_name)
250 ret = CopyData(*out_name, in_object->ObjectName->Buffer,
251 size - sizeof(wchar_t));
252 if (!NT_SUCCESS(ret))
255 (*out_name)[size / sizeof(wchar_t) - 1] = L'\0';
258 *attributes = in_object->Attributes;
261 *root = in_object->RootDirectory;
262 ret = STATUS_SUCCESS;
264 } __except(EXCEPTION_EXECUTE_HANDLER) {
265 ret = GetExceptionCode();
268 if (!NT_SUCCESS(ret) && *out_name) {
269 operator delete(*out_name, NT_ALLOC);
276 NTSTATUS GetProcessId(HANDLE process, ULONG *process_id) {
277 PROCESS_BASIC_INFORMATION proc_info;
278 ULONG bytes_returned;
280 NTSTATUS ret = g_nt.QueryInformationProcess(process, ProcessBasicInformation,
281 &proc_info, sizeof(proc_info),
283 if (!NT_SUCCESS(ret) || sizeof(proc_info) != bytes_returned)
286 *process_id = proc_info.UniqueProcessId;
287 return STATUS_SUCCESS;
290 bool IsSameProcess(HANDLE process) {
291 if (NtCurrentProcess == process)
294 static ULONG s_process_id = 0;
297 NTSTATUS ret = GetProcessId(NtCurrentProcess, &s_process_id);
298 if (!NT_SUCCESS(ret))
303 NTSTATUS ret = GetProcessId(process, &process_id);
304 if (!NT_SUCCESS(ret))
307 return (process_id == s_process_id);
310 bool IsValidImageSection(HANDLE section, PVOID *base, PLARGE_INTEGER offset,
312 if (!section || !base || !view_size || offset)
315 HANDLE query_section;
317 NTSTATUS ret = g_nt.DuplicateObject(NtCurrentProcess, section,
318 NtCurrentProcess, &query_section,
319 SECTION_QUERY, 0, 0);
320 if (!NT_SUCCESS(ret))
323 SECTION_BASIC_INFORMATION basic_info;
324 SIZE_T bytes_returned;
325 ret = g_nt.QuerySection(query_section, SectionBasicInformation, &basic_info,
326 sizeof(basic_info), &bytes_returned);
328 VERIFY_SUCCESS(g_nt.Close(query_section));
330 if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned)
333 if (!(basic_info.Attributes & SEC_IMAGE))
339 UNICODE_STRING* AnsiToUnicode(const char* string) {
340 ANSI_STRING ansi_string;
341 ansi_string.Length = static_cast<USHORT>(g_nt.strlen(string));
342 ansi_string.MaximumLength = ansi_string.Length + 1;
343 ansi_string.Buffer = const_cast<char*>(string);
345 if (ansi_string.Length > ansi_string.MaximumLength)
348 size_t name_bytes = ansi_string.MaximumLength * sizeof(wchar_t) +
349 sizeof(UNICODE_STRING);
351 UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(
352 new(NT_ALLOC) char[name_bytes]);
356 out_string->MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t);
357 out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
359 BOOLEAN alloc_destination = FALSE;
360 NTSTATUS ret = g_nt.RtlAnsiStringToUnicodeString(out_string, &ansi_string,
362 DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret);
363 if (!NT_SUCCESS(ret)) {
364 operator delete(out_string, NT_ALLOC);
371 UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags) {
372 UNICODE_STRING* out_name = NULL;
376 base::win::PEImage pe(module);
378 if (!pe.VerifyMagic())
380 *flags |= MODULE_IS_PE_IMAGE;
382 PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
384 char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name));
385 out_name = AnsiToUnicode(name);
388 PIMAGE_NT_HEADERS headers = pe.GetNTHeaders();
390 if (headers->OptionalHeader.AddressOfEntryPoint)
391 *flags |= MODULE_HAS_ENTRY_POINT;
392 if (headers->OptionalHeader.SizeOfCode)
393 *flags |= MODULE_HAS_CODE;
396 } __except(EXCEPTION_EXECUTE_HANDLER) {
402 UNICODE_STRING* GetBackingFilePath(PVOID address) {
403 // We'll start with something close to max_path charactes for the name.
404 ULONG buffer_bytes = MAX_PATH * 2;
407 MEMORY_SECTION_NAME* section_name = reinterpret_cast<MEMORY_SECTION_NAME*>(
408 new(NT_ALLOC) char[buffer_bytes]);
413 ULONG returned_bytes;
414 NTSTATUS ret = g_nt.QueryVirtualMemory(NtCurrentProcess, address,
415 MemorySectionName, section_name,
416 buffer_bytes, &returned_bytes);
418 if (STATUS_BUFFER_OVERFLOW == ret) {
419 // Retry the call with the given buffer size.
420 operator delete(section_name, NT_ALLOC);
422 buffer_bytes = returned_bytes;
425 if (!NT_SUCCESS(ret)) {
426 operator delete(section_name, NT_ALLOC);
430 return reinterpret_cast<UNICODE_STRING*>(section_name);
434 UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) {
435 if ((!module_path) || (!module_path->Buffer))
439 int start_pos = module_path->Length / sizeof(wchar_t) - 1;
442 for (; ix >= 0; --ix) {
443 if (module_path->Buffer[ix] == L'\\') {
444 sep = &module_path->Buffer[ix];
449 // Ends with path separator. Not a valid module name.
450 if ((ix == start_pos) && sep)
453 // No path separator found. Use the entire name.
455 sep = &module_path->Buffer[-1];
458 // Add one to the size so we can null terminate the string.
459 size_t size_bytes = (start_pos - ix + 1) * sizeof(wchar_t);
461 // Based on the code above, size_bytes should always be small enough
462 // to make the static_cast below safe.
463 DCHECK_NT(kuint16max > size_bytes);
464 char* str_buffer = new(NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)];
468 UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(str_buffer);
469 out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
470 out_string->Length = static_cast<USHORT>(size_bytes - sizeof(wchar_t));
471 out_string->MaximumLength = static_cast<USHORT>(size_bytes);
473 NTSTATUS ret = CopyData(out_string->Buffer, &sep[1], out_string->Length);
474 if (!NT_SUCCESS(ret)) {
475 operator delete(out_string, NT_ALLOC);
479 out_string->Buffer[out_string->Length / sizeof(wchar_t)] = L'\0';
483 NTSTATUS AutoProtectMemory::ChangeProtection(void* address, size_t bytes,
485 DCHECK_NT(!changed_);
486 SIZE_T new_bytes = bytes;
487 NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address,
488 &new_bytes, protect, &old_protect_);
489 if (NT_SUCCESS(ret)) {
498 NTSTATUS AutoProtectMemory::RevertProtection() {
500 return STATUS_SUCCESS;
505 SIZE_T new_bytes = bytes_;
506 NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address_,
507 &new_bytes, old_protect_,
509 DCHECK_NT(NT_SUCCESS(ret));
519 bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length,
520 uint32 file_info_class) {
521 if (FileRenameInformation != file_info_class)
524 if (length < sizeof(FILE_RENAME_INFORMATION))
527 // Make sure file name length doesn't exceed the message length
528 if (length - offsetof(FILE_RENAME_INFORMATION, FileName) <
529 file_info->FileNameLength)
532 // We don't support a root directory.
533 if (file_info->RootDirectory)
536 // Check if it starts with \\??\\. We don't support relative paths.
537 if (file_info->FileNameLength < 4 || file_info->FileNameLength > kuint16max)
540 if (file_info->FileName[0] != L'\\' ||
541 file_info->FileName[1] != L'?' ||
542 file_info->FileName[2] != L'?' ||
543 file_info->FileName[3] != L'\\')
549 } // namespace sandbox
551 void* operator new(size_t size, sandbox::AllocationType type,
553 using namespace sandbox;
555 if (NT_ALLOC == type) {
559 // Use default flags for the allocation.
560 return g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size);
561 } else if (NT_PAGE == type) {
562 return AllocateNearTo(near_to, size);
568 void operator delete(void* memory, sandbox::AllocationType type) {
569 using namespace sandbox;
571 if (NT_ALLOC == type) {
572 // Use default flags.
573 VERIFY(g_nt.RtlFreeHeap(sandbox::g_heap, 0, memory));
574 } else if (NT_PAGE == type) {
577 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
584 void operator delete(void* memory, sandbox::AllocationType type,
586 UNREFERENCED_PARAMETER(near_to);
587 operator delete(memory, type);
590 void* __cdecl operator new(size_t size, void* buffer,
591 sandbox::AllocationType type) {
592 UNREFERENCED_PARAMETER(size);
593 UNREFERENCED_PARAMETER(type);
597 void __cdecl operator delete(void* memory, void* buffer,
598 sandbox::AllocationType type) {
599 UNREFERENCED_PARAMETER(memory);
600 UNREFERENCED_PARAMETER(buffer);
601 UNREFERENCED_PARAMETER(type);