Home | History | Annotate | Download | only in src
      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
    421