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/service_resolver.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "sandbox/win/src/sandbox_nt_util.h"
9 #include "sandbox/win/src/win_utils.h"
14 const ULONG kMmovR10EcxMovEax
= 0xB8D18B4C;
15 const USHORT kSyscall
= 0x050F;
16 const BYTE kRetNp
= 0xC3;
17 const ULONG64 kMov1
= 0x54894808244C8948;
18 const ULONG64 kMov2
= 0x4C182444894C1024;
19 const ULONG kMov3
= 0x20244C89;
20 const USHORT kTestByte
= 0x04F6;
21 const BYTE kPtr
= 0x25;
22 const BYTE kRet
= 0xC3;
23 const USHORT kJne
= 0x0375;
25 // Service code for 64 bit systems.
27 // This struct contains roughly the following code:
35 ULONG mov_r10_rcx_mov_eax
; // = 4C 8B D1 B8
37 USHORT syscall
; // = 0F 05
40 USHORT xchg_ax_ax1
; // = 66 90
41 USHORT xchg_ax_ax2
; // = 66 90
44 // Service code for 64 bit Windows 8.
45 struct ServiceEntryW8
{
46 // This struct contains the following code:
47 // 00 48894c2408 mov [rsp+8], rcx
48 // 05 4889542410 mov [rsp+10], rdx
49 // 0a 4c89442418 mov [rsp+18], r8
50 // 0f 4c894c2420 mov [rsp+20], r9
51 // 14 4c8bd1 mov r10,rcx
52 // 17 b825000000 mov eax,25h
57 ULONG64 mov_1
; // = 48 89 4C 24 08 48 89 54
58 ULONG64 mov_2
; // = 24 10 4C 89 44 24 18 4C
59 ULONG mov_3
; // = 89 4C 24 20
60 ULONG mov_r10_rcx_mov_eax
; // = 4C 8B D1 B8
62 USHORT syscall
; // = 0F 05
67 // Service code for 64 bit systems with int 2e fallback.
68 struct ServiceEntryWithInt2E
{
69 // This struct contains roughly the following code:
70 // 00 4c8bd1 mov r10,rcx
71 // 03 b855000000 mov eax,52h
72 // 08 f604250803fe7f01 test byte ptr SharedUserData!308, 1
73 // 10 7503 jne [over syscall]
79 ULONG mov_r10_rcx_mov_eax
; // = 4C 8B D1 B8
81 USHORT test_byte
; // = F6 04
83 ULONG user_shared_data_ptr
;
85 USHORT jne_over_syscall
; // = 75 03
86 USHORT syscall
; // = 0F 05
88 USHORT int2e
; // = CD 2E
92 // We don't have an internal thunk for x64.
93 struct ServiceFullThunk
{
95 ServiceEntry original
;
96 ServiceEntryW8 original_w8
;
97 ServiceEntryWithInt2E original_int2e_fallback
;
103 bool IsService(const void* source
) {
104 const ServiceEntry
* service
=
105 reinterpret_cast<const ServiceEntry
*>(source
);
107 return (kMmovR10EcxMovEax
== service
->mov_r10_rcx_mov_eax
&&
108 kSyscall
== service
->syscall
&& kRetNp
== service
->ret
);
111 bool IsServiceW8(const void* source
) {
112 const ServiceEntryW8
* service
=
113 reinterpret_cast<const ServiceEntryW8
*>(source
);
115 return (kMmovR10EcxMovEax
== service
->mov_r10_rcx_mov_eax
&&
116 kMov1
== service
->mov_1
&& kMov2
== service
->mov_2
&&
117 kMov3
== service
->mov_3
);
120 bool IsServiceWithInt2E(const void* source
) {
121 const ServiceEntryWithInt2E
* service
=
122 reinterpret_cast<const ServiceEntryWithInt2E
*>(source
);
124 return (kMmovR10EcxMovEax
== service
->mov_r10_rcx_mov_eax
&&
125 kTestByte
== service
->test_byte
&& kPtr
== service
->ptr
&&
126 kJne
== service
->jne_over_syscall
&& kSyscall
== service
->syscall
&&
127 kRet
== service
->ret
&& kRet
== service
->ret2
);
134 NTSTATUS
ServiceResolverThunk::Setup(const void* target_module
,
135 const void* interceptor_module
,
136 const char* target_name
,
137 const char* interceptor_name
,
138 const void* interceptor_entry_point
,
140 size_t storage_bytes
,
141 size_t* storage_used
) {
142 NTSTATUS ret
= Init(target_module
, interceptor_module
, target_name
,
143 interceptor_name
, interceptor_entry_point
,
144 thunk_storage
, storage_bytes
);
145 if (!NT_SUCCESS(ret
))
148 size_t thunk_bytes
= GetThunkSize();
149 scoped_ptr
<char[]> thunk_buffer(new char[thunk_bytes
]);
150 ServiceFullThunk
* thunk
= reinterpret_cast<ServiceFullThunk
*>(
153 if (!IsFunctionAService(&thunk
->original
))
154 return STATUS_UNSUCCESSFUL
;
156 ret
= PerformPatch(thunk
, thunk_storage
);
158 if (NULL
!= storage_used
)
159 *storage_used
= thunk_bytes
;
164 size_t ServiceResolverThunk::GetThunkSize() const {
165 return sizeof(ServiceFullThunk
);
168 NTSTATUS
ServiceResolverThunk::CopyThunk(const void* target_module
,
169 const char* target_name
,
171 size_t storage_bytes
,
172 size_t* storage_used
) {
173 NTSTATUS ret
= ResolveTarget(target_module
, target_name
, &target_
);
174 if (!NT_SUCCESS(ret
))
177 size_t thunk_bytes
= GetThunkSize();
178 if (storage_bytes
< thunk_bytes
)
179 return STATUS_UNSUCCESSFUL
;
181 ServiceFullThunk
* thunk
= reinterpret_cast<ServiceFullThunk
*>(thunk_storage
);
183 if (!IsFunctionAService(&thunk
->original
))
184 return STATUS_UNSUCCESSFUL
;
186 if (NULL
!= storage_used
)
187 *storage_used
= thunk_bytes
;
192 bool ServiceResolverThunk::IsFunctionAService(void* local_thunk
) const {
193 ServiceFullThunk function_code
;
195 if (!::ReadProcessMemory(process_
, target_
, &function_code
,
196 sizeof(function_code
), &read
))
199 if (sizeof(function_code
) != read
)
202 if (!IsService(&function_code
) && !IsServiceW8(&function_code
) &&
203 !IsServiceWithInt2E(&function_code
))
206 // Save the verified code.
207 memcpy(local_thunk
, &function_code
, sizeof(function_code
));
212 NTSTATUS
ServiceResolverThunk::PerformPatch(void* local_thunk
,
213 void* remote_thunk
) {
214 // Patch the original code.
215 ServiceEntry local_service
;
216 DCHECK_NT(GetInternalThunkSize() >= sizeof(local_service
));
217 if (!SetInternalThunk(&local_service
, sizeof(local_service
), NULL
,
219 return STATUS_UNSUCCESSFUL
;
221 // Copy the local thunk buffer to the child.
223 if (!::WriteProcessMemory(process_
, remote_thunk
, local_thunk
,
224 sizeof(ServiceFullThunk
), &actual
))
225 return STATUS_UNSUCCESSFUL
;
227 if (sizeof(ServiceFullThunk
) != actual
)
228 return STATUS_UNSUCCESSFUL
;
230 // And now change the function to intercept, on the child.
231 if (NULL
!= ntdll_base_
) {
232 // Running a unit test.
233 if (!::WriteProcessMemory(process_
, target_
, &local_service
,
234 sizeof(local_service
), &actual
))
235 return STATUS_UNSUCCESSFUL
;
237 if (!WriteProtectedChildMemory(process_
, target_
, &local_service
,
238 sizeof(local_service
)))
239 return STATUS_UNSUCCESSFUL
;
242 return STATUS_SUCCESS
;
245 bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk
) const {
250 } // namespace sandbox