security/sandbox/win/src/service_resolver_32.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial