Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
michael@0 | 2 | // Use of this source code is governed by a BSD-style license that can be |
michael@0 | 3 | // found in the LICENSE file. |
michael@0 | 4 | |
michael@0 | 5 | #include "sandbox/win/src/wow64.h" |
michael@0 | 6 | |
michael@0 | 7 | #include <sstream> |
michael@0 | 8 | |
michael@0 | 9 | #include "base/logging.h" |
michael@0 | 10 | #include "base/memory/scoped_ptr.h" |
michael@0 | 11 | #include "base/win/scoped_process_information.h" |
michael@0 | 12 | #include "base/win/windows_version.h" |
michael@0 | 13 | #include "sandbox/win/src/target_process.h" |
michael@0 | 14 | |
michael@0 | 15 | namespace { |
michael@0 | 16 | |
michael@0 | 17 | // Holds the information needed for the interception of NtMapViewOfSection on |
michael@0 | 18 | // 64 bits. |
michael@0 | 19 | // Warning: do not modify this definition without changing also the code on the |
michael@0 | 20 | // 64 bit helper process. |
michael@0 | 21 | struct PatchInfo32 { |
michael@0 | 22 | HANDLE dll_load; // Event to signal the broker. |
michael@0 | 23 | ULONG pad1; |
michael@0 | 24 | HANDLE continue_load; // Event to wait for the broker. |
michael@0 | 25 | ULONG pad2; |
michael@0 | 26 | HANDLE section; // First argument of the call. |
michael@0 | 27 | ULONG pad3; |
michael@0 | 28 | void* orig_MapViewOfSection; |
michael@0 | 29 | ULONG original_high; |
michael@0 | 30 | void* signal_and_wait; |
michael@0 | 31 | ULONG pad4; |
michael@0 | 32 | void* patch_location; |
michael@0 | 33 | ULONG patch_high; |
michael@0 | 34 | }; |
michael@0 | 35 | |
michael@0 | 36 | // Size of the 64 bit service entry. |
michael@0 | 37 | const SIZE_T kServiceEntry64Size = 0x10; |
michael@0 | 38 | |
michael@0 | 39 | // Removes the interception of ntdll64. |
michael@0 | 40 | bool Restore64Code(HANDLE child, PatchInfo32* patch_info) { |
michael@0 | 41 | PatchInfo32 local_patch_info; |
michael@0 | 42 | SIZE_T actual; |
michael@0 | 43 | if (!::ReadProcessMemory(child, patch_info, &local_patch_info, |
michael@0 | 44 | sizeof(local_patch_info), &actual)) |
michael@0 | 45 | return false; |
michael@0 | 46 | if (sizeof(local_patch_info) != actual) |
michael@0 | 47 | return false; |
michael@0 | 48 | |
michael@0 | 49 | if (local_patch_info.original_high) |
michael@0 | 50 | return false; |
michael@0 | 51 | if (local_patch_info.patch_high) |
michael@0 | 52 | return false; |
michael@0 | 53 | |
michael@0 | 54 | char buffer[kServiceEntry64Size]; |
michael@0 | 55 | |
michael@0 | 56 | if (!::ReadProcessMemory(child, local_patch_info.orig_MapViewOfSection, |
michael@0 | 57 | &buffer, kServiceEntry64Size, &actual)) |
michael@0 | 58 | return false; |
michael@0 | 59 | if (kServiceEntry64Size != actual) |
michael@0 | 60 | return false; |
michael@0 | 61 | |
michael@0 | 62 | if (!::WriteProcessMemory(child, local_patch_info.patch_location, &buffer, |
michael@0 | 63 | kServiceEntry64Size, &actual)) |
michael@0 | 64 | return false; |
michael@0 | 65 | if (kServiceEntry64Size != actual) |
michael@0 | 66 | return false; |
michael@0 | 67 | return true; |
michael@0 | 68 | } |
michael@0 | 69 | |
michael@0 | 70 | typedef BOOL (WINAPI* IsWow64ProcessFunction)(HANDLE process, BOOL* wow64); |
michael@0 | 71 | |
michael@0 | 72 | } // namespace |
michael@0 | 73 | |
michael@0 | 74 | namespace sandbox { |
michael@0 | 75 | |
michael@0 | 76 | Wow64::~Wow64() { |
michael@0 | 77 | if (dll_load_) |
michael@0 | 78 | ::CloseHandle(dll_load_); |
michael@0 | 79 | |
michael@0 | 80 | if (continue_load_) |
michael@0 | 81 | ::CloseHandle(continue_load_); |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | // The basic idea is to allocate one page of memory on the child, and initialize |
michael@0 | 85 | // the first part of it with our version of PatchInfo32. Then launch the helper |
michael@0 | 86 | // process passing it that address on the child. The helper process will patch |
michael@0 | 87 | // the 64 bit version of NtMapViewOfFile, and the interception will signal the |
michael@0 | 88 | // first event on the buffer. We'll be waiting on that event and after the 32 |
michael@0 | 89 | // bit version of ntdll is loaded, we'll remove the interception and return to |
michael@0 | 90 | // our caller. |
michael@0 | 91 | bool Wow64::WaitForNtdll() { |
michael@0 | 92 | if (base::win::OSInfo::GetInstance()->wow64_status() != |
michael@0 | 93 | base::win::OSInfo::WOW64_ENABLED) |
michael@0 | 94 | return true; |
michael@0 | 95 | |
michael@0 | 96 | const size_t page_size = 4096; |
michael@0 | 97 | |
michael@0 | 98 | // Create some default manual reset un-named events, not signaled. |
michael@0 | 99 | dll_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL); |
michael@0 | 100 | continue_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL); |
michael@0 | 101 | HANDLE current_process = ::GetCurrentProcess(); |
michael@0 | 102 | HANDLE remote_load, remote_continue; |
michael@0 | 103 | DWORD access = EVENT_MODIFY_STATE | SYNCHRONIZE; |
michael@0 | 104 | if (!::DuplicateHandle(current_process, dll_load_, child_->Process(), |
michael@0 | 105 | &remote_load, access, FALSE, 0)) |
michael@0 | 106 | return false; |
michael@0 | 107 | if (!::DuplicateHandle(current_process, continue_load_, child_->Process(), |
michael@0 | 108 | &remote_continue, access, FALSE, 0)) |
michael@0 | 109 | return false; |
michael@0 | 110 | |
michael@0 | 111 | void* buffer = ::VirtualAllocEx(child_->Process(), NULL, page_size, |
michael@0 | 112 | MEM_COMMIT, PAGE_EXECUTE_READWRITE); |
michael@0 | 113 | DCHECK(buffer); |
michael@0 | 114 | if (!buffer) |
michael@0 | 115 | return false; |
michael@0 | 116 | |
michael@0 | 117 | PatchInfo32* patch_info = reinterpret_cast<PatchInfo32*>(buffer); |
michael@0 | 118 | PatchInfo32 local_patch_info = {0}; |
michael@0 | 119 | local_patch_info.dll_load = remote_load; |
michael@0 | 120 | local_patch_info.continue_load = remote_continue; |
michael@0 | 121 | SIZE_T written; |
michael@0 | 122 | if (!::WriteProcessMemory(child_->Process(), patch_info, &local_patch_info, |
michael@0 | 123 | offsetof(PatchInfo32, section), &written)) |
michael@0 | 124 | return false; |
michael@0 | 125 | if (offsetof(PatchInfo32, section) != written) |
michael@0 | 126 | return false; |
michael@0 | 127 | |
michael@0 | 128 | if (!RunWowHelper(buffer)) |
michael@0 | 129 | return false; |
michael@0 | 130 | |
michael@0 | 131 | // The child is intercepted on 64 bit, go on and wait for our event. |
michael@0 | 132 | if (!DllMapped()) |
michael@0 | 133 | return false; |
michael@0 | 134 | |
michael@0 | 135 | // The 32 bit version is available, cleanup the child. |
michael@0 | 136 | return Restore64Code(child_->Process(), patch_info); |
michael@0 | 137 | } |
michael@0 | 138 | |
michael@0 | 139 | bool Wow64::RunWowHelper(void* buffer) { |
michael@0 | 140 | COMPILE_ASSERT(sizeof(buffer) <= sizeof(DWORD), unsupported_64_bits); |
michael@0 | 141 | |
michael@0 | 142 | // Get the path to the helper (beside the exe). |
michael@0 | 143 | wchar_t prog_name[MAX_PATH]; |
michael@0 | 144 | GetModuleFileNameW(NULL, prog_name, MAX_PATH); |
michael@0 | 145 | std::wstring path(prog_name); |
michael@0 | 146 | size_t name_pos = path.find_last_of(L"\\"); |
michael@0 | 147 | if (std::wstring::npos == name_pos) |
michael@0 | 148 | return false; |
michael@0 | 149 | path.resize(name_pos + 1); |
michael@0 | 150 | |
michael@0 | 151 | std::wstringstream command; |
michael@0 | 152 | command << std::hex << std::showbase << L"\"" << path << |
michael@0 | 153 | L"wow_helper.exe\" " << child_->ProcessId() << " " << |
michael@0 | 154 | bit_cast<ULONG>(buffer); |
michael@0 | 155 | |
michael@0 | 156 | scoped_ptr_malloc<wchar_t> writable_command(_wcsdup(command.str().c_str())); |
michael@0 | 157 | |
michael@0 | 158 | STARTUPINFO startup_info = {0}; |
michael@0 | 159 | startup_info.cb = sizeof(startup_info); |
michael@0 | 160 | base::win::ScopedProcessInformation process_info; |
michael@0 | 161 | if (!::CreateProcess(NULL, writable_command.get(), NULL, NULL, FALSE, 0, NULL, |
michael@0 | 162 | NULL, &startup_info, process_info.Receive())) |
michael@0 | 163 | return false; |
michael@0 | 164 | |
michael@0 | 165 | DWORD reason = ::WaitForSingleObject(process_info.process_handle(), INFINITE); |
michael@0 | 166 | |
michael@0 | 167 | DWORD code; |
michael@0 | 168 | bool ok = |
michael@0 | 169 | ::GetExitCodeProcess(process_info.process_handle(), &code) ? true : false; |
michael@0 | 170 | |
michael@0 | 171 | if (WAIT_TIMEOUT == reason) |
michael@0 | 172 | return false; |
michael@0 | 173 | |
michael@0 | 174 | return ok && (0 == code); |
michael@0 | 175 | } |
michael@0 | 176 | |
michael@0 | 177 | // First we must wake up the child, then wait for dll loads on the child until |
michael@0 | 178 | // the one we care is loaded; at that point we must suspend the child again. |
michael@0 | 179 | bool Wow64::DllMapped() { |
michael@0 | 180 | if (1 != ::ResumeThread(child_->MainThread())) { |
michael@0 | 181 | NOTREACHED(); |
michael@0 | 182 | return false; |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | for (;;) { |
michael@0 | 186 | DWORD reason = ::WaitForSingleObject(dll_load_, INFINITE); |
michael@0 | 187 | if (WAIT_TIMEOUT == reason || WAIT_ABANDONED == reason) |
michael@0 | 188 | return false; |
michael@0 | 189 | |
michael@0 | 190 | if (!::ResetEvent(dll_load_)) |
michael@0 | 191 | return false; |
michael@0 | 192 | |
michael@0 | 193 | bool found = NtdllPresent(); |
michael@0 | 194 | if (found) { |
michael@0 | 195 | if (::SuspendThread(child_->MainThread())) |
michael@0 | 196 | return false; |
michael@0 | 197 | } |
michael@0 | 198 | |
michael@0 | 199 | if (!::SetEvent(continue_load_)) |
michael@0 | 200 | return false; |
michael@0 | 201 | |
michael@0 | 202 | if (found) |
michael@0 | 203 | return true; |
michael@0 | 204 | } |
michael@0 | 205 | } |
michael@0 | 206 | |
michael@0 | 207 | bool Wow64::NtdllPresent() { |
michael@0 | 208 | const size_t kBufferSize = 512; |
michael@0 | 209 | char buffer[kBufferSize]; |
michael@0 | 210 | SIZE_T read; |
michael@0 | 211 | if (!::ReadProcessMemory(child_->Process(), ntdll_, &buffer, kBufferSize, |
michael@0 | 212 | &read)) |
michael@0 | 213 | return false; |
michael@0 | 214 | if (kBufferSize != read) |
michael@0 | 215 | return false; |
michael@0 | 216 | return true; |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | } // namespace sandbox |