Home | History | Annotate | Download | only in src
      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