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/wow64.h"
      6 
      7 #include <sstream>
      8 
      9 #include "base/logging.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/win/scoped_process_information.h"
     12 #include "base/win/windows_version.h"
     13 #include "sandbox/win/src/target_process.h"
     14 
     15 namespace {
     16 
     17 // Holds the information needed for the interception of NtMapViewOfSection on
     18 // 64 bits.
     19 // Warning: do not modify this definition without changing also the code on the
     20 // 64 bit helper process.
     21 struct PatchInfo32 {
     22   HANDLE dll_load;  // Event to signal the broker.
     23   ULONG pad1;
     24   HANDLE continue_load;  // Event to wait for the broker.
     25   ULONG pad2;
     26   HANDLE section;  // First argument of the call.
     27   ULONG pad3;
     28   void* orig_MapViewOfSection;
     29   ULONG original_high;
     30   void* signal_and_wait;
     31   ULONG pad4;
     32   void* patch_location;
     33   ULONG patch_high;
     34 };
     35 
     36 // Size of the 64 bit service entry.
     37 const SIZE_T kServiceEntry64Size = 0x10;
     38 
     39 // Removes the interception of ntdll64.
     40 bool Restore64Code(HANDLE child, PatchInfo32* patch_info) {
     41   PatchInfo32 local_patch_info;
     42   SIZE_T actual;
     43   if (!::ReadProcessMemory(child, patch_info, &local_patch_info,
     44                            sizeof(local_patch_info), &actual))
     45     return false;
     46   if (sizeof(local_patch_info) != actual)
     47     return false;
     48 
     49   if (local_patch_info.original_high)
     50     return false;
     51   if (local_patch_info.patch_high)
     52     return false;
     53 
     54   char buffer[kServiceEntry64Size];
     55 
     56   if (!::ReadProcessMemory(child, local_patch_info.orig_MapViewOfSection,
     57                            &buffer, kServiceEntry64Size, &actual))
     58     return false;
     59   if (kServiceEntry64Size != actual)
     60     return false;
     61 
     62   if (!::WriteProcessMemory(child, local_patch_info.patch_location, &buffer,
     63                             kServiceEntry64Size, &actual))
     64     return false;
     65   if (kServiceEntry64Size != actual)
     66     return false;
     67   return true;
     68 }
     69 
     70 typedef BOOL (WINAPI* IsWow64ProcessFunction)(HANDLE process, BOOL* wow64);
     71 
     72 }  // namespace
     73 
     74 namespace sandbox {
     75 
     76 Wow64::~Wow64() {
     77   if (dll_load_)
     78     ::CloseHandle(dll_load_);
     79 
     80   if (continue_load_)
     81     ::CloseHandle(continue_load_);
     82 }
     83 
     84 // The basic idea is to allocate one page of memory on the child, and initialize
     85 // the first part of it with our version of PatchInfo32. Then launch the helper
     86 // process passing it that address on the child. The helper process will patch
     87 // the 64 bit version of NtMapViewOfFile, and the interception will signal the
     88 // first event on the buffer. We'll be waiting on that event and after the 32
     89 // bit version of ntdll is loaded, we'll remove the interception and return to
     90 // our caller.
     91 bool Wow64::WaitForNtdll() {
     92   if (base::win::OSInfo::GetInstance()->wow64_status() !=
     93       base::win::OSInfo::WOW64_ENABLED)
     94     return true;
     95 
     96   const size_t page_size = 4096;
     97 
     98   // Create some default manual reset un-named events, not signaled.
     99   dll_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    100   continue_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    101   HANDLE current_process = ::GetCurrentProcess();
    102   HANDLE remote_load, remote_continue;
    103   DWORD access = EVENT_MODIFY_STATE | SYNCHRONIZE;
    104   if (!::DuplicateHandle(current_process, dll_load_, child_->Process(),
    105                          &remote_load, access, FALSE, 0))
    106     return false;
    107   if (!::DuplicateHandle(current_process, continue_load_, child_->Process(),
    108                          &remote_continue, access, FALSE, 0))
    109     return false;
    110 
    111   void* buffer = ::VirtualAllocEx(child_->Process(), NULL, page_size,
    112                                   MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    113   DCHECK(buffer);
    114   if (!buffer)
    115     return false;
    116 
    117   PatchInfo32* patch_info = reinterpret_cast<PatchInfo32*>(buffer);
    118   PatchInfo32 local_patch_info = {0};
    119   local_patch_info.dll_load = remote_load;
    120   local_patch_info.continue_load = remote_continue;
    121   SIZE_T written;
    122   if (!::WriteProcessMemory(child_->Process(), patch_info, &local_patch_info,
    123                             offsetof(PatchInfo32, section), &written))
    124     return false;
    125   if (offsetof(PatchInfo32, section) != written)
    126     return false;
    127 
    128   if (!RunWowHelper(buffer))
    129     return false;
    130 
    131   // The child is intercepted on 64 bit, go on and wait for our event.
    132   if (!DllMapped())
    133     return false;
    134 
    135   // The 32 bit version is available, cleanup the child.
    136   return Restore64Code(child_->Process(), patch_info);
    137 }
    138 
    139 bool Wow64::RunWowHelper(void* buffer) {
    140   COMPILE_ASSERT(sizeof(buffer) <= sizeof(DWORD), unsupported_64_bits);
    141 
    142   // Get the path to the helper (beside the exe).
    143   wchar_t prog_name[MAX_PATH];
    144   GetModuleFileNameW(NULL, prog_name, MAX_PATH);
    145   base::string16 path(prog_name);
    146   size_t name_pos = path.find_last_of(L"\\");
    147   if (base::string16::npos == name_pos)
    148     return false;
    149   path.resize(name_pos + 1);
    150 
    151   std::basic_stringstream<base::char16> command;
    152   command << std::hex << std::showbase << L"\"" << path <<
    153                L"wow_helper.exe\" " << child_->ProcessId() << " " <<
    154                bit_cast<ULONG>(buffer);
    155 
    156   scoped_ptr<wchar_t, base::FreeDeleter>
    157       writable_command(_wcsdup(command.str().c_str()));
    158 
    159   STARTUPINFO startup_info = {0};
    160   startup_info.cb = sizeof(startup_info);
    161   PROCESS_INFORMATION temp_process_info = {};
    162   if (!::CreateProcess(NULL, writable_command.get(), NULL, NULL, FALSE, 0, NULL,
    163                        NULL, &startup_info, &temp_process_info))
    164     return false;
    165   base::win::ScopedProcessInformation process_info(temp_process_info);
    166 
    167   DWORD reason = ::WaitForSingleObject(process_info.process_handle(), INFINITE);
    168 
    169   DWORD code;
    170   bool ok =
    171       ::GetExitCodeProcess(process_info.process_handle(), &code) ? true : false;
    172 
    173   if (WAIT_TIMEOUT == reason)
    174     return false;
    175 
    176   return ok && (0 == code);
    177 }
    178 
    179 // First we must wake up the child, then wait for dll loads on the child until
    180 // the one we care is loaded; at that point we must suspend the child again.
    181 bool Wow64::DllMapped() {
    182   if (1 != ::ResumeThread(child_->MainThread())) {
    183     NOTREACHED();
    184     return false;
    185   }
    186 
    187   for (;;) {
    188     DWORD reason = ::WaitForSingleObject(dll_load_, INFINITE);
    189     if (WAIT_TIMEOUT == reason || WAIT_ABANDONED == reason)
    190       return false;
    191 
    192     if (!::ResetEvent(dll_load_))
    193       return false;
    194 
    195     bool found = NtdllPresent();
    196     if (found) {
    197       if (::SuspendThread(child_->MainThread()))
    198         return false;
    199     }
    200 
    201     if (!::SetEvent(continue_load_))
    202       return false;
    203 
    204     if (found)
    205       return true;
    206   }
    207 }
    208 
    209 bool Wow64::NtdllPresent() {
    210   const size_t kBufferSize = 512;
    211   char buffer[kBufferSize];
    212   SIZE_T read;
    213   if (!::ReadProcessMemory(child_->Process(), ntdll_, &buffer, kBufferSize,
    214                            &read))
    215     return false;
    216   if (kBufferSize != read)
    217     return false;
    218   return true;
    219 }
    220 
    221 }  // namespace sandbox
    222