Add ICU message format support
[chromium-blink-merge.git] / sandbox / win / src / Wow64.cc
blob24facfc7c11b37fed50016b03021fa6dd76e3df2
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.
5 #include "sandbox/win/src/wow64.h"
7 #include <sstream>
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"
15 namespace {
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;
36 // Size of the 64 bit service entry.
37 const SIZE_T kServiceEntry64Size = 0x10;
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;
49 if (local_patch_info.original_high)
50 return false;
51 if (local_patch_info.patch_high)
52 return false;
54 char buffer[kServiceEntry64Size];
56 if (!::ReadProcessMemory(child, local_patch_info.orig_MapViewOfSection,
57 &buffer, kServiceEntry64Size, &actual))
58 return false;
59 if (kServiceEntry64Size != actual)
60 return false;
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;
70 typedef BOOL (WINAPI* IsWow64ProcessFunction)(HANDLE process, BOOL* wow64);
72 } // namespace
74 namespace sandbox {
76 Wow64::Wow64(TargetProcess* child, HMODULE ntdll)
77 : child_(child), ntdll_(ntdll), dll_load_(NULL), continue_load_(NULL) {
80 Wow64::~Wow64() {
83 // The basic idea is to allocate one page of memory on the child, and initialize
84 // the first part of it with our version of PatchInfo32. Then launch the helper
85 // process passing it that address on the child. The helper process will patch
86 // the 64 bit version of NtMapViewOfFile, and the interception will signal the
87 // first event on the buffer. We'll be waiting on that event and after the 32
88 // bit version of ntdll is loaded, we'll remove the interception and return to
89 // our caller.
90 bool Wow64::WaitForNtdll() {
91 if (base::win::OSInfo::GetInstance()->wow64_status() !=
92 base::win::OSInfo::WOW64_ENABLED)
93 return true;
95 const size_t page_size = 4096;
97 // Create some default manual reset un-named events, not signaled.
98 dll_load_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL));
99 continue_load_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL));
100 HANDLE current_process = ::GetCurrentProcess();
101 HANDLE remote_load, remote_continue;
102 DWORD access = EVENT_MODIFY_STATE | SYNCHRONIZE;
103 if (!::DuplicateHandle(current_process, dll_load_.Get(), child_->Process(),
104 &remote_load, access, FALSE, 0)) {
105 return false;
107 if (!::DuplicateHandle(current_process, continue_load_.Get(),
108 child_->Process(), &remote_continue, access, FALSE,
109 0)) {
110 return false;
113 void* buffer = ::VirtualAllocEx(child_->Process(), NULL, page_size,
114 MEM_COMMIT, PAGE_EXECUTE_READWRITE);
115 DCHECK(buffer);
116 if (!buffer)
117 return false;
119 PatchInfo32* patch_info = reinterpret_cast<PatchInfo32*>(buffer);
120 PatchInfo32 local_patch_info = {0};
121 local_patch_info.dll_load = remote_load;
122 local_patch_info.continue_load = remote_continue;
123 SIZE_T written;
124 if (!::WriteProcessMemory(child_->Process(), patch_info, &local_patch_info,
125 offsetof(PatchInfo32, section), &written))
126 return false;
127 if (offsetof(PatchInfo32, section) != written)
128 return false;
130 if (!RunWowHelper(buffer))
131 return false;
133 // The child is intercepted on 64 bit, go on and wait for our event.
134 if (!DllMapped())
135 return false;
137 // The 32 bit version is available, cleanup the child.
138 return Restore64Code(child_->Process(), patch_info);
141 bool Wow64::RunWowHelper(void* buffer) {
142 static_assert(sizeof(buffer) <= sizeof(DWORD), "unsupported 64 bits");
144 // Get the path to the helper (beside the exe).
145 wchar_t prog_name[MAX_PATH];
146 GetModuleFileNameW(NULL, prog_name, MAX_PATH);
147 base::string16 path(prog_name);
148 size_t name_pos = path.find_last_of(L"\\");
149 if (base::string16::npos == name_pos)
150 return false;
151 path.resize(name_pos + 1);
153 std::basic_stringstream<base::char16> command;
154 command << std::hex << std::showbase << L"\"" << path <<
155 L"wow_helper.exe\" " << child_->ProcessId() << " " <<
156 bit_cast<ULONG>(buffer);
158 scoped_ptr<wchar_t, base::FreeDeleter>
159 writable_command(_wcsdup(command.str().c_str()));
161 STARTUPINFO startup_info = {0};
162 startup_info.cb = sizeof(startup_info);
163 PROCESS_INFORMATION temp_process_info = {};
164 if (!::CreateProcess(NULL, writable_command.get(), NULL, NULL, FALSE, 0, NULL,
165 NULL, &startup_info, &temp_process_info))
166 return false;
167 base::win::ScopedProcessInformation process_info(temp_process_info);
169 DWORD reason = ::WaitForSingleObject(process_info.process_handle(), INFINITE);
171 DWORD code;
172 bool ok =
173 ::GetExitCodeProcess(process_info.process_handle(), &code) ? true : false;
175 if (WAIT_TIMEOUT == reason)
176 return false;
178 return ok && (0 == code);
181 // First we must wake up the child, then wait for dll loads on the child until
182 // the one we care is loaded; at that point we must suspend the child again.
183 bool Wow64::DllMapped() {
184 if (1 != ::ResumeThread(child_->MainThread())) {
185 NOTREACHED();
186 return false;
189 for (;;) {
190 DWORD reason = ::WaitForSingleObject(dll_load_.Get(), INFINITE);
191 if (WAIT_TIMEOUT == reason || WAIT_ABANDONED == reason)
192 return false;
194 if (!::ResetEvent(dll_load_.Get()))
195 return false;
197 bool found = NtdllPresent();
198 if (found) {
199 if (::SuspendThread(child_->MainThread()))
200 return false;
203 if (!::SetEvent(continue_load_.Get()))
204 return false;
206 if (found)
207 return true;
211 bool Wow64::NtdllPresent() {
212 const size_t kBufferSize = 512;
213 char buffer[kBufferSize];
214 SIZE_T read;
215 if (!::ReadProcessMemory(child_->Process(), ntdll_, &buffer, kBufferSize,
216 &read))
217 return false;
218 if (kBufferSize != read)
219 return false;
220 return true;
223 } // namespace sandbox