• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
Restore64Code(HANDLE child,PatchInfo32 * patch_info)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 
~Wow64()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.
WaitForNtdll()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 
RunWowHelper(void * buffer)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.
DllMapped()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 
NtdllPresent()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