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