security/sandbox/win/src/Wow64.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/wow64.h"
     7 #include <sstream>
     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"
    15 namespace {
    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 };
    36 // Size of the 64 bit service entry.
    37 const SIZE_T kServiceEntry64Size = 0x10;
    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;
    49   if (local_patch_info.original_high)
    50     return false;
    51   if (local_patch_info.patch_high)
    52     return false;
    54   char buffer[kServiceEntry64Size];
    56   if (!::ReadProcessMemory(child, local_patch_info.orig_MapViewOfSection,
    57                            &buffer, kServiceEntry64Size, &actual))
    58     return false;
    59   if (kServiceEntry64Size != actual)
    60     return false;
    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 }
    70 typedef BOOL (WINAPI* IsWow64ProcessFunction)(HANDLE process, BOOL* wow64);
    72 }  // namespace
    74 namespace sandbox {
    76 Wow64::~Wow64() {
    77   if (dll_load_)
    78     ::CloseHandle(dll_load_);
    80   if (continue_load_)
    81     ::CloseHandle(continue_load_);
    82 }
    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;
    96   const size_t page_size = 4096;
    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;
   111   void* buffer = ::VirtualAllocEx(child_->Process(), NULL, page_size,
   112                                   MEM_COMMIT, PAGE_EXECUTE_READWRITE);
   113   DCHECK(buffer);
   114   if (!buffer)
   115     return false;
   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;
   128   if (!RunWowHelper(buffer))
   129     return false;
   131   // The child is intercepted on 64 bit, go on and wait for our event.
   132   if (!DllMapped())
   133     return false;
   135   // The 32 bit version is available, cleanup the child.
   136   return Restore64Code(child_->Process(), patch_info);
   137 }
   139 bool Wow64::RunWowHelper(void* buffer) {
   140   COMPILE_ASSERT(sizeof(buffer) <= sizeof(DWORD), unsupported_64_bits);
   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   std::wstring path(prog_name);
   146   size_t name_pos = path.find_last_of(L"\\");
   147   if (std::wstring::npos == name_pos)
   148     return false;
   149   path.resize(name_pos + 1);
   151   std::wstringstream command;
   152   command << std::hex << std::showbase << L"\"" << path <<
   153                L"wow_helper.exe\" " << child_->ProcessId() << " " <<
   154                bit_cast<ULONG>(buffer);
   156   scoped_ptr_malloc<wchar_t> writable_command(_wcsdup(command.str().c_str()));
   158   STARTUPINFO startup_info = {0};
   159   startup_info.cb = sizeof(startup_info);
   160   base::win::ScopedProcessInformation process_info;
   161   if (!::CreateProcess(NULL, writable_command.get(), NULL, NULL, FALSE, 0, NULL,
   162                        NULL, &startup_info, process_info.Receive()))
   163     return false;
   165   DWORD reason = ::WaitForSingleObject(process_info.process_handle(), INFINITE);
   167   DWORD code;
   168   bool ok =
   169       ::GetExitCodeProcess(process_info.process_handle(), &code) ? true : false;
   171   if (WAIT_TIMEOUT == reason)
   172     return false;
   174   return ok && (0 == code);
   175 }
   177 // First we must wake up the child, then wait for dll loads on the child until
   178 // the one we care is loaded; at that point we must suspend the child again.
   179 bool Wow64::DllMapped() {
   180   if (1 != ::ResumeThread(child_->MainThread())) {
   181     NOTREACHED();
   182     return false;
   183   }
   185   for (;;) {
   186     DWORD reason = ::WaitForSingleObject(dll_load_, INFINITE);
   187     if (WAIT_TIMEOUT == reason || WAIT_ABANDONED == reason)
   188       return false;
   190     if (!::ResetEvent(dll_load_))
   191       return false;
   193     bool found = NtdllPresent();
   194     if (found) {
   195       if (::SuspendThread(child_->MainThread()))
   196         return false;
   197     }
   199     if (!::SetEvent(continue_load_))
   200       return false;
   202     if (found)
   203       return true;
   204   }
   205 }
   207 bool Wow64::NtdllPresent() {
   208   const size_t kBufferSize = 512;
   209   char buffer[kBufferSize];
   210   SIZE_T read;
   211   if (!::ReadProcessMemory(child_->Process(), ntdll_, &buffer, kBufferSize,
   212                            &read))
   213     return false;
   214   if (kBufferSize != read)
   215     return false;
   216   return true;
   217 }
   219 }  // namespace sandbox

mercurial