security/sandbox/win/src/service_resolver_32.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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;
    52 };
    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 };
    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 };
   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 };
   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);
   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 };
   140 #pragma pack(pop)
   142 };  // namespace
   144 namespace sandbox {
   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;
   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());
   166   if (!IsFunctionAService(&thunk->original) &&
   167       (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage)))
   168     return STATUS_UNSUCCESSFUL;
   170   ret = PerformPatch(thunk, thunk_storage);
   172   if (NULL != storage_used)
   173     *storage_used = thunk_bytes;
   175   return ret;
   176 }
   178 size_t ServiceResolverThunk::GetThunkSize() const {
   179   return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize();
   180 }
   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;
   189   if (sizeof(function_code) != read)
   190     return false;
   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;
   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;
   207     if (sizeof(ki_system_call) != read)
   208       return false;
   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;
   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     }
   229     if (module_1 != module_2)
   230       return false;
   231   }
   233   // Save the verified code
   234   memcpy(local_thunk, &function_code, sizeof(function_code));
   236   return true;
   237 }
   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);
   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;
   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   }
   264   // setup the thunk
   265   SetInternalThunk(&full_local_thunk->internal_thunk, GetInternalThunkSize(),
   266                    remote_thunk, interceptor_);
   268   size_t thunk_size = GetThunkSize();
   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;
   276   if (thunk_size != written)
   277     return STATUS_UNSUCCESSFUL;
   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   }
   291   return STATUS_SUCCESS;
   292 }
   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;
   302   if (sizeof(function_code) != read)
   303     return false;
   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;
   309     // First, fix our copy of their patch.
   310     relative += bit_cast<ULONG>(target_) - bit_cast<ULONG>(remote_thunk);
   312     function_code.service_id = relative;
   314     // And now, remember how to re-patch it.
   315     ServiceFullThunk *full_thunk =
   316         reinterpret_cast<ServiceFullThunk*>(remote_thunk);
   318     const ULONG kJmp32Size = 5;
   320     relative_jump_ = bit_cast<ULONG>(&full_thunk->internal_thunk) -
   321                      bit_cast<ULONG>(target_) - kJmp32Size;
   322   }
   324   // Save the verified code
   325   memcpy(local_thunk, &function_code, sizeof(function_code));
   327   return true;
   328 }
   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;
   337   if (sizeof(function_code) != read)
   338     return false;
   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;
   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   }
   353   return false;
   354 }
   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;
   363   if (sizeof(function_code) != read)
   364     return false;
   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   }
   372   // Save the verified code
   373   memcpy(local_thunk, &function_code, sizeof(function_code));
   374   return true;
   375 }
   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;
   384   if (sizeof(function_code) != read)
   385     return false;
   387   if (kMovEax != function_code.mov_eax ||
   388       function_code.service_id > kMaxService)
   389     return false;
   391   // Save the verified code
   392   memcpy(local_thunk, &function_code, sizeof(function_code));
   394   return true;
   395 }
   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;
   404   if (sizeof(function_code) != read)
   405     return false;
   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   }
   414   // Save the verified code
   415   memcpy(local_thunk, &function_code, sizeof(function_code));
   417   return true;
   418 }
   420 }  // namespace sandbox

mercurial