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 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); 150 151 std::wstringstream 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 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; 164 165 DWORD reason = ::WaitForSingleObject(process_info.process_handle(), INFINITE); 166 167 DWORD code; 168 bool ok = 169 ::GetExitCodeProcess(process_info.process_handle(), &code) ? true : false; 170 171 if (WAIT_TIMEOUT == reason) 172 return false; 173 174 return ok && (0 == code); 175 } 176 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 } 184 185 for (;;) { 186 DWORD reason = ::WaitForSingleObject(dll_load_, INFINITE); 187 if (WAIT_TIMEOUT == reason || WAIT_ABANDONED == reason) 188 return false; 189 190 if (!::ResetEvent(dll_load_)) 191 return false; 192 193 bool found = NtdllPresent(); 194 if (found) { 195 if (::SuspendThread(child_->MainThread())) 196 return false; 197 } 198 199 if (!::SetEvent(continue_load_)) 200 return false; 201 202 if (found) 203 return true; 204 } 205 } 206 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 } 218 219 } // namespace sandbox 220