- add sources.
[platform/framework/web/crosswalk.git] / src / sandbox / win / src / service_resolver_32.cc
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.
4
5 #include "sandbox/win/src/service_resolver.h"
6
7 #include "base/memory/scoped_ptr.h"
8 #include "sandbox/win/src/win_utils.h"
9
10 namespace {
11 #pragma pack(push, 1)
12
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;
32
33 const int kMaxService = 1000;
34
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;
52 };
53
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;
74 };
75
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
85   //
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
93   //
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;
106 };
107
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;
122 };
123
124 // Make sure that relaxed patching works as expected.
125 const size_t kMinServiceSize = offsetof(ServiceEntry, ret);
126 COMPILE_ASSERT(sizeof(ServiceEntryW8) >= kMinServiceSize, wrong_service_len);
127 COMPILE_ASSERT(sizeof(Wow64Entry) >= kMinServiceSize, wrong_service_len);
128 COMPILE_ASSERT(sizeof(Wow64EntryW8) >= kMinServiceSize, wrong_service_len);
129
130 struct ServiceFullThunk {
131   union {
132     ServiceEntry original;
133     ServiceEntryW8 original_w8;
134     Wow64Entry wow_64;
135     Wow64EntryW8 wow_64_w8;
136   };
137   int internal_thunk;  // Dummy member to the beginning of the internal thunk.
138 };
139
140 #pragma pack(pop)
141
142 };  // namespace
143
144 namespace sandbox {
145
146 NTSTATUS ServiceResolverThunk::Setup(const void* target_module,
147                                      const void* interceptor_module,
148                                      const char* target_name,
149                                      const char* interceptor_name,
150                                      const void* interceptor_entry_point,
151                                      void* thunk_storage,
152                                      size_t storage_bytes,
153                                      size_t* storage_used) {
154   NTSTATUS ret = Init(target_module, interceptor_module, target_name,
155                       interceptor_name, interceptor_entry_point,
156                       thunk_storage, storage_bytes);
157   if (!NT_SUCCESS(ret))
158     return ret;
159
160   relative_jump_ = 0;
161   size_t thunk_bytes = GetThunkSize();
162   scoped_ptr<char[]> thunk_buffer(new char[thunk_bytes]);
163   ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(
164                                 thunk_buffer.get());
165
166   if (!IsFunctionAService(&thunk->original) &&
167       (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage)))
168     return STATUS_UNSUCCESSFUL;
169
170   ret = PerformPatch(thunk, thunk_storage);
171
172   if (NULL != storage_used)
173     *storage_used = thunk_bytes;
174
175   return ret;
176 }
177
178 size_t ServiceResolverThunk::GetThunkSize() const {
179   return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize();
180 }
181
182 bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const {
183   ServiceEntry function_code;
184   SIZE_T read;
185   if (!::ReadProcessMemory(process_, target_, &function_code,
186                            sizeof(function_code), &read))
187     return false;
188
189   if (sizeof(function_code) != read)
190     return false;
191
192   if (kMovEax != function_code.mov_eax ||
193       kMovEdx != function_code.mov_edx ||
194       (kCallPtrEdx != function_code.call_ptr_edx &&
195        kCallEdx != function_code.call_ptr_edx) ||
196       kRet != function_code.ret)
197     return false;
198
199   // Find the system call pointer if we don't already have it.
200   if (kCallEdx != function_code.call_ptr_edx) {
201     DWORD ki_system_call;
202     if (!::ReadProcessMemory(process_,
203                              bit_cast<const void*>(function_code.stub),
204                              &ki_system_call, sizeof(ki_system_call), &read))
205       return false;
206
207     if (sizeof(ki_system_call) != read)
208       return false;
209
210     HMODULE module_1, module_2;
211     // last check, call_stub should point to a KiXXSystemCall function on ntdll
212     if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
213                                GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
214                            bit_cast<const wchar_t*>(ki_system_call), &module_1))
215       return false;
216
217     if (NULL != ntdll_base_) {
218       // This path is only taken when running the unit tests. We want to be
219       // able to patch a buffer in memory, so target_ is not inside ntdll.
220       module_2 = ntdll_base_;
221     } else {
222       if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
223                                  GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
224                              reinterpret_cast<const wchar_t*>(target_),
225                              &module_2))
226         return false;
227     }
228
229     if (module_1 != module_2)
230       return false;
231   }
232
233   // Save the verified code
234   memcpy(local_thunk, &function_code, sizeof(function_code));
235
236   return true;
237 }
238
239 NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk,
240                                             void* remote_thunk) {
241   ServiceEntry intercepted_code;
242   size_t bytes_to_write = sizeof(intercepted_code);
243   ServiceFullThunk *full_local_thunk = reinterpret_cast<ServiceFullThunk*>(
244       local_thunk);
245   ServiceFullThunk *full_remote_thunk = reinterpret_cast<ServiceFullThunk*>(
246       remote_thunk);
247
248   // patch the original code
249   memcpy(&intercepted_code, &full_local_thunk->original,
250          sizeof(intercepted_code));
251   intercepted_code.mov_eax = kMovEax;
252   intercepted_code.service_id = full_local_thunk->original.service_id;
253   intercepted_code.mov_edx = kMovEdx;
254   intercepted_code.stub = bit_cast<ULONG>(&full_remote_thunk->internal_thunk);
255   intercepted_code.call_ptr_edx = kJmpEdx;
256   bytes_to_write = kMinServiceSize;
257
258   if (relative_jump_) {
259     intercepted_code.mov_eax = kJmp32;
260     intercepted_code.service_id = relative_jump_;
261     bytes_to_write = offsetof(ServiceEntry, mov_edx);
262   }
263
264   // setup the thunk
265   SetInternalThunk(&full_local_thunk->internal_thunk, GetInternalThunkSize(),
266                    remote_thunk, interceptor_);
267
268   size_t thunk_size = GetThunkSize();
269
270   // copy the local thunk buffer to the child
271   SIZE_T written;
272   if (!::WriteProcessMemory(process_, remote_thunk, local_thunk,
273                             thunk_size, &written))
274     return STATUS_UNSUCCESSFUL;
275
276   if (thunk_size != written)
277     return STATUS_UNSUCCESSFUL;
278
279   // and now change the function to intercept, on the child
280   if (NULL != ntdll_base_) {
281     // running a unit test
282     if (!::WriteProcessMemory(process_, target_, &intercepted_code,
283                               bytes_to_write, &written))
284       return STATUS_UNSUCCESSFUL;
285   } else {
286     if (!WriteProtectedChildMemory(process_, target_, &intercepted_code,
287                                    bytes_to_write))
288       return STATUS_UNSUCCESSFUL;
289   }
290
291   return STATUS_SUCCESS;
292 }
293
294 bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk,
295                                                 void* remote_thunk) {
296   ServiceEntry function_code;
297   SIZE_T read;
298   if (!::ReadProcessMemory(process_, target_, &function_code,
299                            sizeof(function_code), &read))
300     return false;
301
302   if (sizeof(function_code) != read)
303     return false;
304
305   if (kJmp32 == function_code.mov_eax) {
306     // Plain old entry point patch. The relative jump address follows it.
307     ULONG relative = function_code.service_id;
308
309     // First, fix our copy of their patch.
310     relative += bit_cast<ULONG>(target_) - bit_cast<ULONG>(remote_thunk);
311
312     function_code.service_id = relative;
313
314     // And now, remember how to re-patch it.
315     ServiceFullThunk *full_thunk =
316         reinterpret_cast<ServiceFullThunk*>(remote_thunk);
317
318     const ULONG kJmp32Size = 5;
319
320     relative_jump_ = bit_cast<ULONG>(&full_thunk->internal_thunk) -
321                      bit_cast<ULONG>(target_) - kJmp32Size;
322   }
323
324   // Save the verified code
325   memcpy(local_thunk, &function_code, sizeof(function_code));
326
327   return true;
328 }
329
330 bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const {
331   Wow64Entry function_code;
332   SIZE_T read;
333   if (!::ReadProcessMemory(process_, target_, &function_code,
334                            sizeof(function_code), &read))
335     return false;
336
337   if (sizeof(function_code) != read)
338     return false;
339
340   if (kMovEax != function_code.mov_eax || kXorEcx != function_code.xor_ecx ||
341       kLeaEdx != function_code.lea_edx || kCallFs1 != function_code.call_fs1 ||
342       kCallFs2 != function_code.call_fs2 || kCallFs3 != function_code.call_fs3)
343     return false;
344
345   if ((kAddEsp1 == function_code.add_esp1 &&
346        kAddEsp2 == function_code.add_esp2 &&
347        kRet == function_code.ret) || kRet == function_code.add_esp1) {
348     // Save the verified code
349     memcpy(local_thunk, &function_code, sizeof(function_code));
350     return true;
351   }
352
353   return false;
354 }
355
356 bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const {
357   Wow64EntryW8 function_code;
358   SIZE_T read;
359   if (!::ReadProcessMemory(process_, target_, &function_code,
360                            sizeof(function_code), &read))
361     return false;
362
363   if (sizeof(function_code) != read)
364     return false;
365
366   if (kMovEax != function_code.mov_eax || kCallFs1 != function_code.call_fs1 ||
367       kCallFs2 != function_code.call_fs2 ||
368       kCallFs3 != function_code.call_fs3 || kRet != function_code.ret) {
369     return false;
370   }
371
372   // Save the verified code
373   memcpy(local_thunk, &function_code, sizeof(function_code));
374   return true;
375 }
376
377 bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const {
378   ServiceEntry function_code;
379   SIZE_T read;
380   if (!::ReadProcessMemory(process_, target_, &function_code,
381                            sizeof(function_code), &read))
382     return false;
383
384   if (sizeof(function_code) != read)
385     return false;
386
387   if (kMovEax != function_code.mov_eax ||
388       function_code.service_id > kMaxService)
389     return false;
390
391   // Save the verified code
392   memcpy(local_thunk, &function_code, sizeof(function_code));
393
394   return true;
395 }
396
397 bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const {
398   ServiceEntryW8 function_code;
399   SIZE_T read;
400   if (!::ReadProcessMemory(process_, target_, &function_code,
401                            sizeof(function_code), &read))
402     return false;
403
404   if (sizeof(function_code) != read)
405     return false;
406
407   if (kMovEax != function_code.mov_eax || kCallEip != function_code.call_eip ||
408       function_code.call_offset != 3 || kRet != function_code.ret_p ||
409       kMovEdxEsp != function_code.mov_edx_esp ||
410       kSysenter != function_code.sysenter || kRet2 != function_code.ret) {
411     return false;
412   }
413
414   // Save the verified code
415   memcpy(local_thunk, &function_code, sizeof(function_code));
416
417   return true;
418 }
419
420 }  // namespace sandbox