| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright (c) 2011 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/wow_helper/service64_resolver.h" |  | 
| 6 |  | 
| 7 #include <limits.h> |  | 
| 8 #include <stddef.h> |  | 
| 9 |  | 
| 10 #include "base/bit_cast.h" |  | 
| 11 #include "base/memory/scoped_ptr.h" |  | 
| 12 #include "sandbox/win/wow_helper/target_code.h" |  | 
| 13 |  | 
| 14 namespace { |  | 
| 15 #pragma pack(push, 1) |  | 
| 16 |  | 
| 17 const BYTE kMovEax = 0xB8; |  | 
| 18 const BYTE kMovEdx = 0xBA; |  | 
| 19 const USHORT kCallPtrEdx = 0x12FF; |  | 
| 20 const BYTE kRet = 0xC2; |  | 
| 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 ULONG kCallFs2Ret = 0xC2000000; |  | 
| 27 const BYTE kPopEdx = 0x5A; |  | 
| 28 const BYTE kPushEdx = 0x52; |  | 
| 29 const BYTE kPush32 = 0x68; |  | 
| 30 |  | 
| 31 const ULONG kMmovR10EcxMovEax = 0xB8D18B4C; |  | 
| 32 const USHORT kSyscall = 0x050F; |  | 
| 33 const BYTE kRetNp = 0xC3; |  | 
| 34 const BYTE kPad = 0x66; |  | 
| 35 const USHORT kNop16 = 0x9066; |  | 
| 36 const BYTE kRelJmp = 0xE9; |  | 
| 37 |  | 
| 38 const ULONG kXorRaxMovEax = 0xB8C03148; |  | 
| 39 const ULONG kSaveRcx = 0x10488948; |  | 
| 40 const ULONG kMovRcxRaxJmp = 0xE9C88B48; |  | 
| 41 |  | 
| 42 // Service code for 64 bit systems. |  | 
| 43 struct ServiceEntry { |  | 
| 44   // this struct contains roughly the following code: |  | 
| 45   // mov     r10,rcx |  | 
| 46   // mov     eax,52h |  | 
| 47   // syscall |  | 
| 48   // ret |  | 
| 49   // xchg    ax,ax |  | 
| 50   // xchg    ax,ax |  | 
| 51 |  | 
| 52   ULONG mov_r10_ecx_mov_eax;  // = 4C 8B D1 B8 |  | 
| 53   ULONG service_id; |  | 
| 54   USHORT syscall;             // = 0F 05 |  | 
| 55   BYTE ret;                   // = C3 |  | 
| 56   BYTE pad;                   // = 66 |  | 
| 57   USHORT xchg_ax_ax1;         // = 66 90 |  | 
| 58   USHORT xchg_ax_ax2;         // = 66 90 |  | 
| 59 }; |  | 
| 60 |  | 
| 61 struct Redirected { |  | 
| 62   // this struct contains roughly the following code: |  | 
| 63   // jmp    relative_32 |  | 
| 64   // xchg   ax,ax       // 3 byte nop |  | 
| 65 |  | 
| 66   Redirected() { |  | 
| 67     jmp = kRelJmp; |  | 
| 68     relative = 0; |  | 
| 69     pad = kPad; |  | 
| 70     xchg_ax_ax = kNop16; |  | 
| 71   }; |  | 
| 72   BYTE jmp;             // = E9 |  | 
| 73   ULONG relative; |  | 
| 74   BYTE pad;             // = 66 |  | 
| 75   USHORT xchg_ax_ax;    // = 66 90 |  | 
| 76 }; |  | 
| 77 |  | 
| 78 struct InternalThunk { |  | 
| 79   // this struct contains roughly the following code: |  | 
| 80   // xor rax,rax |  | 
| 81   // mov eax, 0x00080000              // Thunk storage. |  | 
| 82   // mov [rax]PatchInfo.service, rcx  // Save first argument. |  | 
| 83   // mov rcx, rax |  | 
| 84   // jmp relative_to_interceptor |  | 
| 85 |  | 
| 86   InternalThunk() { |  | 
| 87     xor_rax_mov_eax = kXorRaxMovEax; |  | 
| 88     patch_info = 0; |  | 
| 89     save_rcx = kSaveRcx; |  | 
| 90     mov_rcx_rax_jmp = kMovRcxRaxJmp; |  | 
| 91     relative = 0; |  | 
| 92   }; |  | 
| 93   ULONG xor_rax_mov_eax;  // = 48 31 C0 B8 |  | 
| 94   ULONG patch_info; |  | 
| 95   ULONG save_rcx;         // = 48 89 48 10 |  | 
| 96   ULONG mov_rcx_rax_jmp;  // = 48 8b c8 e9 |  | 
| 97   ULONG relative; |  | 
| 98 }; |  | 
| 99 |  | 
| 100 struct ServiceFullThunk { |  | 
| 101   sandbox::PatchInfo patch_info; |  | 
| 102   ServiceEntry original; |  | 
| 103   InternalThunk internal_thunk; |  | 
| 104 }; |  | 
| 105 |  | 
| 106 #pragma pack(pop) |  | 
| 107 |  | 
| 108 // Simple utility function to write to a buffer on the child, if the memery has |  | 
| 109 // write protection attributes. |  | 
| 110 // Arguments: |  | 
| 111 // child_process (in): process to write to. |  | 
| 112 // address (out): memory position on the child to write to. |  | 
| 113 // buffer (in): local buffer with the data to write . |  | 
| 114 // length (in): number of bytes to write. |  | 
| 115 // Returns true on success. |  | 
| 116 bool WriteProtectedChildMemory(HANDLE child_process, |  | 
| 117                                void* address, |  | 
| 118                                const void* buffer, |  | 
| 119                                size_t length) { |  | 
| 120   // first, remove the protections |  | 
| 121   DWORD old_protection; |  | 
| 122   if (!::VirtualProtectEx(child_process, address, length, |  | 
| 123                           PAGE_WRITECOPY, &old_protection)) |  | 
| 124     return false; |  | 
| 125 |  | 
| 126   SIZE_T written; |  | 
| 127   bool ok = ::WriteProcessMemory(child_process, address, buffer, length, |  | 
| 128                                  &written) && (length == written); |  | 
| 129 |  | 
| 130   // always attempt to restore the original protection |  | 
| 131   if (!::VirtualProtectEx(child_process, address, length, |  | 
| 132                           old_protection, &old_protection)) |  | 
| 133     return false; |  | 
| 134 |  | 
| 135   return ok; |  | 
| 136 } |  | 
| 137 |  | 
| 138 // Get pointers to the functions that we need from ntdll.dll. |  | 
| 139 NTSTATUS ResolveNtdll(sandbox::PatchInfo* patch_info) { |  | 
| 140   wchar_t* ntdll_name = L"ntdll.dll"; |  | 
| 141   HMODULE ntdll = ::GetModuleHandle(ntdll_name); |  | 
| 142   if (!ntdll) |  | 
| 143     return STATUS_PROCEDURE_NOT_FOUND; |  | 
| 144 |  | 
| 145   void* signal = ::GetProcAddress(ntdll, "NtSignalAndWaitForSingleObject"); |  | 
| 146   if (!signal) |  | 
| 147     return STATUS_PROCEDURE_NOT_FOUND; |  | 
| 148 |  | 
| 149   patch_info->signal_and_wait = |  | 
| 150       reinterpret_cast<NtSignalAndWaitForSingleObjectFunction>(signal); |  | 
| 151 |  | 
| 152   return STATUS_SUCCESS; |  | 
| 153 } |  | 
| 154 |  | 
| 155 };  // namespace |  | 
| 156 |  | 
| 157 namespace sandbox { |  | 
| 158 |  | 
| 159 NTSTATUS ResolverThunk::Init(const void* target_module, |  | 
| 160                              const void* interceptor_module, |  | 
| 161                              const char* target_name, |  | 
| 162                              const char* interceptor_name, |  | 
| 163                              const void* interceptor_entry_point, |  | 
| 164                              void* thunk_storage, |  | 
| 165                              size_t storage_bytes) { |  | 
| 166   if (NULL == thunk_storage || 0 == storage_bytes || |  | 
| 167       NULL == target_module || NULL == target_name) |  | 
| 168     return STATUS_INVALID_PARAMETER; |  | 
| 169 |  | 
| 170   if (storage_bytes < GetThunkSize()) |  | 
| 171     return STATUS_BUFFER_TOO_SMALL; |  | 
| 172 |  | 
| 173   NTSTATUS ret = STATUS_SUCCESS; |  | 
| 174   if (NULL == interceptor_entry_point) { |  | 
| 175     ret = ResolveInterceptor(interceptor_module, interceptor_name, |  | 
| 176                              &interceptor_entry_point); |  | 
| 177     if (!NT_SUCCESS(ret)) |  | 
| 178       return ret; |  | 
| 179   } |  | 
| 180 |  | 
| 181   ret = ResolveTarget(target_module, target_name, &target_); |  | 
| 182   if (!NT_SUCCESS(ret)) |  | 
| 183     return ret; |  | 
| 184 |  | 
| 185   interceptor_ = interceptor_entry_point; |  | 
| 186 |  | 
| 187   return ret; |  | 
| 188 } |  | 
| 189 |  | 
| 190 NTSTATUS ResolverThunk::ResolveInterceptor(const void* interceptor_module, |  | 
| 191                                            const char* interceptor_name, |  | 
| 192                                            const void** address) { |  | 
| 193   return STATUS_NOT_IMPLEMENTED; |  | 
| 194 } |  | 
| 195 |  | 
| 196 NTSTATUS ResolverThunk::ResolveTarget(const void* module, |  | 
| 197                                       const char* function_name, |  | 
| 198                                       void** address) { |  | 
| 199   return STATUS_NOT_IMPLEMENTED; |  | 
| 200 } |  | 
| 201 |  | 
| 202 NTSTATUS Service64ResolverThunk::Setup(const void* target_module, |  | 
| 203                                        const void* interceptor_module, |  | 
| 204                                        const char* target_name, |  | 
| 205                                        const char* interceptor_name, |  | 
| 206                                        const void* interceptor_entry_point, |  | 
| 207                                        void* thunk_storage, |  | 
| 208                                        size_t storage_bytes, |  | 
| 209                                        size_t* storage_used) { |  | 
| 210   NTSTATUS ret = Init(target_module, interceptor_module, target_name, |  | 
| 211                       interceptor_name, interceptor_entry_point, |  | 
| 212                       thunk_storage, storage_bytes); |  | 
| 213   if (!NT_SUCCESS(ret)) |  | 
| 214     return ret; |  | 
| 215 |  | 
| 216   size_t thunk_bytes = GetThunkSize(); |  | 
| 217   scoped_ptr<char[]> thunk_buffer(new char[thunk_bytes]); |  | 
| 218   ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>( |  | 
| 219                                 thunk_buffer.get()); |  | 
| 220 |  | 
| 221   if (!IsFunctionAService(&thunk->original)) |  | 
| 222     return STATUS_UNSUCCESSFUL; |  | 
| 223 |  | 
| 224   ret = PerformPatch(thunk, thunk_storage); |  | 
| 225 |  | 
| 226   if (NULL != storage_used) |  | 
| 227     *storage_used = thunk_bytes; |  | 
| 228 |  | 
| 229   return ret; |  | 
| 230 } |  | 
| 231 |  | 
| 232 NTSTATUS Service64ResolverThunk::ResolveInterceptor( |  | 
| 233     const void* interceptor_module, |  | 
| 234     const char* interceptor_name, |  | 
| 235     const void** address) { |  | 
| 236   // After all, we are using a locally mapped version of the exe, so the |  | 
| 237   // action is the same as for a target function. |  | 
| 238   return ResolveTarget(interceptor_module, interceptor_name, |  | 
| 239                        const_cast<void**>(address)); |  | 
| 240 } |  | 
| 241 |  | 
| 242 // In this case all the work is done from the parent, so resolve is |  | 
| 243 // just a simple GetProcAddress. |  | 
| 244 NTSTATUS Service64ResolverThunk::ResolveTarget(const void* module, |  | 
| 245                                              const char* function_name, |  | 
| 246                                              void** address) { |  | 
| 247   if (NULL == module) |  | 
| 248     return STATUS_UNSUCCESSFUL; |  | 
| 249 |  | 
| 250   *address = ::GetProcAddress(bit_cast<HMODULE>(module), function_name); |  | 
| 251 |  | 
| 252   if (NULL == *address) |  | 
| 253     return STATUS_UNSUCCESSFUL; |  | 
| 254 |  | 
| 255   return STATUS_SUCCESS; |  | 
| 256 } |  | 
| 257 |  | 
| 258 size_t Service64ResolverThunk::GetThunkSize() const { |  | 
| 259   return sizeof(ServiceFullThunk); |  | 
| 260 } |  | 
| 261 |  | 
| 262 bool Service64ResolverThunk::IsFunctionAService(void* local_thunk) const { |  | 
| 263   ServiceEntry function_code; |  | 
| 264   SIZE_T read; |  | 
| 265   if (!::ReadProcessMemory(process_, target_, &function_code, |  | 
| 266                            sizeof(function_code), &read)) |  | 
| 267     return false; |  | 
| 268 |  | 
| 269   if (sizeof(function_code) != read) |  | 
| 270     return false; |  | 
| 271 |  | 
| 272   if (kMmovR10EcxMovEax != function_code.mov_r10_ecx_mov_eax || |  | 
| 273       kSyscall != function_code.syscall || kRetNp != function_code.ret) |  | 
| 274     return false; |  | 
| 275 |  | 
| 276   // Save the verified code |  | 
| 277   memcpy(local_thunk, &function_code, sizeof(function_code)); |  | 
| 278 |  | 
| 279   return true; |  | 
| 280 } |  | 
| 281 |  | 
| 282 NTSTATUS Service64ResolverThunk::PerformPatch(void* local_thunk, |  | 
| 283                                               void* remote_thunk) { |  | 
| 284   ServiceFullThunk* full_local_thunk = reinterpret_cast<ServiceFullThunk*>( |  | 
| 285                                            local_thunk); |  | 
| 286   ServiceFullThunk* full_remote_thunk = reinterpret_cast<ServiceFullThunk*>( |  | 
| 287                                            remote_thunk); |  | 
| 288 |  | 
| 289   // If the source or target are above 4GB we cannot do this relative jump. |  | 
| 290   if (reinterpret_cast<ULONG_PTR>(full_remote_thunk) > |  | 
| 291       static_cast<ULONG_PTR>(ULONG_MAX)) |  | 
| 292     return STATUS_CONFLICTING_ADDRESSES; |  | 
| 293 |  | 
| 294   if (reinterpret_cast<ULONG_PTR>(target_) > static_cast<ULONG_PTR>(ULONG_MAX)) |  | 
| 295     return STATUS_CONFLICTING_ADDRESSES; |  | 
| 296 |  | 
| 297   // Patch the original code. |  | 
| 298   Redirected local_service; |  | 
| 299   Redirected* remote_service = reinterpret_cast<Redirected*>(target_); |  | 
| 300   ULONG_PTR diff = reinterpret_cast<BYTE*>(&full_remote_thunk->internal_thunk) - |  | 
| 301                    &remote_service->pad; |  | 
| 302   local_service.relative = static_cast<ULONG>(diff); |  | 
| 303 |  | 
| 304   // Setup the PatchInfo structure. |  | 
| 305   SIZE_T actual; |  | 
| 306   if (!::ReadProcessMemory(process_, remote_thunk, local_thunk, |  | 
| 307                            sizeof(PatchInfo), &actual)) |  | 
| 308     return STATUS_UNSUCCESSFUL; |  | 
| 309   if (sizeof(PatchInfo) != actual) |  | 
| 310     return STATUS_UNSUCCESSFUL; |  | 
| 311 |  | 
| 312   full_local_thunk->patch_info.orig_MapViewOfSection = reinterpret_cast< |  | 
| 313       NtMapViewOfSectionFunction>(&full_remote_thunk->original); |  | 
| 314   full_local_thunk->patch_info.patch_location = target_; |  | 
| 315   NTSTATUS ret = ResolveNtdll(&full_local_thunk->patch_info); |  | 
| 316   if (!NT_SUCCESS(ret)) |  | 
| 317     return ret; |  | 
| 318 |  | 
| 319   // Setup the thunk. The jump out is performed from right after the end of the |  | 
| 320   // thunk (full_remote_thunk + 1). |  | 
| 321   InternalThunk my_thunk; |  | 
| 322   ULONG_PTR patch_info = reinterpret_cast<ULONG_PTR>(remote_thunk); |  | 
| 323   my_thunk.patch_info = static_cast<ULONG>(patch_info); |  | 
| 324   diff = reinterpret_cast<const BYTE*>(interceptor_) - |  | 
| 325          reinterpret_cast<BYTE*>(full_remote_thunk + 1); |  | 
| 326   my_thunk.relative = static_cast<ULONG>(diff); |  | 
| 327 |  | 
| 328   memcpy(&full_local_thunk->internal_thunk, &my_thunk, sizeof(my_thunk)); |  | 
| 329 |  | 
| 330   // copy the local thunk buffer to the child |  | 
| 331   if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, |  | 
| 332                             sizeof(ServiceFullThunk), &actual)) |  | 
| 333     return STATUS_UNSUCCESSFUL; |  | 
| 334 |  | 
| 335   if (sizeof(ServiceFullThunk) != actual) |  | 
| 336     return STATUS_UNSUCCESSFUL; |  | 
| 337 |  | 
| 338   // and now change the function to intercept, on the child |  | 
| 339   if (!::WriteProtectedChildMemory(process_, target_, &local_service, |  | 
| 340                                    sizeof(local_service))) |  | 
| 341     return STATUS_UNSUCCESSFUL; |  | 
| 342 |  | 
| 343   return STATUS_SUCCESS; |  | 
| 344 } |  | 
| 345 |  | 
| 346 }  // namespace sandbox |  | 
| OLD | NEW | 
|---|