Roll src/third_party/WebKit e0eac24:489c548 (svn 193311:193320)
[chromium-blink-merge.git] / sandbox / win / src / service_resolver_32.cc
blobab69ab85388076f6e57f9cbd5bcf585b737a3a26
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/win_utils.h"
10 namespace {
11 #pragma pack(push, 1)
13 const BYTE kMovEax = 0xB8;
14 const BYTE kMovEdx = 0xBA;
15 const USHORT kMovEdxEsp = 0xD48B;
16 const USHORT kCallPtrEdx = 0x12FF;
17 const USHORT kCallEdx = 0xD2FF;
18 const BYTE kCallEip = 0xE8;
19 const BYTE kRet = 0xC2;
20 const BYTE kRet2 = 0xC3;
21 const BYTE kNop = 0x90;
22 const USHORT kJmpEdx = 0xE2FF;
23 const USHORT kXorEcx = 0xC933;
24 const ULONG kLeaEdx = 0x0424548D;
25 const ULONG kCallFs1 = 0xC015FF64;
26 const USHORT kCallFs2 = 0;
27 const BYTE kCallFs3 = 0;
28 const BYTE kAddEsp1 = 0x83;
29 const USHORT kAddEsp2 = 0x4C4;
30 const BYTE kJmp32 = 0xE9;
31 const USHORT kSysenter = 0x340F;
33 const int kMaxService = 1000;
35 // Service code for 32 bit systems.
36 // NOTE: on win2003 "call dword ptr [edx]" is "call edx".
37 struct ServiceEntry {
38 // This struct contains roughly the following code:
39 // 00 mov eax,25h
40 // 05 mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
41 // 0a call dword ptr [edx]
42 // 0c ret 2Ch
43 // 0f nop
44 BYTE mov_eax; // = B8
45 ULONG service_id;
46 BYTE mov_edx; // = BA
47 ULONG stub;
48 USHORT call_ptr_edx; // = FF 12
49 BYTE ret; // = C2
50 USHORT num_params;
51 BYTE nop;
54 // Service code for 32 bit Windows 8.
55 struct ServiceEntryW8 {
56 // This struct contains the following code:
57 // 00 b825000000 mov eax,25h
58 // 05 e803000000 call eip+3
59 // 0a c22c00 ret 2Ch
60 // 0d 8bd4 mov edx,esp
61 // 0f 0f34 sysenter
62 // 11 c3 ret
63 // 12 8bff mov edi,edi
64 BYTE mov_eax; // = B8
65 ULONG service_id;
66 BYTE call_eip; // = E8
67 ULONG call_offset;
68 BYTE ret_p; // = C2
69 USHORT num_params;
70 USHORT mov_edx_esp; // = BD D4
71 USHORT sysenter; // = 0F 34
72 BYTE ret; // = C3
73 USHORT nop;
76 // Service code for a 32 bit process running on a 64 bit os.
77 struct Wow64Entry {
78 // This struct may contain one of two versions of code:
79 // 1. For XP, Vista and 2K3:
80 // 00 b825000000 mov eax, 25h
81 // 05 33c9 xor ecx, ecx
82 // 07 8d542404 lea edx, [esp + 4]
83 // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
84 // 12 c22c00 ret 2Ch
86 // 2. For Windows 7:
87 // 00 b825000000 mov eax, 25h
88 // 05 33c9 xor ecx, ecx
89 // 07 8d542404 lea edx, [esp + 4]
90 // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
91 // 12 83c404 add esp, 4
92 // 15 c22c00 ret 2Ch
94 // So we base the structure on the bigger one:
95 BYTE mov_eax; // = B8
96 ULONG service_id;
97 USHORT xor_ecx; // = 33 C9
98 ULONG lea_edx; // = 8D 54 24 04
99 ULONG call_fs1; // = 64 FF 15 C0
100 USHORT call_fs2; // = 00 00
101 BYTE call_fs3; // = 00
102 BYTE add_esp1; // = 83 or ret
103 USHORT add_esp2; // = C4 04 or num_params
104 BYTE ret; // = C2
105 USHORT num_params;
108 // Service code for a 32 bit process running on 64 bit Windows 8.
109 struct Wow64EntryW8 {
110 // 00 b825000000 mov eax, 25h
111 // 05 64ff15c0000000 call dword ptr fs:[0C0h]
112 // 0b c22c00 ret 2Ch
113 // 0f 90 nop
114 BYTE mov_eax; // = B8
115 ULONG service_id;
116 ULONG call_fs1; // = 64 FF 15 C0
117 USHORT call_fs2; // = 00 00
118 BYTE call_fs3; // = 00
119 BYTE ret; // = C2
120 USHORT num_params;
121 BYTE nop;
124 // Make sure that relaxed patching works as expected.
125 const size_t kMinServiceSize = offsetof(ServiceEntry, ret);
126 static_assert(sizeof(ServiceEntryW8) >= kMinServiceSize,
127 "wrong service length");
128 static_assert(sizeof(Wow64Entry) >= kMinServiceSize, "wrong service length");
129 static_assert(sizeof(Wow64EntryW8) >= kMinServiceSize, "wrong service length");
131 struct ServiceFullThunk {
132 union {
133 ServiceEntry original;
134 ServiceEntryW8 original_w8;
135 Wow64Entry wow_64;
136 Wow64EntryW8 wow_64_w8;
138 int internal_thunk; // Dummy member to the beginning of the internal thunk.
141 #pragma pack(pop)
143 }; // namespace
145 namespace sandbox {
147 NTSTATUS ServiceResolverThunk::Setup(const void* target_module,
148 const void* interceptor_module,
149 const char* target_name,
150 const char* interceptor_name,
151 const void* interceptor_entry_point,
152 void* thunk_storage,
153 size_t storage_bytes,
154 size_t* storage_used) {
155 NTSTATUS ret = Init(target_module, interceptor_module, target_name,
156 interceptor_name, interceptor_entry_point,
157 thunk_storage, storage_bytes);
158 if (!NT_SUCCESS(ret))
159 return ret;
161 relative_jump_ = 0;
162 size_t thunk_bytes = GetThunkSize();
163 scoped_ptr<char[]> thunk_buffer(new char[thunk_bytes]);
164 ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(
165 thunk_buffer.get());
167 if (!IsFunctionAService(&thunk->original) &&
168 (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage)))
169 return STATUS_UNSUCCESSFUL;
171 ret = PerformPatch(thunk, thunk_storage);
173 if (NULL != storage_used)
174 *storage_used = thunk_bytes;
176 return ret;
179 size_t ServiceResolverThunk::GetThunkSize() const {
180 return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize();
183 NTSTATUS ServiceResolverThunk::CopyThunk(const void* target_module,
184 const char* target_name,
185 BYTE* thunk_storage,
186 size_t storage_bytes,
187 size_t* storage_used) {
188 NTSTATUS ret = ResolveTarget(target_module, target_name, &target_);
189 if (!NT_SUCCESS(ret))
190 return ret;
192 size_t thunk_bytes = GetThunkSize();
193 if (storage_bytes < thunk_bytes)
194 return STATUS_UNSUCCESSFUL;
196 ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(thunk_storage);
198 if (!IsFunctionAService(&thunk->original) &&
199 (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) {
200 return STATUS_UNSUCCESSFUL;
203 if (NULL != storage_used)
204 *storage_used = thunk_bytes;
206 return ret;
209 bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const {
210 ServiceEntry function_code;
211 SIZE_T read;
212 if (!::ReadProcessMemory(process_, target_, &function_code,
213 sizeof(function_code), &read))
214 return false;
216 if (sizeof(function_code) != read)
217 return false;
219 if (kMovEax != function_code.mov_eax ||
220 kMovEdx != function_code.mov_edx ||
221 (kCallPtrEdx != function_code.call_ptr_edx &&
222 kCallEdx != function_code.call_ptr_edx) ||
223 kRet != function_code.ret)
224 return false;
226 // Find the system call pointer if we don't already have it.
227 if (kCallEdx != function_code.call_ptr_edx) {
228 DWORD ki_system_call;
229 if (!::ReadProcessMemory(process_,
230 bit_cast<const void*>(function_code.stub),
231 &ki_system_call, sizeof(ki_system_call), &read))
232 return false;
234 if (sizeof(ki_system_call) != read)
235 return false;
237 HMODULE module_1, module_2;
238 // last check, call_stub should point to a KiXXSystemCall function on ntdll
239 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
240 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
241 bit_cast<const wchar_t*>(ki_system_call), &module_1))
242 return false;
244 if (NULL != ntdll_base_) {
245 // This path is only taken when running the unit tests. We want to be
246 // able to patch a buffer in memory, so target_ is not inside ntdll.
247 module_2 = ntdll_base_;
248 } else {
249 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
250 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
251 reinterpret_cast<const wchar_t*>(target_),
252 &module_2))
253 return false;
256 if (module_1 != module_2)
257 return false;
260 // Save the verified code
261 memcpy(local_thunk, &function_code, sizeof(function_code));
263 return true;
266 NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk,
267 void* remote_thunk) {
268 ServiceEntry intercepted_code;
269 size_t bytes_to_write = sizeof(intercepted_code);
270 ServiceFullThunk *full_local_thunk = reinterpret_cast<ServiceFullThunk*>(
271 local_thunk);
272 ServiceFullThunk *full_remote_thunk = reinterpret_cast<ServiceFullThunk*>(
273 remote_thunk);
275 // patch the original code
276 memcpy(&intercepted_code, &full_local_thunk->original,
277 sizeof(intercepted_code));
278 intercepted_code.mov_eax = kMovEax;
279 intercepted_code.service_id = full_local_thunk->original.service_id;
280 intercepted_code.mov_edx = kMovEdx;
281 intercepted_code.stub = bit_cast<ULONG>(&full_remote_thunk->internal_thunk);
282 intercepted_code.call_ptr_edx = kJmpEdx;
283 bytes_to_write = kMinServiceSize;
285 if (relative_jump_) {
286 intercepted_code.mov_eax = kJmp32;
287 intercepted_code.service_id = relative_jump_;
288 bytes_to_write = offsetof(ServiceEntry, mov_edx);
291 // setup the thunk
292 SetInternalThunk(&full_local_thunk->internal_thunk, GetInternalThunkSize(),
293 remote_thunk, interceptor_);
295 size_t thunk_size = GetThunkSize();
297 // copy the local thunk buffer to the child
298 SIZE_T written;
299 if (!::WriteProcessMemory(process_, remote_thunk, local_thunk,
300 thunk_size, &written))
301 return STATUS_UNSUCCESSFUL;
303 if (thunk_size != written)
304 return STATUS_UNSUCCESSFUL;
306 // and now change the function to intercept, on the child
307 if (NULL != ntdll_base_) {
308 // running a unit test
309 if (!::WriteProcessMemory(process_, target_, &intercepted_code,
310 bytes_to_write, &written))
311 return STATUS_UNSUCCESSFUL;
312 } else {
313 if (!WriteProtectedChildMemory(process_, target_, &intercepted_code,
314 bytes_to_write))
315 return STATUS_UNSUCCESSFUL;
318 return STATUS_SUCCESS;
321 bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk,
322 void* remote_thunk) {
323 ServiceEntry function_code;
324 SIZE_T read;
325 if (!::ReadProcessMemory(process_, target_, &function_code,
326 sizeof(function_code), &read))
327 return false;
329 if (sizeof(function_code) != read)
330 return false;
332 if (kJmp32 == function_code.mov_eax) {
333 // Plain old entry point patch. The relative jump address follows it.
334 ULONG relative = function_code.service_id;
336 // First, fix our copy of their patch.
337 relative += bit_cast<ULONG>(target_) - bit_cast<ULONG>(remote_thunk);
339 function_code.service_id = relative;
341 // And now, remember how to re-patch it.
342 ServiceFullThunk *full_thunk =
343 reinterpret_cast<ServiceFullThunk*>(remote_thunk);
345 const ULONG kJmp32Size = 5;
347 relative_jump_ = bit_cast<ULONG>(&full_thunk->internal_thunk) -
348 bit_cast<ULONG>(target_) - kJmp32Size;
351 // Save the verified code
352 memcpy(local_thunk, &function_code, sizeof(function_code));
354 return true;
357 bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const {
358 Wow64Entry function_code;
359 SIZE_T read;
360 if (!::ReadProcessMemory(process_, target_, &function_code,
361 sizeof(function_code), &read))
362 return false;
364 if (sizeof(function_code) != read)
365 return false;
367 if (kMovEax != function_code.mov_eax || kXorEcx != function_code.xor_ecx ||
368 kLeaEdx != function_code.lea_edx || kCallFs1 != function_code.call_fs1 ||
369 kCallFs2 != function_code.call_fs2 || kCallFs3 != function_code.call_fs3)
370 return false;
372 if ((kAddEsp1 == function_code.add_esp1 &&
373 kAddEsp2 == function_code.add_esp2 &&
374 kRet == function_code.ret) || kRet == function_code.add_esp1) {
375 // Save the verified code
376 memcpy(local_thunk, &function_code, sizeof(function_code));
377 return true;
380 return false;
383 bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const {
384 Wow64EntryW8 function_code;
385 SIZE_T read;
386 if (!::ReadProcessMemory(process_, target_, &function_code,
387 sizeof(function_code), &read))
388 return false;
390 if (sizeof(function_code) != read)
391 return false;
393 if (kMovEax != function_code.mov_eax || kCallFs1 != function_code.call_fs1 ||
394 kCallFs2 != function_code.call_fs2 ||
395 kCallFs3 != function_code.call_fs3 || kRet != function_code.ret) {
396 return false;
399 // Save the verified code
400 memcpy(local_thunk, &function_code, sizeof(function_code));
401 return true;
404 bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const {
405 ServiceEntryW8 function_code;
406 SIZE_T read;
407 if (!::ReadProcessMemory(process_, target_, &function_code,
408 sizeof(function_code), &read))
409 return false;
411 if (sizeof(function_code) != read)
412 return false;
414 if (kMovEax != function_code.mov_eax || kCallEip != function_code.call_eip ||
415 function_code.call_offset != 3 || kRet != function_code.ret_p ||
416 kMovEdxEsp != function_code.mov_edx_esp ||
417 kSysenter != function_code.sysenter || kRet2 != function_code.ret) {
418 return false;
421 // Save the verified code
422 memcpy(local_thunk, &function_code, sizeof(function_code));
424 return true;
427 } // namespace sandbox