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