1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/sandbox/win/src/service_resolver_32.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,420 @@ 1.4 +// Copyright (c) 2012 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include "sandbox/win/src/service_resolver.h" 1.9 + 1.10 +#include "base/memory/scoped_ptr.h" 1.11 +#include "sandbox/win/src/win_utils.h" 1.12 + 1.13 +namespace { 1.14 +#pragma pack(push, 1) 1.15 + 1.16 +const BYTE kMovEax = 0xB8; 1.17 +const BYTE kMovEdx = 0xBA; 1.18 +const USHORT kMovEdxEsp = 0xD48B; 1.19 +const USHORT kCallPtrEdx = 0x12FF; 1.20 +const USHORT kCallEdx = 0xD2FF; 1.21 +const BYTE kCallEip = 0xE8; 1.22 +const BYTE kRet = 0xC2; 1.23 +const BYTE kRet2 = 0xC3; 1.24 +const BYTE kNop = 0x90; 1.25 +const USHORT kJmpEdx = 0xE2FF; 1.26 +const USHORT kXorEcx = 0xC933; 1.27 +const ULONG kLeaEdx = 0x0424548D; 1.28 +const ULONG kCallFs1 = 0xC015FF64; 1.29 +const USHORT kCallFs2 = 0; 1.30 +const BYTE kCallFs3 = 0; 1.31 +const BYTE kAddEsp1 = 0x83; 1.32 +const USHORT kAddEsp2 = 0x4C4; 1.33 +const BYTE kJmp32 = 0xE9; 1.34 +const USHORT kSysenter = 0x340F; 1.35 + 1.36 +const int kMaxService = 1000; 1.37 + 1.38 +// Service code for 32 bit systems. 1.39 +// NOTE: on win2003 "call dword ptr [edx]" is "call edx". 1.40 +struct ServiceEntry { 1.41 + // This struct contains roughly the following code: 1.42 + // 00 mov eax,25h 1.43 + // 05 mov edx,offset SharedUserData!SystemCallStub (7ffe0300) 1.44 + // 0a call dword ptr [edx] 1.45 + // 0c ret 2Ch 1.46 + // 0f nop 1.47 + BYTE mov_eax; // = B8 1.48 + ULONG service_id; 1.49 + BYTE mov_edx; // = BA 1.50 + ULONG stub; 1.51 + USHORT call_ptr_edx; // = FF 12 1.52 + BYTE ret; // = C2 1.53 + USHORT num_params; 1.54 + BYTE nop; 1.55 +}; 1.56 + 1.57 +// Service code for 32 bit Windows 8. 1.58 +struct ServiceEntryW8 { 1.59 + // This struct contains the following code: 1.60 + // 00 b825000000 mov eax,25h 1.61 + // 05 e803000000 call eip+3 1.62 + // 0a c22c00 ret 2Ch 1.63 + // 0d 8bd4 mov edx,esp 1.64 + // 0f 0f34 sysenter 1.65 + // 11 c3 ret 1.66 + // 12 8bff mov edi,edi 1.67 + BYTE mov_eax; // = B8 1.68 + ULONG service_id; 1.69 + BYTE call_eip; // = E8 1.70 + ULONG call_offset; 1.71 + BYTE ret_p; // = C2 1.72 + USHORT num_params; 1.73 + USHORT mov_edx_esp; // = BD D4 1.74 + USHORT sysenter; // = 0F 34 1.75 + BYTE ret; // = C3 1.76 + USHORT nop; 1.77 +}; 1.78 + 1.79 +// Service code for a 32 bit process running on a 64 bit os. 1.80 +struct Wow64Entry { 1.81 + // This struct may contain one of two versions of code: 1.82 + // 1. For XP, Vista and 2K3: 1.83 + // 00 b825000000 mov eax, 25h 1.84 + // 05 33c9 xor ecx, ecx 1.85 + // 07 8d542404 lea edx, [esp + 4] 1.86 + // 0b 64ff15c0000000 call dword ptr fs:[0C0h] 1.87 + // 12 c22c00 ret 2Ch 1.88 + // 1.89 + // 2. For Windows 7: 1.90 + // 00 b825000000 mov eax, 25h 1.91 + // 05 33c9 xor ecx, ecx 1.92 + // 07 8d542404 lea edx, [esp + 4] 1.93 + // 0b 64ff15c0000000 call dword ptr fs:[0C0h] 1.94 + // 12 83c404 add esp, 4 1.95 + // 15 c22c00 ret 2Ch 1.96 + // 1.97 + // So we base the structure on the bigger one: 1.98 + BYTE mov_eax; // = B8 1.99 + ULONG service_id; 1.100 + USHORT xor_ecx; // = 33 C9 1.101 + ULONG lea_edx; // = 8D 54 24 04 1.102 + ULONG call_fs1; // = 64 FF 15 C0 1.103 + USHORT call_fs2; // = 00 00 1.104 + BYTE call_fs3; // = 00 1.105 + BYTE add_esp1; // = 83 or ret 1.106 + USHORT add_esp2; // = C4 04 or num_params 1.107 + BYTE ret; // = C2 1.108 + USHORT num_params; 1.109 +}; 1.110 + 1.111 +// Service code for a 32 bit process running on 64 bit Windows 8. 1.112 +struct Wow64EntryW8 { 1.113 + // 00 b825000000 mov eax, 25h 1.114 + // 05 64ff15c0000000 call dword ptr fs:[0C0h] 1.115 + // 0b c22c00 ret 2Ch 1.116 + // 0f 90 nop 1.117 + BYTE mov_eax; // = B8 1.118 + ULONG service_id; 1.119 + ULONG call_fs1; // = 64 FF 15 C0 1.120 + USHORT call_fs2; // = 00 00 1.121 + BYTE call_fs3; // = 00 1.122 + BYTE ret; // = C2 1.123 + USHORT num_params; 1.124 + BYTE nop; 1.125 +}; 1.126 + 1.127 +// Make sure that relaxed patching works as expected. 1.128 +const size_t kMinServiceSize = offsetof(ServiceEntry, ret); 1.129 +COMPILE_ASSERT(sizeof(ServiceEntryW8) >= kMinServiceSize, wrong_service_len); 1.130 +COMPILE_ASSERT(sizeof(Wow64Entry) >= kMinServiceSize, wrong_service_len); 1.131 +COMPILE_ASSERT(sizeof(Wow64EntryW8) >= kMinServiceSize, wrong_service_len); 1.132 + 1.133 +struct ServiceFullThunk { 1.134 + union { 1.135 + ServiceEntry original; 1.136 + ServiceEntryW8 original_w8; 1.137 + Wow64Entry wow_64; 1.138 + Wow64EntryW8 wow_64_w8; 1.139 + }; 1.140 + int internal_thunk; // Dummy member to the beginning of the internal thunk. 1.141 +}; 1.142 + 1.143 +#pragma pack(pop) 1.144 + 1.145 +}; // namespace 1.146 + 1.147 +namespace sandbox { 1.148 + 1.149 +NTSTATUS ServiceResolverThunk::Setup(const void* target_module, 1.150 + const void* interceptor_module, 1.151 + const char* target_name, 1.152 + const char* interceptor_name, 1.153 + const void* interceptor_entry_point, 1.154 + void* thunk_storage, 1.155 + size_t storage_bytes, 1.156 + size_t* storage_used) { 1.157 + NTSTATUS ret = Init(target_module, interceptor_module, target_name, 1.158 + interceptor_name, interceptor_entry_point, 1.159 + thunk_storage, storage_bytes); 1.160 + if (!NT_SUCCESS(ret)) 1.161 + return ret; 1.162 + 1.163 + relative_jump_ = 0; 1.164 + size_t thunk_bytes = GetThunkSize(); 1.165 + scoped_ptr<char[]> thunk_buffer(new char[thunk_bytes]); 1.166 + ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>( 1.167 + thunk_buffer.get()); 1.168 + 1.169 + if (!IsFunctionAService(&thunk->original) && 1.170 + (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) 1.171 + return STATUS_UNSUCCESSFUL; 1.172 + 1.173 + ret = PerformPatch(thunk, thunk_storage); 1.174 + 1.175 + if (NULL != storage_used) 1.176 + *storage_used = thunk_bytes; 1.177 + 1.178 + return ret; 1.179 +} 1.180 + 1.181 +size_t ServiceResolverThunk::GetThunkSize() const { 1.182 + return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize(); 1.183 +} 1.184 + 1.185 +bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { 1.186 + ServiceEntry function_code; 1.187 + SIZE_T read; 1.188 + if (!::ReadProcessMemory(process_, target_, &function_code, 1.189 + sizeof(function_code), &read)) 1.190 + return false; 1.191 + 1.192 + if (sizeof(function_code) != read) 1.193 + return false; 1.194 + 1.195 + if (kMovEax != function_code.mov_eax || 1.196 + kMovEdx != function_code.mov_edx || 1.197 + (kCallPtrEdx != function_code.call_ptr_edx && 1.198 + kCallEdx != function_code.call_ptr_edx) || 1.199 + kRet != function_code.ret) 1.200 + return false; 1.201 + 1.202 + // Find the system call pointer if we don't already have it. 1.203 + if (kCallEdx != function_code.call_ptr_edx) { 1.204 + DWORD ki_system_call; 1.205 + if (!::ReadProcessMemory(process_, 1.206 + bit_cast<const void*>(function_code.stub), 1.207 + &ki_system_call, sizeof(ki_system_call), &read)) 1.208 + return false; 1.209 + 1.210 + if (sizeof(ki_system_call) != read) 1.211 + return false; 1.212 + 1.213 + HMODULE module_1, module_2; 1.214 + // last check, call_stub should point to a KiXXSystemCall function on ntdll 1.215 + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | 1.216 + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 1.217 + bit_cast<const wchar_t*>(ki_system_call), &module_1)) 1.218 + return false; 1.219 + 1.220 + if (NULL != ntdll_base_) { 1.221 + // This path is only taken when running the unit tests. We want to be 1.222 + // able to patch a buffer in memory, so target_ is not inside ntdll. 1.223 + module_2 = ntdll_base_; 1.224 + } else { 1.225 + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | 1.226 + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 1.227 + reinterpret_cast<const wchar_t*>(target_), 1.228 + &module_2)) 1.229 + return false; 1.230 + } 1.231 + 1.232 + if (module_1 != module_2) 1.233 + return false; 1.234 + } 1.235 + 1.236 + // Save the verified code 1.237 + memcpy(local_thunk, &function_code, sizeof(function_code)); 1.238 + 1.239 + return true; 1.240 +} 1.241 + 1.242 +NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, 1.243 + void* remote_thunk) { 1.244 + ServiceEntry intercepted_code; 1.245 + size_t bytes_to_write = sizeof(intercepted_code); 1.246 + ServiceFullThunk *full_local_thunk = reinterpret_cast<ServiceFullThunk*>( 1.247 + local_thunk); 1.248 + ServiceFullThunk *full_remote_thunk = reinterpret_cast<ServiceFullThunk*>( 1.249 + remote_thunk); 1.250 + 1.251 + // patch the original code 1.252 + memcpy(&intercepted_code, &full_local_thunk->original, 1.253 + sizeof(intercepted_code)); 1.254 + intercepted_code.mov_eax = kMovEax; 1.255 + intercepted_code.service_id = full_local_thunk->original.service_id; 1.256 + intercepted_code.mov_edx = kMovEdx; 1.257 + intercepted_code.stub = bit_cast<ULONG>(&full_remote_thunk->internal_thunk); 1.258 + intercepted_code.call_ptr_edx = kJmpEdx; 1.259 + bytes_to_write = kMinServiceSize; 1.260 + 1.261 + if (relative_jump_) { 1.262 + intercepted_code.mov_eax = kJmp32; 1.263 + intercepted_code.service_id = relative_jump_; 1.264 + bytes_to_write = offsetof(ServiceEntry, mov_edx); 1.265 + } 1.266 + 1.267 + // setup the thunk 1.268 + SetInternalThunk(&full_local_thunk->internal_thunk, GetInternalThunkSize(), 1.269 + remote_thunk, interceptor_); 1.270 + 1.271 + size_t thunk_size = GetThunkSize(); 1.272 + 1.273 + // copy the local thunk buffer to the child 1.274 + SIZE_T written; 1.275 + if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, 1.276 + thunk_size, &written)) 1.277 + return STATUS_UNSUCCESSFUL; 1.278 + 1.279 + if (thunk_size != written) 1.280 + return STATUS_UNSUCCESSFUL; 1.281 + 1.282 + // and now change the function to intercept, on the child 1.283 + if (NULL != ntdll_base_) { 1.284 + // running a unit test 1.285 + if (!::WriteProcessMemory(process_, target_, &intercepted_code, 1.286 + bytes_to_write, &written)) 1.287 + return STATUS_UNSUCCESSFUL; 1.288 + } else { 1.289 + if (!WriteProtectedChildMemory(process_, target_, &intercepted_code, 1.290 + bytes_to_write)) 1.291 + return STATUS_UNSUCCESSFUL; 1.292 + } 1.293 + 1.294 + return STATUS_SUCCESS; 1.295 +} 1.296 + 1.297 +bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk, 1.298 + void* remote_thunk) { 1.299 + ServiceEntry function_code; 1.300 + SIZE_T read; 1.301 + if (!::ReadProcessMemory(process_, target_, &function_code, 1.302 + sizeof(function_code), &read)) 1.303 + return false; 1.304 + 1.305 + if (sizeof(function_code) != read) 1.306 + return false; 1.307 + 1.308 + if (kJmp32 == function_code.mov_eax) { 1.309 + // Plain old entry point patch. The relative jump address follows it. 1.310 + ULONG relative = function_code.service_id; 1.311 + 1.312 + // First, fix our copy of their patch. 1.313 + relative += bit_cast<ULONG>(target_) - bit_cast<ULONG>(remote_thunk); 1.314 + 1.315 + function_code.service_id = relative; 1.316 + 1.317 + // And now, remember how to re-patch it. 1.318 + ServiceFullThunk *full_thunk = 1.319 + reinterpret_cast<ServiceFullThunk*>(remote_thunk); 1.320 + 1.321 + const ULONG kJmp32Size = 5; 1.322 + 1.323 + relative_jump_ = bit_cast<ULONG>(&full_thunk->internal_thunk) - 1.324 + bit_cast<ULONG>(target_) - kJmp32Size; 1.325 + } 1.326 + 1.327 + // Save the verified code 1.328 + memcpy(local_thunk, &function_code, sizeof(function_code)); 1.329 + 1.330 + return true; 1.331 +} 1.332 + 1.333 +bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const { 1.334 + Wow64Entry function_code; 1.335 + SIZE_T read; 1.336 + if (!::ReadProcessMemory(process_, target_, &function_code, 1.337 + sizeof(function_code), &read)) 1.338 + return false; 1.339 + 1.340 + if (sizeof(function_code) != read) 1.341 + return false; 1.342 + 1.343 + if (kMovEax != function_code.mov_eax || kXorEcx != function_code.xor_ecx || 1.344 + kLeaEdx != function_code.lea_edx || kCallFs1 != function_code.call_fs1 || 1.345 + kCallFs2 != function_code.call_fs2 || kCallFs3 != function_code.call_fs3) 1.346 + return false; 1.347 + 1.348 + if ((kAddEsp1 == function_code.add_esp1 && 1.349 + kAddEsp2 == function_code.add_esp2 && 1.350 + kRet == function_code.ret) || kRet == function_code.add_esp1) { 1.351 + // Save the verified code 1.352 + memcpy(local_thunk, &function_code, sizeof(function_code)); 1.353 + return true; 1.354 + } 1.355 + 1.356 + return false; 1.357 +} 1.358 + 1.359 +bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const { 1.360 + Wow64EntryW8 function_code; 1.361 + SIZE_T read; 1.362 + if (!::ReadProcessMemory(process_, target_, &function_code, 1.363 + sizeof(function_code), &read)) 1.364 + return false; 1.365 + 1.366 + if (sizeof(function_code) != read) 1.367 + return false; 1.368 + 1.369 + if (kMovEax != function_code.mov_eax || kCallFs1 != function_code.call_fs1 || 1.370 + kCallFs2 != function_code.call_fs2 || 1.371 + kCallFs3 != function_code.call_fs3 || kRet != function_code.ret) { 1.372 + return false; 1.373 + } 1.374 + 1.375 + // Save the verified code 1.376 + memcpy(local_thunk, &function_code, sizeof(function_code)); 1.377 + return true; 1.378 +} 1.379 + 1.380 +bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const { 1.381 + ServiceEntry function_code; 1.382 + SIZE_T read; 1.383 + if (!::ReadProcessMemory(process_, target_, &function_code, 1.384 + sizeof(function_code), &read)) 1.385 + return false; 1.386 + 1.387 + if (sizeof(function_code) != read) 1.388 + return false; 1.389 + 1.390 + if (kMovEax != function_code.mov_eax || 1.391 + function_code.service_id > kMaxService) 1.392 + return false; 1.393 + 1.394 + // Save the verified code 1.395 + memcpy(local_thunk, &function_code, sizeof(function_code)); 1.396 + 1.397 + return true; 1.398 +} 1.399 + 1.400 +bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const { 1.401 + ServiceEntryW8 function_code; 1.402 + SIZE_T read; 1.403 + if (!::ReadProcessMemory(process_, target_, &function_code, 1.404 + sizeof(function_code), &read)) 1.405 + return false; 1.406 + 1.407 + if (sizeof(function_code) != read) 1.408 + return false; 1.409 + 1.410 + if (kMovEax != function_code.mov_eax || kCallEip != function_code.call_eip || 1.411 + function_code.call_offset != 3 || kRet != function_code.ret_p || 1.412 + kMovEdxEsp != function_code.mov_edx_esp || 1.413 + kSysenter != function_code.sysenter || kRet2 != function_code.ret) { 1.414 + return false; 1.415 + } 1.416 + 1.417 + // Save the verified code 1.418 + memcpy(local_thunk, &function_code, sizeof(function_code)); 1.419 + 1.420 + return true; 1.421 +} 1.422 + 1.423 +} // namespace sandbox