1 // Copyright (c) 2011 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/wow_helper/service64_resolver.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "sandbox/win/wow_helper/target_code.h"
13 const BYTE kMovEax
= 0xB8;
14 const BYTE kMovEdx
= 0xBA;
15 const USHORT kCallPtrEdx
= 0x12FF;
16 const BYTE kRet
= 0xC2;
17 const BYTE kNop
= 0x90;
18 const USHORT kJmpEdx
= 0xE2FF;
19 const USHORT kXorEcx
= 0xC933;
20 const ULONG kLeaEdx
= 0x0424548D;
21 const ULONG kCallFs1
= 0xC015FF64;
22 const ULONG kCallFs2Ret
= 0xC2000000;
23 const BYTE kPopEdx
= 0x5A;
24 const BYTE kPushEdx
= 0x52;
25 const BYTE kPush32
= 0x68;
27 const ULONG kMmovR10EcxMovEax
= 0xB8D18B4C;
28 const USHORT kSyscall
= 0x050F;
29 const BYTE kRetNp
= 0xC3;
30 const BYTE kPad
= 0x66;
31 const USHORT kNop16
= 0x9066;
32 const BYTE kRelJmp
= 0xE9;
34 const ULONG kXorRaxMovEax
= 0xB8C03148;
35 const ULONG kSaveRcx
= 0x10488948;
36 const ULONG kMovRcxRaxJmp
= 0xE9C88B48;
38 // Service code for 64 bit systems.
40 // this struct contains roughly the following code:
48 ULONG mov_r10_ecx_mov_eax
; // = 4C 8B D1 B8
50 USHORT syscall
; // = 0F 05
53 USHORT xchg_ax_ax1
; // = 66 90
54 USHORT xchg_ax_ax2
; // = 66 90
58 // this struct contains roughly the following code:
60 // xchg ax,ax // 3 byte nop
71 USHORT xchg_ax_ax
; // = 66 90
74 struct InternalThunk
{
75 // this struct contains roughly the following code:
77 // mov eax, 0x00080000 // Thunk storage.
78 // mov [rax]PatchInfo.service, rcx // Save first argument.
80 // jmp relative_to_interceptor
83 xor_rax_mov_eax
= kXorRaxMovEax
;
86 mov_rcx_rax_jmp
= kMovRcxRaxJmp
;
89 ULONG xor_rax_mov_eax
; // = 48 31 C0 B8
91 ULONG save_rcx
; // = 48 89 48 10
92 ULONG mov_rcx_rax_jmp
; // = 48 8b c8 e9
96 struct ServiceFullThunk
{
97 sandbox::PatchInfo patch_info
;
98 ServiceEntry original
;
99 InternalThunk internal_thunk
;
104 // Simple utility function to write to a buffer on the child, if the memery has
105 // write protection attributes.
107 // child_process (in): process to write to.
108 // address (out): memory position on the child to write to.
109 // buffer (in): local buffer with the data to write .
110 // length (in): number of bytes to write.
111 // Returns true on success.
112 bool WriteProtectedChildMemory(HANDLE child_process
,
116 // first, remove the protections
117 DWORD old_protection
;
118 if (!::VirtualProtectEx(child_process
, address
, length
,
119 PAGE_WRITECOPY
, &old_protection
))
123 bool ok
= ::WriteProcessMemory(child_process
, address
, buffer
, length
,
124 &written
) && (length
== written
);
126 // always attempt to restore the original protection
127 if (!::VirtualProtectEx(child_process
, address
, length
,
128 old_protection
, &old_protection
))
134 // Get pointers to the functions that we need from ntdll.dll.
135 NTSTATUS
ResolveNtdll(sandbox::PatchInfo
* patch_info
) {
136 wchar_t* ntdll_name
= L
"ntdll.dll";
137 HMODULE ntdll
= ::GetModuleHandle(ntdll_name
);
139 return STATUS_PROCEDURE_NOT_FOUND
;
141 void* signal
= ::GetProcAddress(ntdll
, "NtSignalAndWaitForSingleObject");
143 return STATUS_PROCEDURE_NOT_FOUND
;
145 patch_info
->signal_and_wait
=
146 reinterpret_cast<NtSignalAndWaitForSingleObjectFunction
>(signal
);
148 return STATUS_SUCCESS
;
155 NTSTATUS
ResolverThunk::Init(const void* target_module
,
156 const void* interceptor_module
,
157 const char* target_name
,
158 const char* interceptor_name
,
159 const void* interceptor_entry_point
,
161 size_t storage_bytes
) {
162 if (NULL
== thunk_storage
|| 0 == storage_bytes
||
163 NULL
== target_module
|| NULL
== target_name
)
164 return STATUS_INVALID_PARAMETER
;
166 if (storage_bytes
< GetThunkSize())
167 return STATUS_BUFFER_TOO_SMALL
;
169 NTSTATUS ret
= STATUS_SUCCESS
;
170 if (NULL
== interceptor_entry_point
) {
171 ret
= ResolveInterceptor(interceptor_module
, interceptor_name
,
172 &interceptor_entry_point
);
173 if (!NT_SUCCESS(ret
))
177 ret
= ResolveTarget(target_module
, target_name
, &target_
);
178 if (!NT_SUCCESS(ret
))
181 interceptor_
= interceptor_entry_point
;
186 NTSTATUS
ResolverThunk::ResolveInterceptor(const void* interceptor_module
,
187 const char* interceptor_name
,
188 const void** address
) {
189 return STATUS_NOT_IMPLEMENTED
;
192 NTSTATUS
ResolverThunk::ResolveTarget(const void* module
,
193 const char* function_name
,
195 return STATUS_NOT_IMPLEMENTED
;
198 NTSTATUS
Service64ResolverThunk::Setup(const void* target_module
,
199 const void* interceptor_module
,
200 const char* target_name
,
201 const char* interceptor_name
,
202 const void* interceptor_entry_point
,
204 size_t storage_bytes
,
205 size_t* storage_used
) {
206 NTSTATUS ret
= Init(target_module
, interceptor_module
, target_name
,
207 interceptor_name
, interceptor_entry_point
,
208 thunk_storage
, storage_bytes
);
209 if (!NT_SUCCESS(ret
))
212 size_t thunk_bytes
= GetThunkSize();
213 scoped_ptr
<char[]> thunk_buffer(new char[thunk_bytes
]);
214 ServiceFullThunk
* thunk
= reinterpret_cast<ServiceFullThunk
*>(
217 if (!IsFunctionAService(&thunk
->original
))
218 return STATUS_UNSUCCESSFUL
;
220 ret
= PerformPatch(thunk
, thunk_storage
);
222 if (NULL
!= storage_used
)
223 *storage_used
= thunk_bytes
;
228 NTSTATUS
Service64ResolverThunk::ResolveInterceptor(
229 const void* interceptor_module
,
230 const char* interceptor_name
,
231 const void** address
) {
232 // After all, we are using a locally mapped version of the exe, so the
233 // action is the same as for a target function.
234 return ResolveTarget(interceptor_module
, interceptor_name
,
235 const_cast<void**>(address
));
238 // In this case all the work is done from the parent, so resolve is
239 // just a simple GetProcAddress.
240 NTSTATUS
Service64ResolverThunk::ResolveTarget(const void* module
,
241 const char* function_name
,
244 return STATUS_UNSUCCESSFUL
;
246 *address
= ::GetProcAddress(bit_cast
<HMODULE
>(module
), function_name
);
248 if (NULL
== *address
)
249 return STATUS_UNSUCCESSFUL
;
251 return STATUS_SUCCESS
;
254 size_t Service64ResolverThunk::GetThunkSize() const {
255 return sizeof(ServiceFullThunk
);
258 bool Service64ResolverThunk::IsFunctionAService(void* local_thunk
) const {
259 ServiceEntry function_code
;
261 if (!::ReadProcessMemory(process_
, target_
, &function_code
,
262 sizeof(function_code
), &read
))
265 if (sizeof(function_code
) != read
)
268 if (kMmovR10EcxMovEax
!= function_code
.mov_r10_ecx_mov_eax
||
269 kSyscall
!= function_code
.syscall
|| kRetNp
!= function_code
.ret
)
272 // Save the verified code
273 memcpy(local_thunk
, &function_code
, sizeof(function_code
));
278 NTSTATUS
Service64ResolverThunk::PerformPatch(void* local_thunk
,
279 void* remote_thunk
) {
280 ServiceFullThunk
* full_local_thunk
= reinterpret_cast<ServiceFullThunk
*>(
282 ServiceFullThunk
* full_remote_thunk
= reinterpret_cast<ServiceFullThunk
*>(
285 // If the source or target are above 4GB we cannot do this relative jump.
286 if (reinterpret_cast<ULONG_PTR
>(full_remote_thunk
) >
287 static_cast<ULONG_PTR
>(ULONG_MAX
))
288 return STATUS_CONFLICTING_ADDRESSES
;
290 if (reinterpret_cast<ULONG_PTR
>(target_
) > static_cast<ULONG_PTR
>(ULONG_MAX
))
291 return STATUS_CONFLICTING_ADDRESSES
;
293 // Patch the original code.
294 Redirected local_service
;
295 Redirected
* remote_service
= reinterpret_cast<Redirected
*>(target_
);
296 ULONG_PTR diff
= reinterpret_cast<BYTE
*>(&full_remote_thunk
->internal_thunk
) -
297 &remote_service
->pad
;
298 local_service
.relative
= static_cast<ULONG
>(diff
);
300 // Setup the PatchInfo structure.
302 if (!::ReadProcessMemory(process_
, remote_thunk
, local_thunk
,
303 sizeof(PatchInfo
), &actual
))
304 return STATUS_UNSUCCESSFUL
;
305 if (sizeof(PatchInfo
) != actual
)
306 return STATUS_UNSUCCESSFUL
;
308 full_local_thunk
->patch_info
.orig_MapViewOfSection
= reinterpret_cast<
309 NtMapViewOfSectionFunction
>(&full_remote_thunk
->original
);
310 full_local_thunk
->patch_info
.patch_location
= target_
;
311 NTSTATUS ret
= ResolveNtdll(&full_local_thunk
->patch_info
);
312 if (!NT_SUCCESS(ret
))
315 // Setup the thunk. The jump out is performed from right after the end of the
316 // thunk (full_remote_thunk + 1).
317 InternalThunk my_thunk
;
318 ULONG_PTR patch_info
= reinterpret_cast<ULONG_PTR
>(remote_thunk
);
319 my_thunk
.patch_info
= static_cast<ULONG
>(patch_info
);
320 diff
= reinterpret_cast<const BYTE
*>(interceptor_
) -
321 reinterpret_cast<BYTE
*>(full_remote_thunk
+ 1);
322 my_thunk
.relative
= static_cast<ULONG
>(diff
);
324 memcpy(&full_local_thunk
->internal_thunk
, &my_thunk
, sizeof(my_thunk
));
326 // copy the local thunk buffer to the child
327 if (!::WriteProcessMemory(process_
, remote_thunk
, local_thunk
,
328 sizeof(ServiceFullThunk
), &actual
))
329 return STATUS_UNSUCCESSFUL
;
331 if (sizeof(ServiceFullThunk
) != actual
)
332 return STATUS_UNSUCCESSFUL
;
334 // and now change the function to intercept, on the child
335 if (!::WriteProtectedChildMemory(process_
, target_
, &local_service
,
336 sizeof(local_service
)))
337 return STATUS_UNSUCCESSFUL
;
339 return STATUS_SUCCESS
;
342 } // namespace sandbox