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 15namespace { 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. 21struct 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. 37const SIZE_T kServiceEntry64Size = 0x10; 38 39// Removes the interception of ntdll64. 40bool 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 70typedef BOOL (WINAPI* IsWow64ProcessFunction)(HANDLE process, BOOL* wow64); 71 72} // namespace 73 74namespace sandbox { 75 76Wow64::~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. 91bool 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 139bool 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_malloc<wchar_t> writable_command(_wcsdup(command.str().c_str())); 157 158 STARTUPINFO startup_info = {0}; 159 startup_info.cb = sizeof(startup_info); 160 PROCESS_INFORMATION temp_process_info = {}; 161 if (!::CreateProcess(NULL, writable_command.get(), NULL, NULL, FALSE, 0, NULL, 162 NULL, &startup_info, &temp_process_info)) 163 return false; 164 base::win::ScopedProcessInformation process_info(temp_process_info); 165 166 DWORD reason = ::WaitForSingleObject(process_info.process_handle(), INFINITE); 167 168 DWORD code; 169 bool ok = 170 ::GetExitCodeProcess(process_info.process_handle(), &code) ? true : false; 171 172 if (WAIT_TIMEOUT == reason) 173 return false; 174 175 return ok && (0 == code); 176} 177 178// First we must wake up the child, then wait for dll loads on the child until 179// the one we care is loaded; at that point we must suspend the child again. 180bool Wow64::DllMapped() { 181 if (1 != ::ResumeThread(child_->MainThread())) { 182 NOTREACHED(); 183 return false; 184 } 185 186 for (;;) { 187 DWORD reason = ::WaitForSingleObject(dll_load_, INFINITE); 188 if (WAIT_TIMEOUT == reason || WAIT_ABANDONED == reason) 189 return false; 190 191 if (!::ResetEvent(dll_load_)) 192 return false; 193 194 bool found = NtdllPresent(); 195 if (found) { 196 if (::SuspendThread(child_->MainThread())) 197 return false; 198 } 199 200 if (!::SetEvent(continue_load_)) 201 return false; 202 203 if (found) 204 return true; 205 } 206} 207 208bool Wow64::NtdllPresent() { 209 const size_t kBufferSize = 512; 210 char buffer[kBufferSize]; 211 SIZE_T read; 212 if (!::ReadProcessMemory(child_->Process(), ntdll_, &buffer, kBufferSize, 213 &read)) 214 return false; 215 if (kBufferSize != read) 216 return false; 217 return true; 218} 219 220} // namespace sandbox 221