Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / sandbox / win / src / service_resolver_32.cc
blob2ab30fede04f5ca98ae597991a209f3a9bc39835
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 USHORT kJmpEdx = 0xE2FF;
22 const USHORT kXorEcx = 0xC933;
23 const ULONG kLeaEdx = 0x0424548D;
24 const ULONG kCallFs1 = 0xC015FF64;
25 const USHORT kCallFs2 = 0;
26 const BYTE kCallFs3 = 0;
27 const BYTE kAddEsp1 = 0x83;
28 const USHORT kAddEsp2 = 0x4C4;
29 const BYTE kJmp32 = 0xE9;
30 const USHORT kSysenter = 0x340F;
32 // Service code for 32 bit systems.
33 // NOTE: on win2003 "call dword ptr [edx]" is "call edx".
34 struct ServiceEntry {
35 // This struct contains roughly the following code:
36 // 00 mov eax,25h
37 // 05 mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
38 // 0a call dword ptr [edx]
39 // 0c ret 2Ch
40 // 0f nop
41 BYTE mov_eax; // = B8
42 ULONG service_id;
43 BYTE mov_edx; // = BA
44 ULONG stub;
45 USHORT call_ptr_edx; // = FF 12
46 BYTE ret; // = C2
47 USHORT num_params;
48 BYTE nop;
51 // Service code for 32 bit Windows 8.
52 struct ServiceEntryW8 {
53 // This struct contains the following code:
54 // 00 b825000000 mov eax,25h
55 // 05 e803000000 call eip+3
56 // 0a c22c00 ret 2Ch
57 // 0d 8bd4 mov edx,esp
58 // 0f 0f34 sysenter
59 // 11 c3 ret
60 // 12 8bff mov edi,edi
61 BYTE mov_eax; // = B8
62 ULONG service_id;
63 BYTE call_eip; // = E8
64 ULONG call_offset;
65 BYTE ret_p; // = C2
66 USHORT num_params;
67 USHORT mov_edx_esp; // = BD D4
68 USHORT sysenter; // = 0F 34
69 BYTE ret; // = C3
70 USHORT nop;
73 // Service code for a 32 bit process running on a 64 bit os.
74 struct Wow64Entry {
75 // This struct may contain one of two versions of code:
76 // 1. For XP, Vista and 2K3:
77 // 00 b825000000 mov eax, 25h
78 // 05 33c9 xor ecx, ecx
79 // 07 8d542404 lea edx, [esp + 4]
80 // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
81 // 12 c22c00 ret 2Ch
83 // 2. For Windows 7:
84 // 00 b825000000 mov eax, 25h
85 // 05 33c9 xor ecx, ecx
86 // 07 8d542404 lea edx, [esp + 4]
87 // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
88 // 12 83c404 add esp, 4
89 // 15 c22c00 ret 2Ch
91 // So we base the structure on the bigger one:
92 BYTE mov_eax; // = B8
93 ULONG service_id;
94 USHORT xor_ecx; // = 33 C9
95 ULONG lea_edx; // = 8D 54 24 04
96 ULONG call_fs1; // = 64 FF 15 C0
97 USHORT call_fs2; // = 00 00
98 BYTE call_fs3; // = 00
99 BYTE add_esp1; // = 83 or ret
100 USHORT add_esp2; // = C4 04 or num_params
101 BYTE ret; // = C2
102 USHORT num_params;
105 // Service code for a 32 bit process running on 64 bit Windows 8.
106 struct Wow64EntryW8 {
107 // 00 b825000000 mov eax, 25h
108 // 05 64ff15c0000000 call dword ptr fs:[0C0h]
109 // 0b c22c00 ret 2Ch
110 // 0f 90 nop
111 BYTE mov_eax; // = B8
112 ULONG service_id;
113 ULONG call_fs1; // = 64 FF 15 C0
114 USHORT call_fs2; // = 00 00
115 BYTE call_fs3; // = 00
116 BYTE ret; // = C2
117 USHORT num_params;
118 BYTE nop;
121 // Make sure that relaxed patching works as expected.
122 const size_t kMinServiceSize = offsetof(ServiceEntry, ret);
123 static_assert(sizeof(ServiceEntryW8) >= kMinServiceSize,
124 "wrong service length");
125 static_assert(sizeof(Wow64Entry) >= kMinServiceSize, "wrong service length");
126 static_assert(sizeof(Wow64EntryW8) >= kMinServiceSize, "wrong service length");
128 struct ServiceFullThunk {
129 union {
130 ServiceEntry original;
131 ServiceEntryW8 original_w8;
132 Wow64Entry wow_64;
133 Wow64EntryW8 wow_64_w8;
135 int internal_thunk; // Dummy member to the beginning of the internal thunk.
138 #pragma pack(pop)
140 }; // namespace
142 namespace sandbox {
144 NTSTATUS ServiceResolverThunk::Setup(const void* target_module,
145 const void* interceptor_module,
146 const char* target_name,
147 const char* interceptor_name,
148 const void* interceptor_entry_point,
149 void* thunk_storage,
150 size_t storage_bytes,
151 size_t* storage_used) {
152 NTSTATUS ret = Init(target_module, interceptor_module, target_name,
153 interceptor_name, interceptor_entry_point,
154 thunk_storage, storage_bytes);
155 if (!NT_SUCCESS(ret))
156 return ret;
158 relative_jump_ = 0;
159 size_t thunk_bytes = GetThunkSize();
160 scoped_ptr<char[]> thunk_buffer(new char[thunk_bytes]);
161 ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(
162 thunk_buffer.get());
164 if (!IsFunctionAService(&thunk->original) &&
165 (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage)))
166 return STATUS_UNSUCCESSFUL;
168 ret = PerformPatch(thunk, thunk_storage);
170 if (NULL != storage_used)
171 *storage_used = thunk_bytes;
173 return ret;
176 size_t ServiceResolverThunk::GetThunkSize() const {
177 return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize();
180 NTSTATUS ServiceResolverThunk::CopyThunk(const void* target_module,
181 const char* target_name,
182 BYTE* thunk_storage,
183 size_t storage_bytes,
184 size_t* storage_used) {
185 NTSTATUS ret = ResolveTarget(target_module, target_name, &target_);
186 if (!NT_SUCCESS(ret))
187 return ret;
189 size_t thunk_bytes = GetThunkSize();
190 if (storage_bytes < thunk_bytes)
191 return STATUS_UNSUCCESSFUL;
193 ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(thunk_storage);
195 if (!IsFunctionAService(&thunk->original) &&
196 (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) {
197 return STATUS_UNSUCCESSFUL;
200 if (NULL != storage_used)
201 *storage_used = thunk_bytes;
203 return ret;
206 bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const {
207 ServiceEntry function_code;
208 SIZE_T read;
209 if (!::ReadProcessMemory(process_, target_, &function_code,
210 sizeof(function_code), &read))
211 return false;
213 if (sizeof(function_code) != read)
214 return false;
216 if (kMovEax != function_code.mov_eax ||
217 kMovEdx != function_code.mov_edx ||
218 (kCallPtrEdx != function_code.call_ptr_edx &&
219 kCallEdx != function_code.call_ptr_edx) ||
220 kRet != function_code.ret)
221 return false;
223 // Find the system call pointer if we don't already have it.
224 if (kCallEdx != function_code.call_ptr_edx) {
225 DWORD ki_system_call;
226 if (!::ReadProcessMemory(process_,
227 bit_cast<const void*>(function_code.stub),
228 &ki_system_call, sizeof(ki_system_call), &read))
229 return false;
231 if (sizeof(ki_system_call) != read)
232 return false;
234 HMODULE module_1, module_2;
235 // last check, call_stub should point to a KiXXSystemCall function on ntdll
236 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
237 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
238 bit_cast<const wchar_t*>(ki_system_call), &module_1))
239 return false;
241 if (NULL != ntdll_base_) {
242 // This path is only taken when running the unit tests. We want to be
243 // able to patch a buffer in memory, so target_ is not inside ntdll.
244 module_2 = ntdll_base_;
245 } else {
246 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
247 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
248 reinterpret_cast<const wchar_t*>(target_),
249 &module_2))
250 return false;
253 if (module_1 != module_2)
254 return false;
257 // Save the verified code
258 memcpy(local_thunk, &function_code, sizeof(function_code));
260 return true;
263 NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk,
264 void* remote_thunk) {
265 ServiceEntry intercepted_code;
266 size_t bytes_to_write = sizeof(intercepted_code);
267 ServiceFullThunk *full_local_thunk = reinterpret_cast<ServiceFullThunk*>(
268 local_thunk);
269 ServiceFullThunk *full_remote_thunk = reinterpret_cast<ServiceFullThunk*>(
270 remote_thunk);
272 // patch the original code
273 memcpy(&intercepted_code, &full_local_thunk->original,
274 sizeof(intercepted_code));
275 intercepted_code.mov_eax = kMovEax;
276 intercepted_code.service_id = full_local_thunk->original.service_id;
277 intercepted_code.mov_edx = kMovEdx;
278 intercepted_code.stub = bit_cast<ULONG>(&full_remote_thunk->internal_thunk);
279 intercepted_code.call_ptr_edx = kJmpEdx;
280 bytes_to_write = kMinServiceSize;
282 if (relative_jump_) {
283 intercepted_code.mov_eax = kJmp32;
284 intercepted_code.service_id = relative_jump_;
285 bytes_to_write = offsetof(ServiceEntry, mov_edx);
288 // setup the thunk
289 SetInternalThunk(&full_local_thunk->internal_thunk, GetInternalThunkSize(),
290 remote_thunk, interceptor_);
292 size_t thunk_size = GetThunkSize();
294 // copy the local thunk buffer to the child
295 SIZE_T written;
296 if (!::WriteProcessMemory(process_, remote_thunk, local_thunk,
297 thunk_size, &written))
298 return STATUS_UNSUCCESSFUL;
300 if (thunk_size != written)
301 return STATUS_UNSUCCESSFUL;
303 // and now change the function to intercept, on the child
304 if (NULL != ntdll_base_) {
305 // running a unit test
306 if (!::WriteProcessMemory(process_, target_, &intercepted_code,
307 bytes_to_write, &written))
308 return STATUS_UNSUCCESSFUL;
309 } else {
310 if (!WriteProtectedChildMemory(process_, target_, &intercepted_code,
311 bytes_to_write))
312 return STATUS_UNSUCCESSFUL;
315 return STATUS_SUCCESS;
318 bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk,
319 void* remote_thunk) {
320 ServiceEntry function_code;
321 SIZE_T read;
322 if (!::ReadProcessMemory(process_, target_, &function_code,
323 sizeof(function_code), &read))
324 return false;
326 if (sizeof(function_code) != read)
327 return false;
329 if (kJmp32 == function_code.mov_eax) {
330 // Plain old entry point patch. The relative jump address follows it.
331 ULONG relative = function_code.service_id;
333 // First, fix our copy of their patch.
334 relative += bit_cast<ULONG>(target_) - bit_cast<ULONG>(remote_thunk);
336 function_code.service_id = relative;
338 // And now, remember how to re-patch it.
339 ServiceFullThunk *full_thunk =
340 reinterpret_cast<ServiceFullThunk*>(remote_thunk);
342 const ULONG kJmp32Size = 5;
344 relative_jump_ = bit_cast<ULONG>(&full_thunk->internal_thunk) -
345 bit_cast<ULONG>(target_) - kJmp32Size;
348 // Save the verified code
349 memcpy(local_thunk, &function_code, sizeof(function_code));
351 return true;
354 bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const {
355 Wow64Entry function_code;
356 SIZE_T read;
357 if (!::ReadProcessMemory(process_, target_, &function_code,
358 sizeof(function_code), &read))
359 return false;
361 if (sizeof(function_code) != read)
362 return false;
364 if (kMovEax != function_code.mov_eax || kXorEcx != function_code.xor_ecx ||
365 kLeaEdx != function_code.lea_edx || kCallFs1 != function_code.call_fs1 ||
366 kCallFs2 != function_code.call_fs2 || kCallFs3 != function_code.call_fs3)
367 return false;
369 if ((kAddEsp1 == function_code.add_esp1 &&
370 kAddEsp2 == function_code.add_esp2 &&
371 kRet == function_code.ret) || kRet == function_code.add_esp1) {
372 // Save the verified code
373 memcpy(local_thunk, &function_code, sizeof(function_code));
374 return true;
377 return false;
380 bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const {
381 Wow64EntryW8 function_code;
382 SIZE_T read;
383 if (!::ReadProcessMemory(process_, target_, &function_code,
384 sizeof(function_code), &read))
385 return false;
387 if (sizeof(function_code) != read)
388 return false;
390 if (kMovEax != function_code.mov_eax || kCallFs1 != function_code.call_fs1 ||
391 kCallFs2 != function_code.call_fs2 ||
392 kCallFs3 != function_code.call_fs3 || kRet != function_code.ret) {
393 return false;
396 // Save the verified code
397 memcpy(local_thunk, &function_code, sizeof(function_code));
398 return true;
401 bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const {
402 ServiceEntryW8 function_code;
403 SIZE_T read;
404 if (!::ReadProcessMemory(process_, target_, &function_code,
405 sizeof(function_code), &read))
406 return false;
408 if (sizeof(function_code) != read)
409 return false;
411 if (kMovEax != function_code.mov_eax || kCallEip != function_code.call_eip ||
412 function_code.call_offset != 3 || kRet != function_code.ret_p ||
413 kMovEdxEsp != function_code.mov_edx_esp ||
414 kSysenter != function_code.sysenter || kRet2 != function_code.ret) {
415 return false;
418 // Save the verified code
419 memcpy(local_thunk, &function_code, sizeof(function_code));
421 return true;
424 } // namespace sandbox