security/sandbox/win/src/Wow64.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/sandbox/win/src/Wow64.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,219 @@
     1.4 +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +#include "sandbox/win/src/wow64.h"
     1.9 +
    1.10 +#include <sstream>
    1.11 +
    1.12 +#include "base/logging.h"
    1.13 +#include "base/memory/scoped_ptr.h"
    1.14 +#include "base/win/scoped_process_information.h"
    1.15 +#include "base/win/windows_version.h"
    1.16 +#include "sandbox/win/src/target_process.h"
    1.17 +
    1.18 +namespace {
    1.19 +
    1.20 +// Holds the information needed for the interception of NtMapViewOfSection on
    1.21 +// 64 bits.
    1.22 +// Warning: do not modify this definition without changing also the code on the
    1.23 +// 64 bit helper process.
    1.24 +struct PatchInfo32 {
    1.25 +  HANDLE dll_load;  // Event to signal the broker.
    1.26 +  ULONG pad1;
    1.27 +  HANDLE continue_load;  // Event to wait for the broker.
    1.28 +  ULONG pad2;
    1.29 +  HANDLE section;  // First argument of the call.
    1.30 +  ULONG pad3;
    1.31 +  void* orig_MapViewOfSection;
    1.32 +  ULONG original_high;
    1.33 +  void* signal_and_wait;
    1.34 +  ULONG pad4;
    1.35 +  void* patch_location;
    1.36 +  ULONG patch_high;
    1.37 +};
    1.38 +
    1.39 +// Size of the 64 bit service entry.
    1.40 +const SIZE_T kServiceEntry64Size = 0x10;
    1.41 +
    1.42 +// Removes the interception of ntdll64.
    1.43 +bool Restore64Code(HANDLE child, PatchInfo32* patch_info) {
    1.44 +  PatchInfo32 local_patch_info;
    1.45 +  SIZE_T actual;
    1.46 +  if (!::ReadProcessMemory(child, patch_info, &local_patch_info,
    1.47 +                           sizeof(local_patch_info), &actual))
    1.48 +    return false;
    1.49 +  if (sizeof(local_patch_info) != actual)
    1.50 +    return false;
    1.51 +
    1.52 +  if (local_patch_info.original_high)
    1.53 +    return false;
    1.54 +  if (local_patch_info.patch_high)
    1.55 +    return false;
    1.56 +
    1.57 +  char buffer[kServiceEntry64Size];
    1.58 +
    1.59 +  if (!::ReadProcessMemory(child, local_patch_info.orig_MapViewOfSection,
    1.60 +                           &buffer, kServiceEntry64Size, &actual))
    1.61 +    return false;
    1.62 +  if (kServiceEntry64Size != actual)
    1.63 +    return false;
    1.64 +
    1.65 +  if (!::WriteProcessMemory(child, local_patch_info.patch_location, &buffer,
    1.66 +                            kServiceEntry64Size, &actual))
    1.67 +    return false;
    1.68 +  if (kServiceEntry64Size != actual)
    1.69 +    return false;
    1.70 +  return true;
    1.71 +}
    1.72 +
    1.73 +typedef BOOL (WINAPI* IsWow64ProcessFunction)(HANDLE process, BOOL* wow64);
    1.74 +
    1.75 +}  // namespace
    1.76 +
    1.77 +namespace sandbox {
    1.78 +
    1.79 +Wow64::~Wow64() {
    1.80 +  if (dll_load_)
    1.81 +    ::CloseHandle(dll_load_);
    1.82 +
    1.83 +  if (continue_load_)
    1.84 +    ::CloseHandle(continue_load_);
    1.85 +}
    1.86 +
    1.87 +// The basic idea is to allocate one page of memory on the child, and initialize
    1.88 +// the first part of it with our version of PatchInfo32. Then launch the helper
    1.89 +// process passing it that address on the child. The helper process will patch
    1.90 +// the 64 bit version of NtMapViewOfFile, and the interception will signal the
    1.91 +// first event on the buffer. We'll be waiting on that event and after the 32
    1.92 +// bit version of ntdll is loaded, we'll remove the interception and return to
    1.93 +// our caller.
    1.94 +bool Wow64::WaitForNtdll() {
    1.95 +  if (base::win::OSInfo::GetInstance()->wow64_status() !=
    1.96 +      base::win::OSInfo::WOW64_ENABLED)
    1.97 +    return true;
    1.98 +
    1.99 +  const size_t page_size = 4096;
   1.100 +
   1.101 +  // Create some default manual reset un-named events, not signaled.
   1.102 +  dll_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
   1.103 +  continue_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
   1.104 +  HANDLE current_process = ::GetCurrentProcess();
   1.105 +  HANDLE remote_load, remote_continue;
   1.106 +  DWORD access = EVENT_MODIFY_STATE | SYNCHRONIZE;
   1.107 +  if (!::DuplicateHandle(current_process, dll_load_, child_->Process(),
   1.108 +                         &remote_load, access, FALSE, 0))
   1.109 +    return false;
   1.110 +  if (!::DuplicateHandle(current_process, continue_load_, child_->Process(),
   1.111 +                         &remote_continue, access, FALSE, 0))
   1.112 +    return false;
   1.113 +
   1.114 +  void* buffer = ::VirtualAllocEx(child_->Process(), NULL, page_size,
   1.115 +                                  MEM_COMMIT, PAGE_EXECUTE_READWRITE);
   1.116 +  DCHECK(buffer);
   1.117 +  if (!buffer)
   1.118 +    return false;
   1.119 +
   1.120 +  PatchInfo32* patch_info = reinterpret_cast<PatchInfo32*>(buffer);
   1.121 +  PatchInfo32 local_patch_info = {0};
   1.122 +  local_patch_info.dll_load = remote_load;
   1.123 +  local_patch_info.continue_load = remote_continue;
   1.124 +  SIZE_T written;
   1.125 +  if (!::WriteProcessMemory(child_->Process(), patch_info, &local_patch_info,
   1.126 +                            offsetof(PatchInfo32, section), &written))
   1.127 +    return false;
   1.128 +  if (offsetof(PatchInfo32, section) != written)
   1.129 +    return false;
   1.130 +
   1.131 +  if (!RunWowHelper(buffer))
   1.132 +    return false;
   1.133 +
   1.134 +  // The child is intercepted on 64 bit, go on and wait for our event.
   1.135 +  if (!DllMapped())
   1.136 +    return false;
   1.137 +
   1.138 +  // The 32 bit version is available, cleanup the child.
   1.139 +  return Restore64Code(child_->Process(), patch_info);
   1.140 +}
   1.141 +
   1.142 +bool Wow64::RunWowHelper(void* buffer) {
   1.143 +  COMPILE_ASSERT(sizeof(buffer) <= sizeof(DWORD), unsupported_64_bits);
   1.144 +
   1.145 +  // Get the path to the helper (beside the exe).
   1.146 +  wchar_t prog_name[MAX_PATH];
   1.147 +  GetModuleFileNameW(NULL, prog_name, MAX_PATH);
   1.148 +  std::wstring path(prog_name);
   1.149 +  size_t name_pos = path.find_last_of(L"\\");
   1.150 +  if (std::wstring::npos == name_pos)
   1.151 +    return false;
   1.152 +  path.resize(name_pos + 1);
   1.153 +
   1.154 +  std::wstringstream command;
   1.155 +  command << std::hex << std::showbase << L"\"" << path <<
   1.156 +               L"wow_helper.exe\" " << child_->ProcessId() << " " <<
   1.157 +               bit_cast<ULONG>(buffer);
   1.158 +
   1.159 +  scoped_ptr_malloc<wchar_t> writable_command(_wcsdup(command.str().c_str()));
   1.160 +
   1.161 +  STARTUPINFO startup_info = {0};
   1.162 +  startup_info.cb = sizeof(startup_info);
   1.163 +  base::win::ScopedProcessInformation process_info;
   1.164 +  if (!::CreateProcess(NULL, writable_command.get(), NULL, NULL, FALSE, 0, NULL,
   1.165 +                       NULL, &startup_info, process_info.Receive()))
   1.166 +    return false;
   1.167 +
   1.168 +  DWORD reason = ::WaitForSingleObject(process_info.process_handle(), INFINITE);
   1.169 +
   1.170 +  DWORD code;
   1.171 +  bool ok =
   1.172 +      ::GetExitCodeProcess(process_info.process_handle(), &code) ? true : false;
   1.173 +
   1.174 +  if (WAIT_TIMEOUT == reason)
   1.175 +    return false;
   1.176 +
   1.177 +  return ok && (0 == code);
   1.178 +}
   1.179 +
   1.180 +// First we must wake up the child, then wait for dll loads on the child until
   1.181 +// the one we care is loaded; at that point we must suspend the child again.
   1.182 +bool Wow64::DllMapped() {
   1.183 +  if (1 != ::ResumeThread(child_->MainThread())) {
   1.184 +    NOTREACHED();
   1.185 +    return false;
   1.186 +  }
   1.187 +
   1.188 +  for (;;) {
   1.189 +    DWORD reason = ::WaitForSingleObject(dll_load_, INFINITE);
   1.190 +    if (WAIT_TIMEOUT == reason || WAIT_ABANDONED == reason)
   1.191 +      return false;
   1.192 +
   1.193 +    if (!::ResetEvent(dll_load_))
   1.194 +      return false;
   1.195 +
   1.196 +    bool found = NtdllPresent();
   1.197 +    if (found) {
   1.198 +      if (::SuspendThread(child_->MainThread()))
   1.199 +        return false;
   1.200 +    }
   1.201 +
   1.202 +    if (!::SetEvent(continue_load_))
   1.203 +      return false;
   1.204 +
   1.205 +    if (found)
   1.206 +      return true;
   1.207 +  }
   1.208 +}
   1.209 +
   1.210 +bool Wow64::NtdllPresent() {
   1.211 +  const size_t kBufferSize = 512;
   1.212 +  char buffer[kBufferSize];
   1.213 +  SIZE_T read;
   1.214 +  if (!::ReadProcessMemory(child_->Process(), ntdll_, &buffer, kBufferSize,
   1.215 +                           &read))
   1.216 +    return false;
   1.217 +  if (kBufferSize != read)
   1.218 +    return false;
   1.219 +  return true;
   1.220 +}
   1.221 +
   1.222 +}  // namespace sandbox

mercurial