| OLD | NEW |
| (Empty) |
| 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/src/service_resolver.h" | |
| 6 | |
| 7 #include "base/memory/scoped_ptr.h" | |
| 8 #include "sandbox/src/sandbox_utils.h" | |
| 9 #include "sandbox/src/win_utils.h" | |
| 10 | |
| 11 namespace { | |
| 12 #pragma pack(push, 1) | |
| 13 | |
| 14 const BYTE kMovEax = 0xB8; | |
| 15 const BYTE kMovEdx = 0xBA; | |
| 16 const USHORT kMovEdxEsp = 0xD48B; | |
| 17 const USHORT kCallPtrEdx = 0x12FF; | |
| 18 const USHORT kCallEdx = 0xD2FF; | |
| 19 const BYTE kCallEip = 0xE8; | |
| 20 const BYTE kRet = 0xC2; | |
| 21 const BYTE kRet2 = 0xC3; | |
| 22 const BYTE kNop = 0x90; | |
| 23 const USHORT kJmpEdx = 0xE2FF; | |
| 24 const USHORT kXorEcx = 0xC933; | |
| 25 const ULONG kLeaEdx = 0x0424548D; | |
| 26 const ULONG kCallFs1 = 0xC015FF64; | |
| 27 const USHORT kCallFs2 = 0; | |
| 28 const BYTE kCallFs3 = 0; | |
| 29 const BYTE kAddEsp1 = 0x83; | |
| 30 const USHORT kAddEsp2 = 0x4C4; | |
| 31 const BYTE kJmp32 = 0xE9; | |
| 32 const USHORT kSysenter = 0x340F; | |
| 33 | |
| 34 const int kMaxService = 1000; | |
| 35 | |
| 36 // Service code for 32 bit systems. | |
| 37 // NOTE: on win2003 "call dword ptr [edx]" is "call edx". | |
| 38 struct ServiceEntry { | |
| 39 // This struct contains roughly the following code: | |
| 40 // 00 mov eax,25h | |
| 41 // 05 mov edx,offset SharedUserData!SystemCallStub (7ffe0300) | |
| 42 // 0a call dword ptr [edx] | |
| 43 // 0c ret 2Ch | |
| 44 // 0f nop | |
| 45 BYTE mov_eax; // = B8 | |
| 46 ULONG service_id; | |
| 47 BYTE mov_edx; // = BA | |
| 48 ULONG stub; | |
| 49 USHORT call_ptr_edx; // = FF 12 | |
| 50 BYTE ret; // = C2 | |
| 51 USHORT num_params; | |
| 52 BYTE nop; | |
| 53 }; | |
| 54 | |
| 55 // Service code for 32 bit Windows 8. | |
| 56 struct ServiceEntryW8 { | |
| 57 // This struct contains the following code: | |
| 58 // 00 b825000000 mov eax,25h | |
| 59 // 05 e803000000 call eip+3 | |
| 60 // 0a c22c00 ret 2Ch | |
| 61 // 0d 8bd4 mov edx,esp | |
| 62 // 0f 0f34 sysenter | |
| 63 // 11 c3 ret | |
| 64 // 12 8bff mov edi,edi | |
| 65 BYTE mov_eax; // = B8 | |
| 66 ULONG service_id; | |
| 67 BYTE call_eip; // = E8 | |
| 68 ULONG call_offset; | |
| 69 BYTE ret_p; // = C2 | |
| 70 USHORT num_params; | |
| 71 USHORT mov_edx_esp; // = BD D4 | |
| 72 USHORT sysenter; // = 0F 34 | |
| 73 BYTE ret; // = C3 | |
| 74 USHORT nop; | |
| 75 }; | |
| 76 | |
| 77 // Service code for a 32 bit process running on a 64 bit os. | |
| 78 struct Wow64Entry { | |
| 79 // This struct may contain one of two versions of code: | |
| 80 // 1. For XP, Vista and 2K3: | |
| 81 // 00 b825000000 mov eax, 25h | |
| 82 // 05 33c9 xor ecx, ecx | |
| 83 // 07 8d542404 lea edx, [esp + 4] | |
| 84 // 0b 64ff15c0000000 call dword ptr fs:[0C0h] | |
| 85 // 12 c22c00 ret 2Ch | |
| 86 // | |
| 87 // 2. For Windows 7: | |
| 88 // 00 b825000000 mov eax, 25h | |
| 89 // 05 33c9 xor ecx, ecx | |
| 90 // 07 8d542404 lea edx, [esp + 4] | |
| 91 // 0b 64ff15c0000000 call dword ptr fs:[0C0h] | |
| 92 // 12 83c404 add esp, 4 | |
| 93 // 15 c22c00 ret 2Ch | |
| 94 // | |
| 95 // So we base the structure on the bigger one: | |
| 96 BYTE mov_eax; // = B8 | |
| 97 ULONG service_id; | |
| 98 USHORT xor_ecx; // = 33 C9 | |
| 99 ULONG lea_edx; // = 8D 54 24 04 | |
| 100 ULONG call_fs1; // = 64 FF 15 C0 | |
| 101 USHORT call_fs2; // = 00 00 | |
| 102 BYTE call_fs3; // = 00 | |
| 103 BYTE add_esp1; // = 83 or ret | |
| 104 USHORT add_esp2; // = C4 04 or num_params | |
| 105 BYTE ret; // = C2 | |
| 106 USHORT num_params; | |
| 107 }; | |
| 108 | |
| 109 // Service code for a 32 bit process running on 64 bit Windows 8. | |
| 110 struct Wow64EntryW8 { | |
| 111 // 00 b825000000 mov eax, 25h | |
| 112 // 05 64ff15c0000000 call dword ptr fs:[0C0h] | |
| 113 // 0b c22c00 ret 2Ch | |
| 114 // 0f 90 nop | |
| 115 BYTE mov_eax; // = B8 | |
| 116 ULONG service_id; | |
| 117 ULONG call_fs1; // = 64 FF 15 C0 | |
| 118 USHORT call_fs2; // = 00 00 | |
| 119 BYTE call_fs3; // = 00 | |
| 120 BYTE ret; // = C2 | |
| 121 USHORT num_params; | |
| 122 BYTE nop; | |
| 123 }; | |
| 124 | |
| 125 // Make sure that relaxed patching works as expected. | |
| 126 const size_t kMinServiceSize = offsetof(ServiceEntry, ret); | |
| 127 COMPILE_ASSERT(sizeof(ServiceEntryW8) >= kMinServiceSize, wrong_service_len); | |
| 128 COMPILE_ASSERT(sizeof(Wow64Entry) >= kMinServiceSize, wrong_service_len); | |
| 129 COMPILE_ASSERT(sizeof(Wow64EntryW8) >= kMinServiceSize, wrong_service_len); | |
| 130 | |
| 131 struct ServiceFullThunk { | |
| 132 union { | |
| 133 ServiceEntry original; | |
| 134 ServiceEntryW8 original_w8; | |
| 135 Wow64Entry wow_64; | |
| 136 Wow64EntryW8 wow_64_w8; | |
| 137 }; | |
| 138 int internal_thunk; // Dummy member to the beginning of the internal thunk. | |
| 139 }; | |
| 140 | |
| 141 #pragma pack(pop) | |
| 142 | |
| 143 }; // namespace | |
| 144 | |
| 145 namespace sandbox { | |
| 146 | |
| 147 NTSTATUS ServiceResolverThunk::Setup(const void* target_module, | |
| 148 const void* interceptor_module, | |
| 149 const char* target_name, | |
| 150 const char* interceptor_name, | |
| 151 const void* interceptor_entry_point, | |
| 152 void* thunk_storage, | |
| 153 size_t storage_bytes, | |
| 154 size_t* storage_used) { | |
| 155 NTSTATUS ret = Init(target_module, interceptor_module, target_name, | |
| 156 interceptor_name, interceptor_entry_point, | |
| 157 thunk_storage, storage_bytes); | |
| 158 if (!NT_SUCCESS(ret)) | |
| 159 return ret; | |
| 160 | |
| 161 relative_jump_ = 0; | |
| 162 size_t thunk_bytes = GetThunkSize(); | |
| 163 scoped_array<char> thunk_buffer(new char[thunk_bytes]); | |
| 164 ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>( | |
| 165 thunk_buffer.get()); | |
| 166 | |
| 167 if (!IsFunctionAService(&thunk->original) && | |
| 168 (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) | |
| 169 return STATUS_UNSUCCESSFUL; | |
| 170 | |
| 171 ret = PerformPatch(thunk, thunk_storage); | |
| 172 | |
| 173 if (NULL != storage_used) | |
| 174 *storage_used = thunk_bytes; | |
| 175 | |
| 176 return ret; | |
| 177 } | |
| 178 | |
| 179 size_t ServiceResolverThunk::GetThunkSize() const { | |
| 180 return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize(); | |
| 181 } | |
| 182 | |
| 183 bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { | |
| 184 ServiceEntry function_code; | |
| 185 SIZE_T read; | |
| 186 if (!::ReadProcessMemory(process_, target_, &function_code, | |
| 187 sizeof(function_code), &read)) | |
| 188 return false; | |
| 189 | |
| 190 if (sizeof(function_code) != read) | |
| 191 return false; | |
| 192 | |
| 193 if (kMovEax != function_code.mov_eax || | |
| 194 kMovEdx != function_code.mov_edx || | |
| 195 (kCallPtrEdx != function_code.call_ptr_edx && | |
| 196 kCallEdx != function_code.call_ptr_edx) || | |
| 197 kRet != function_code.ret) | |
| 198 return false; | |
| 199 | |
| 200 // Find the system call pointer if we don't already have it. | |
| 201 if (kCallEdx != function_code.call_ptr_edx) { | |
| 202 DWORD ki_system_call; | |
| 203 if (!::ReadProcessMemory(process_, | |
| 204 bit_cast<const void*>(function_code.stub), | |
| 205 &ki_system_call, sizeof(ki_system_call), &read)) | |
| 206 return false; | |
| 207 | |
| 208 if (sizeof(ki_system_call) != read) | |
| 209 return false; | |
| 210 | |
| 211 HMODULE module_1, module_2; | |
| 212 // last check, call_stub should point to a KiXXSystemCall function on ntdll | |
| 213 if (!GetModuleHandleHelper(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | | |
| 214 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, | |
| 215 bit_cast<const wchar_t*>(ki_system_call), | |
| 216 &module_1)) | |
| 217 return false; | |
| 218 | |
| 219 if (NULL != ntdll_base_) { | |
| 220 // This path is only taken when running the unit tests. We want to be | |
| 221 // able to patch a buffer in memory, so target_ is not inside ntdll. | |
| 222 module_2 = ntdll_base_; | |
| 223 } else { | |
| 224 if (!GetModuleHandleHelper( | |
| 225 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | | |
| 226 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, | |
| 227 reinterpret_cast<const wchar_t*>(target_), | |
| 228 &module_2)) { | |
| 229 return false; | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 if (module_1 != module_2) | |
| 234 return false; | |
| 235 } | |
| 236 | |
| 237 // Save the verified code | |
| 238 memcpy(local_thunk, &function_code, sizeof(function_code)); | |
| 239 | |
| 240 return true; | |
| 241 } | |
| 242 | |
| 243 NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, | |
| 244 void* remote_thunk) { | |
| 245 ServiceEntry intercepted_code; | |
| 246 size_t bytes_to_write = sizeof(intercepted_code); | |
| 247 ServiceFullThunk *full_local_thunk = reinterpret_cast<ServiceFullThunk*>( | |
| 248 local_thunk); | |
| 249 ServiceFullThunk *full_remote_thunk = reinterpret_cast<ServiceFullThunk*>( | |
| 250 remote_thunk); | |
| 251 | |
| 252 // patch the original code | |
| 253 memcpy(&intercepted_code, &full_local_thunk->original, | |
| 254 sizeof(intercepted_code)); | |
| 255 intercepted_code.mov_eax = kMovEax; | |
| 256 intercepted_code.service_id = full_local_thunk->original.service_id; | |
| 257 intercepted_code.mov_edx = kMovEdx; | |
| 258 intercepted_code.stub = bit_cast<ULONG>(&full_remote_thunk->internal_thunk); | |
| 259 intercepted_code.call_ptr_edx = kJmpEdx; | |
| 260 bytes_to_write = kMinServiceSize; | |
| 261 | |
| 262 if (relative_jump_) { | |
| 263 intercepted_code.mov_eax = kJmp32; | |
| 264 intercepted_code.service_id = relative_jump_; | |
| 265 bytes_to_write = offsetof(ServiceEntry, mov_edx); | |
| 266 } | |
| 267 | |
| 268 // setup the thunk | |
| 269 SetInternalThunk(&full_local_thunk->internal_thunk, GetInternalThunkSize(), | |
| 270 remote_thunk, interceptor_); | |
| 271 | |
| 272 size_t thunk_size = GetThunkSize(); | |
| 273 | |
| 274 // copy the local thunk buffer to the child | |
| 275 SIZE_T written; | |
| 276 if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, | |
| 277 thunk_size, &written)) | |
| 278 return STATUS_UNSUCCESSFUL; | |
| 279 | |
| 280 if (thunk_size != written) | |
| 281 return STATUS_UNSUCCESSFUL; | |
| 282 | |
| 283 // and now change the function to intercept, on the child | |
| 284 if (NULL != ntdll_base_) { | |
| 285 // running a unit test | |
| 286 if (!::WriteProcessMemory(process_, target_, &intercepted_code, | |
| 287 bytes_to_write, &written)) | |
| 288 return STATUS_UNSUCCESSFUL; | |
| 289 } else { | |
| 290 if (!WriteProtectedChildMemory(process_, target_, &intercepted_code, | |
| 291 bytes_to_write)) | |
| 292 return STATUS_UNSUCCESSFUL; | |
| 293 } | |
| 294 | |
| 295 return STATUS_SUCCESS; | |
| 296 } | |
| 297 | |
| 298 bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk, | |
| 299 void* remote_thunk) { | |
| 300 ServiceEntry function_code; | |
| 301 SIZE_T read; | |
| 302 if (!::ReadProcessMemory(process_, target_, &function_code, | |
| 303 sizeof(function_code), &read)) | |
| 304 return false; | |
| 305 | |
| 306 if (sizeof(function_code) != read) | |
| 307 return false; | |
| 308 | |
| 309 if (kJmp32 == function_code.mov_eax) { | |
| 310 // Plain old entry point patch. The relative jump address follows it. | |
| 311 ULONG relative = function_code.service_id; | |
| 312 | |
| 313 // First, fix our copy of their patch. | |
| 314 relative += bit_cast<ULONG>(target_) - bit_cast<ULONG>(remote_thunk); | |
| 315 | |
| 316 function_code.service_id = relative; | |
| 317 | |
| 318 // And now, remember how to re-patch it. | |
| 319 ServiceFullThunk *full_thunk = | |
| 320 reinterpret_cast<ServiceFullThunk*>(remote_thunk); | |
| 321 | |
| 322 const ULONG kJmp32Size = 5; | |
| 323 | |
| 324 relative_jump_ = bit_cast<ULONG>(&full_thunk->internal_thunk) - | |
| 325 bit_cast<ULONG>(target_) - kJmp32Size; | |
| 326 } | |
| 327 | |
| 328 // Save the verified code | |
| 329 memcpy(local_thunk, &function_code, sizeof(function_code)); | |
| 330 | |
| 331 return true; | |
| 332 } | |
| 333 | |
| 334 bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const { | |
| 335 Wow64Entry function_code; | |
| 336 SIZE_T read; | |
| 337 if (!::ReadProcessMemory(process_, target_, &function_code, | |
| 338 sizeof(function_code), &read)) | |
| 339 return false; | |
| 340 | |
| 341 if (sizeof(function_code) != read) | |
| 342 return false; | |
| 343 | |
| 344 if (kMovEax != function_code.mov_eax || kXorEcx != function_code.xor_ecx || | |
| 345 kLeaEdx != function_code.lea_edx || kCallFs1 != function_code.call_fs1 || | |
| 346 kCallFs2 != function_code.call_fs2 || kCallFs3 != function_code.call_fs3) | |
| 347 return false; | |
| 348 | |
| 349 if ((kAddEsp1 == function_code.add_esp1 && | |
| 350 kAddEsp2 == function_code.add_esp2 && | |
| 351 kRet == function_code.ret) || kRet == function_code.add_esp1) { | |
| 352 // Save the verified code | |
| 353 memcpy(local_thunk, &function_code, sizeof(function_code)); | |
| 354 return true; | |
| 355 } | |
| 356 | |
| 357 return false; | |
| 358 } | |
| 359 | |
| 360 bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const { | |
| 361 Wow64EntryW8 function_code; | |
| 362 SIZE_T read; | |
| 363 if (!::ReadProcessMemory(process_, target_, &function_code, | |
| 364 sizeof(function_code), &read)) | |
| 365 return false; | |
| 366 | |
| 367 if (sizeof(function_code) != read) | |
| 368 return false; | |
| 369 | |
| 370 if (kMovEax != function_code.mov_eax || kCallFs1 != function_code.call_fs1 || | |
| 371 kCallFs2 != function_code.call_fs2 || | |
| 372 kCallFs3 != function_code.call_fs3 || kRet != function_code.ret) { | |
| 373 return false; | |
| 374 } | |
| 375 | |
| 376 // Save the verified code | |
| 377 memcpy(local_thunk, &function_code, sizeof(function_code)); | |
| 378 return true; | |
| 379 } | |
| 380 | |
| 381 bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const { | |
| 382 ServiceEntry function_code; | |
| 383 SIZE_T read; | |
| 384 if (!::ReadProcessMemory(process_, target_, &function_code, | |
| 385 sizeof(function_code), &read)) | |
| 386 return false; | |
| 387 | |
| 388 if (sizeof(function_code) != read) | |
| 389 return false; | |
| 390 | |
| 391 if (kMovEax != function_code.mov_eax || | |
| 392 function_code.service_id > kMaxService) | |
| 393 return false; | |
| 394 | |
| 395 // Save the verified code | |
| 396 memcpy(local_thunk, &function_code, sizeof(function_code)); | |
| 397 | |
| 398 return true; | |
| 399 } | |
| 400 | |
| 401 bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const { | |
| 402 ServiceEntryW8 function_code; | |
| 403 SIZE_T read; | |
| 404 if (!::ReadProcessMemory(process_, target_, &function_code, | |
| 405 sizeof(function_code), &read)) | |
| 406 return false; | |
| 407 | |
| 408 if (sizeof(function_code) != read) | |
| 409 return false; | |
| 410 | |
| 411 if (kMovEax != function_code.mov_eax || kCallEip != function_code.call_eip || | |
| 412 function_code.call_offset != 3 || kRet != function_code.ret_p || | |
| 413 kMovEdxEsp != function_code.mov_edx_esp || | |
| 414 kSysenter != function_code.sysenter || kRet2 != function_code.ret) { | |
| 415 return false; | |
| 416 } | |
| 417 | |
| 418 // Save the verified code | |
| 419 memcpy(local_thunk, &function_code, sizeof(function_code)); | |
| 420 | |
| 421 return true; | |
| 422 } | |
| 423 | |
| 424 } // namespace sandbox | |
| OLD | NEW |