sandbox_nt_util.cc 22 KB

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