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"
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"
17 // Holds the information needed for the interception of NtMapViewOfSection on
19 // Warning: do not modify this definition without changing also the code on the
20 // 64 bit helper process.
22 HANDLE dll_load
; // Event to signal the broker.
24 HANDLE continue_load
; // Event to wait for the broker.
26 HANDLE section
; // First argument of the call.
28 void* orig_MapViewOfSection
;
30 void* signal_and_wait
;
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
;
43 if (!::ReadProcessMemory(child
, patch_info
, &local_patch_info
,
44 sizeof(local_patch_info
), &actual
))
46 if (sizeof(local_patch_info
) != actual
)
49 if (local_patch_info
.original_high
)
51 if (local_patch_info
.patch_high
)
54 char buffer
[kServiceEntry64Size
];
56 if (!::ReadProcessMemory(child
, local_patch_info
.orig_MapViewOfSection
,
57 &buffer
, kServiceEntry64Size
, &actual
))
59 if (kServiceEntry64Size
!= actual
)
62 if (!::WriteProcessMemory(child
, local_patch_info
.patch_location
, &buffer
,
63 kServiceEntry64Size
, &actual
))
65 if (kServiceEntry64Size
!= actual
)
70 typedef BOOL (WINAPI
* IsWow64ProcessFunction
)(HANDLE process
, BOOL
* wow64
);
76 Wow64::Wow64(TargetProcess
* child
, HMODULE ntdll
)
77 : child_(child
), ntdll_(ntdll
), dll_load_(NULL
), continue_load_(NULL
) {
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
90 bool Wow64::WaitForNtdll() {
91 if (base::win::OSInfo::GetInstance()->wow64_status() !=
92 base::win::OSInfo::WOW64_ENABLED
)
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)) {
107 if (!::DuplicateHandle(current_process
, continue_load_
.Get(),
108 child_
->Process(), &remote_continue
, access
, FALSE
,
113 void* buffer
= ::VirtualAllocEx(child_
->Process(), NULL
, page_size
,
114 MEM_COMMIT
, PAGE_EXECUTE_READWRITE
);
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
;
124 if (!::WriteProcessMemory(child_
->Process(), patch_info
, &local_patch_info
,
125 offsetof(PatchInfo32
, section
), &written
))
127 if (offsetof(PatchInfo32
, section
) != written
)
130 if (!RunWowHelper(buffer
))
133 // The child is intercepted on 64 bit, go on and wait for our event.
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
)
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
))
167 base::win::ScopedProcessInformation
process_info(temp_process_info
);
169 DWORD reason
= ::WaitForSingleObject(process_info
.process_handle(), INFINITE
);
173 ::GetExitCodeProcess(process_info
.process_handle(), &code
) ? true : false;
175 if (WAIT_TIMEOUT
== reason
)
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())) {
190 DWORD reason
= ::WaitForSingleObject(dll_load_
.Get(), INFINITE
);
191 if (WAIT_TIMEOUT
== reason
|| WAIT_ABANDONED
== reason
)
194 if (!::ResetEvent(dll_load_
.Get()))
197 bool found
= NtdllPresent();
199 if (::SuspendThread(child_
->MainThread()))
203 if (!::SetEvent(continue_load_
.Get()))
211 bool Wow64::NtdllPresent() {
212 const size_t kBufferSize
= 512;
213 char buffer
[kBufferSize
];
215 if (!::ReadProcessMemory(child_
->Process(), ntdll_
, &buffer
, kBufferSize
,
218 if (kBufferSize
!= read
)
223 } // namespace sandbox