blob: 60c63fd7b98d86b39b70e63d56f945c531c230a0 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sandbox/src/wow64.h"
#include <sstream>
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/win/windows_version.h"
#include "sandbox/src/target_process.h"
namespace {
// Holds the information needed for the interception of NtMapViewOfSection on
// 64 bits.
// Warning: do not modify this definition without changing also the code on the
// 64 bit helper process.
struct PatchInfo32 {
HANDLE dll_load; // Event to signal the broker.
ULONG pad1;
HANDLE continue_load; // Event to wait for the broker.
ULONG pad2;
HANDLE section; // First argument of the call.
ULONG pad3;
void* orig_MapViewOfSection;
ULONG original_high;
void* signal_and_wait;
ULONG pad4;
void* patch_location;
ULONG patch_high;
// Size of the 64 bit service entry.
const SIZE_T kServiceEntry64Size = 0x10;
// Removes the interception of ntdll64.
bool Restore64Code(HANDLE child, PatchInfo32* patch_info) {
PatchInfo32 local_patch_info;
SIZE_T actual;
if (!::ReadProcessMemory(child, patch_info, &local_patch_info,
sizeof(local_patch_info), &actual))
return false;
if (sizeof(local_patch_info) != actual)
return false;
if (local_patch_info.original_high)
return false;
if (local_patch_info.patch_high)
return false;
char buffer[kServiceEntry64Size];
if (!::ReadProcessMemory(child, local_patch_info.orig_MapViewOfSection,
&buffer, kServiceEntry64Size, &actual))
return false;
if (kServiceEntry64Size != actual)
return false;
if (!::WriteProcessMemory(child, local_patch_info.patch_location, &buffer,
kServiceEntry64Size, &actual))
return false;
if (kServiceEntry64Size != actual)
return false;
return true;
typedef BOOL (WINAPI* IsWow64ProcessFunction)(HANDLE process, BOOL* wow64);
} // namespace
namespace sandbox {
Wow64::~Wow64() {
if (dll_load_)
if (continue_load_)
// The basic idea is to allocate one page of memory on the child, and initialize
// the first part of it with our version of PatchInfo32. Then launch the helper
// process passing it that address on the child. The helper process will patch
// the 64 bit version of NtMapViewOfFile, and the interception will signal the
// first event on the buffer. We'll be waiting on that event and after the 32
// bit version of ntdll is loaded, we'll remove the interception and return to
// our caller.
bool Wow64::WaitForNtdll() {
if (base::win::OSInfo::GetInstance()->wow64_status() !=
return true;
const size_t page_size = 4096;
// Create some default manual reset un-named events, not signaled.
dll_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
continue_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
HANDLE current_process = ::GetCurrentProcess();
HANDLE remote_load, remote_continue;
if (!::DuplicateHandle(current_process, dll_load_, child_->Process(),
&remote_load, access, FALSE, 0))
return false;
if (!::DuplicateHandle(current_process, continue_load_, child_->Process(),
&remote_continue, access, FALSE, 0))
return false;
void* buffer = ::VirtualAllocEx(child_->Process(), NULL, page_size,
if (!buffer)
return false;
PatchInfo32* patch_info = reinterpret_cast<PatchInfo32*>(buffer);
PatchInfo32 local_patch_info = {0};
local_patch_info.dll_load = remote_load;
local_patch_info.continue_load = remote_continue;
SIZE_T written;
if (!::WriteProcessMemory(child_->Process(), patch_info, &local_patch_info,
offsetof(PatchInfo32, section), &written))
return false;
if (offsetof(PatchInfo32, section) != written)
return false;
if (!RunWowHelper(buffer))
return false;
// The child is intercepted on 64 bit, go on and wait for our event.
if (!DllMapped())
return false;
// The 32 bit version is available, cleanup the child.
return Restore64Code(child_->Process(), patch_info);
bool Wow64::RunWowHelper(void* buffer) {
COMPILE_ASSERT(sizeof(buffer) <= sizeof DWORD, unsupported_64_bits);
// Get the path to the helper (beside the exe).
wchar_t prog_name[MAX_PATH];
GetModuleFileNameW(NULL, prog_name, MAX_PATH);
std::wstring path(prog_name);
size_t name_pos = path.find_last_of(L"\\");
if (std::wstring::npos == name_pos)
return false;
path.resize(name_pos + 1);
std::wstringstream command;
command << std::hex << std::showbase << L"\"" << path <<
L"wow_helper.exe\" " << child_->ProcessId() << " " <<
scoped_ptr_malloc<wchar_t> writable_command(_wcsdup(command.str().c_str()));
STARTUPINFO startup_info = {0};
startup_info.cb = sizeof(startup_info);
if (!::CreateProcess(NULL, writable_command.get(), NULL, NULL, FALSE, 0, NULL,
NULL, &startup_info, &process_info))
return false;
DWORD reason = ::WaitForSingleObject(process_info.hProcess, INFINITE);
DWORD code;
bool ok = ::GetExitCodeProcess(process_info.hProcess, &code) ? true : false;
if (WAIT_TIMEOUT == reason)
return false;
return ok && (0 == code);
// First we must wake up the child, then wait for dll loads on the child until
// the one we care is loaded; at that point we must suspend the child again.
bool Wow64::DllMapped() {
if (1 != ::ResumeThread(child_->MainThread())) {
return false;
for (;;) {
DWORD reason = ::WaitForSingleObject(dll_load_, INFINITE);
if (WAIT_TIMEOUT == reason || WAIT_ABANDONED == reason)
return false;
if (!::ResetEvent(dll_load_))
return false;
bool found = NtdllPresent();
if (found) {
if (::SuspendThread(child_->MainThread()))
return false;
if (!::SetEvent(continue_load_))
return false;
if (found)
return true;
bool Wow64::NtdllPresent() {
const size_t kBufferSize = 512;
char buffer[kBufferSize];
SIZE_T read;
if (!::ReadProcessMemory(child_->Process(), ntdll_, &buffer, kBufferSize,
return false;
if (kBufferSize != read)
return false;
return true;
} // namespace sandbox