Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / sandbox / win / wow_helper / service64_resolver.cc
blob033b9d771e1519c3c18edc4365c515b261a72fe5
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"
10 namespace {
11 #pragma pack(push, 1)
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.
39 struct ServiceEntry {
40 // this struct contains roughly the following code:
41 // mov r10,rcx
42 // mov eax,52h
43 // syscall
44 // ret
45 // xchg ax,ax
46 // xchg ax,ax
48 ULONG mov_r10_ecx_mov_eax; // = 4C 8B D1 B8
49 ULONG service_id;
50 USHORT syscall; // = 0F 05
51 BYTE ret; // = C3
52 BYTE pad; // = 66
53 USHORT xchg_ax_ax1; // = 66 90
54 USHORT xchg_ax_ax2; // = 66 90
57 struct Redirected {
58 // this struct contains roughly the following code:
59 // jmp relative_32
60 // xchg ax,ax // 3 byte nop
62 Redirected() {
63 jmp = kRelJmp;
64 relative = 0;
65 pad = kPad;
66 xchg_ax_ax = kNop16;
68 BYTE jmp; // = E9
69 ULONG relative;
70 BYTE pad; // = 66
71 USHORT xchg_ax_ax; // = 66 90
74 struct InternalThunk {
75 // this struct contains roughly the following code:
76 // xor rax,rax
77 // mov eax, 0x00080000 // Thunk storage.
78 // mov [rax]PatchInfo.service, rcx // Save first argument.
79 // mov rcx, rax
80 // jmp relative_to_interceptor
82 InternalThunk() {
83 xor_rax_mov_eax = kXorRaxMovEax;
84 patch_info = 0;
85 save_rcx = kSaveRcx;
86 mov_rcx_rax_jmp = kMovRcxRaxJmp;
87 relative = 0;
89 ULONG xor_rax_mov_eax; // = 48 31 C0 B8
90 ULONG patch_info;
91 ULONG save_rcx; // = 48 89 48 10
92 ULONG mov_rcx_rax_jmp; // = 48 8b c8 e9
93 ULONG relative;
96 struct ServiceFullThunk {
97 sandbox::PatchInfo patch_info;
98 ServiceEntry original;
99 InternalThunk internal_thunk;
102 #pragma pack(pop)
104 // Simple utility function to write to a buffer on the child, if the memery has
105 // write protection attributes.
106 // Arguments:
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,
113 void* address,
114 const void* buffer,
115 size_t length) {
116 // first, remove the protections
117 DWORD old_protection;
118 if (!::VirtualProtectEx(child_process, address, length,
119 PAGE_WRITECOPY, &old_protection))
120 return false;
122 SIZE_T written;
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))
129 return false;
131 return ok;
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);
138 if (!ntdll)
139 return STATUS_PROCEDURE_NOT_FOUND;
141 void* signal = ::GetProcAddress(ntdll, "NtSignalAndWaitForSingleObject");
142 if (!signal)
143 return STATUS_PROCEDURE_NOT_FOUND;
145 patch_info->signal_and_wait =
146 reinterpret_cast<NtSignalAndWaitForSingleObjectFunction>(signal);
148 return STATUS_SUCCESS;
151 }; // namespace
153 namespace sandbox {
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,
160 void* thunk_storage,
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))
174 return ret;
177 ret = ResolveTarget(target_module, target_name, &target_);
178 if (!NT_SUCCESS(ret))
179 return ret;
181 interceptor_ = interceptor_entry_point;
183 return ret;
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,
194 void** address) {
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,
203 void* thunk_storage,
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))
210 return ret;
212 size_t thunk_bytes = GetThunkSize();
213 scoped_ptr<char[]> thunk_buffer(new char[thunk_bytes]);
214 ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(
215 thunk_buffer.get());
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;
225 return ret;
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,
242 void** address) {
243 if (NULL == module)
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;
260 SIZE_T read;
261 if (!::ReadProcessMemory(process_, target_, &function_code,
262 sizeof(function_code), &read))
263 return false;
265 if (sizeof(function_code) != read)
266 return false;
268 if (kMmovR10EcxMovEax != function_code.mov_r10_ecx_mov_eax ||
269 kSyscall != function_code.syscall || kRetNp != function_code.ret)
270 return false;
272 // Save the verified code
273 memcpy(local_thunk, &function_code, sizeof(function_code));
275 return true;
278 NTSTATUS Service64ResolverThunk::PerformPatch(void* local_thunk,
279 void* remote_thunk) {
280 ServiceFullThunk* full_local_thunk = reinterpret_cast<ServiceFullThunk*>(
281 local_thunk);
282 ServiceFullThunk* full_remote_thunk = reinterpret_cast<ServiceFullThunk*>(
283 remote_thunk);
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.
301 SIZE_T actual;
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))
313 return 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