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
);
78 ::CloseHandle(dll_load_
);
81 ::CloseHandle(continue_load_
);
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
91 bool Wow64::WaitForNtdll() {
92 if (base::win::OSInfo::GetInstance()->wow64_status() !=
93 base::win::OSInfo::WOW64_ENABLED
)
96 const size_t page_size
= 4096;
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))
107 if (!::DuplicateHandle(current_process
, continue_load_
, child_
->Process(),
108 &remote_continue
, access
, FALSE
, 0))
111 void* buffer
= ::VirtualAllocEx(child_
->Process(), NULL
, page_size
,
112 MEM_COMMIT
, PAGE_EXECUTE_READWRITE
);
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
;
122 if (!::WriteProcessMemory(child_
->Process(), patch_info
, &local_patch_info
,
123 offsetof(PatchInfo32
, section
), &written
))
125 if (offsetof(PatchInfo32
, section
) != written
)
128 if (!RunWowHelper(buffer
))
131 // The child is intercepted on 64 bit, go on and wait for our event.
135 // The 32 bit version is available, cleanup the child.
136 return Restore64Code(child_
->Process(), patch_info
);
139 bool Wow64::RunWowHelper(void* buffer
) {
140 COMPILE_ASSERT(sizeof(buffer
) <= sizeof(DWORD
), unsupported_64_bits
);
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
)
149 path
.resize(name_pos
+ 1);
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
);
156 scoped_ptr
<wchar_t, base::FreeDeleter
>
157 writable_command(_wcsdup(command
.str().c_str()));
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
))
165 base::win::ScopedProcessInformation
process_info(temp_process_info
);
167 DWORD reason
= ::WaitForSingleObject(process_info
.process_handle(), INFINITE
);
171 ::GetExitCodeProcess(process_info
.process_handle(), &code
) ? true : false;
173 if (WAIT_TIMEOUT
== reason
)
176 return ok
&& (0 == code
);
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.
181 bool Wow64::DllMapped() {
182 if (1 != ::ResumeThread(child_
->MainThread())) {
188 DWORD reason
= ::WaitForSingleObject(dll_load_
, INFINITE
);
189 if (WAIT_TIMEOUT
== reason
|| WAIT_ABANDONED
== reason
)
192 if (!::ResetEvent(dll_load_
))
195 bool found
= NtdllPresent();
197 if (::SuspendThread(child_
->MainThread()))
201 if (!::SetEvent(continue_load_
))
209 bool Wow64::NtdllPresent() {
210 const size_t kBufferSize
= 512;
211 char buffer
[kBufferSize
];
213 if (!::ReadProcessMemory(child_
->Process(), ntdll_
, &buffer
, kBufferSize
,
216 if (kBufferSize
!= read
)
221 } // namespace sandbox