Wed, 31 Dec 2014 06:09:35 +0100
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/logging.h" |
michael@0 | 8 | #include "base/memory/scoped_ptr.h" |
michael@0 | 9 | #include "sandbox/win/src/win_utils.h" |
michael@0 | 10 | |
michael@0 | 11 | namespace { |
michael@0 | 12 | #pragma pack(push, 1) |
michael@0 | 13 | |
michael@0 | 14 | const ULONG kMmovR10EcxMovEax = 0xB8D18B4C; |
michael@0 | 15 | const USHORT kSyscall = 0x050F; |
michael@0 | 16 | const BYTE kRetNp = 0xC3; |
michael@0 | 17 | const ULONG64 kMov1 = 0x54894808244C8948; |
michael@0 | 18 | const ULONG64 kMov2 = 0x4C182444894C1024; |
michael@0 | 19 | const ULONG kMov3 = 0x20244C89; |
michael@0 | 20 | |
michael@0 | 21 | // Service code for 64 bit systems. |
michael@0 | 22 | struct ServiceEntry { |
michael@0 | 23 | // This struct contains roughly the following code: |
michael@0 | 24 | // 00 mov r10,rcx |
michael@0 | 25 | // 03 mov eax,52h |
michael@0 | 26 | // 08 syscall |
michael@0 | 27 | // 0a ret |
michael@0 | 28 | // 0b xchg ax,ax |
michael@0 | 29 | // 0e xchg ax,ax |
michael@0 | 30 | |
michael@0 | 31 | ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8 |
michael@0 | 32 | ULONG service_id; |
michael@0 | 33 | USHORT syscall; // = 0F 05 |
michael@0 | 34 | BYTE ret; // = C3 |
michael@0 | 35 | BYTE pad; // = 66 |
michael@0 | 36 | USHORT xchg_ax_ax1; // = 66 90 |
michael@0 | 37 | USHORT xchg_ax_ax2; // = 66 90 |
michael@0 | 38 | }; |
michael@0 | 39 | |
michael@0 | 40 | // Service code for 64 bit Windows 8. |
michael@0 | 41 | struct ServiceEntryW8 { |
michael@0 | 42 | // This struct contains the following code: |
michael@0 | 43 | // 00 48894c2408 mov [rsp+8], rcx |
michael@0 | 44 | // 05 4889542410 mov [rsp+10], rdx |
michael@0 | 45 | // 0a 4c89442418 mov [rsp+18], r8 |
michael@0 | 46 | // 0f 4c894c2420 mov [rsp+20], r9 |
michael@0 | 47 | // 14 4c8bd1 mov r10,rcx |
michael@0 | 48 | // 17 b825000000 mov eax,25h |
michael@0 | 49 | // 1c 0f05 syscall |
michael@0 | 50 | // 1e c3 ret |
michael@0 | 51 | // 1f 90 nop |
michael@0 | 52 | |
michael@0 | 53 | ULONG64 mov_1; // = 48 89 4C 24 08 48 89 54 |
michael@0 | 54 | ULONG64 mov_2; // = 24 10 4C 89 44 24 18 4C |
michael@0 | 55 | ULONG mov_3; // = 89 4C 24 20 |
michael@0 | 56 | ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8 |
michael@0 | 57 | ULONG service_id; |
michael@0 | 58 | USHORT syscall; // = 0F 05 |
michael@0 | 59 | BYTE ret; // = C2 |
michael@0 | 60 | BYTE nop; // = 90 |
michael@0 | 61 | }; |
michael@0 | 62 | |
michael@0 | 63 | // We don't have an internal thunk for x64. |
michael@0 | 64 | struct ServiceFullThunk { |
michael@0 | 65 | union { |
michael@0 | 66 | ServiceEntry original; |
michael@0 | 67 | ServiceEntryW8 original_w8; |
michael@0 | 68 | }; |
michael@0 | 69 | }; |
michael@0 | 70 | |
michael@0 | 71 | #pragma pack(pop) |
michael@0 | 72 | |
michael@0 | 73 | bool IsService(const void* source) { |
michael@0 | 74 | const ServiceEntry* service = |
michael@0 | 75 | reinterpret_cast<const ServiceEntry*>(source); |
michael@0 | 76 | |
michael@0 | 77 | return (kMmovR10EcxMovEax == service->mov_r10_rcx_mov_eax && |
michael@0 | 78 | kSyscall == service->syscall && kRetNp == service->ret); |
michael@0 | 79 | } |
michael@0 | 80 | |
michael@0 | 81 | }; // namespace |
michael@0 | 82 | |
michael@0 | 83 | namespace sandbox { |
michael@0 | 84 | |
michael@0 | 85 | NTSTATUS ServiceResolverThunk::Setup(const void* target_module, |
michael@0 | 86 | const void* interceptor_module, |
michael@0 | 87 | const char* target_name, |
michael@0 | 88 | const char* interceptor_name, |
michael@0 | 89 | const void* interceptor_entry_point, |
michael@0 | 90 | void* thunk_storage, |
michael@0 | 91 | size_t storage_bytes, |
michael@0 | 92 | size_t* storage_used) { |
michael@0 | 93 | NTSTATUS ret = Init(target_module, interceptor_module, target_name, |
michael@0 | 94 | interceptor_name, interceptor_entry_point, |
michael@0 | 95 | thunk_storage, storage_bytes); |
michael@0 | 96 | if (!NT_SUCCESS(ret)) |
michael@0 | 97 | return ret; |
michael@0 | 98 | |
michael@0 | 99 | size_t thunk_bytes = GetThunkSize(); |
michael@0 | 100 | scoped_ptr<char[]> thunk_buffer(new char[thunk_bytes]); |
michael@0 | 101 | ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>( |
michael@0 | 102 | thunk_buffer.get()); |
michael@0 | 103 | |
michael@0 | 104 | if (!IsFunctionAService(&thunk->original)) |
michael@0 | 105 | return STATUS_UNSUCCESSFUL; |
michael@0 | 106 | |
michael@0 | 107 | ret = PerformPatch(thunk, thunk_storage); |
michael@0 | 108 | |
michael@0 | 109 | if (NULL != storage_used) |
michael@0 | 110 | *storage_used = thunk_bytes; |
michael@0 | 111 | |
michael@0 | 112 | return ret; |
michael@0 | 113 | } |
michael@0 | 114 | |
michael@0 | 115 | size_t ServiceResolverThunk::GetThunkSize() const { |
michael@0 | 116 | return sizeof(ServiceFullThunk); |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { |
michael@0 | 120 | ServiceFullThunk function_code; |
michael@0 | 121 | SIZE_T read; |
michael@0 | 122 | if (!::ReadProcessMemory(process_, target_, &function_code, |
michael@0 | 123 | sizeof(function_code), &read)) |
michael@0 | 124 | return false; |
michael@0 | 125 | |
michael@0 | 126 | if (sizeof(function_code) != read) |
michael@0 | 127 | return false; |
michael@0 | 128 | |
michael@0 | 129 | if (!IsService(&function_code)) { |
michael@0 | 130 | // See if it's the Win8 signature. |
michael@0 | 131 | ServiceEntryW8* w8_service = &function_code.original_w8; |
michael@0 | 132 | if (!IsService(&w8_service->mov_r10_rcx_mov_eax) || |
michael@0 | 133 | w8_service->mov_1 != kMov1 || w8_service->mov_1 != kMov1 || |
michael@0 | 134 | w8_service->mov_1 != kMov1) { |
michael@0 | 135 | return false; |
michael@0 | 136 | } |
michael@0 | 137 | } |
michael@0 | 138 | |
michael@0 | 139 | // Save the verified code. |
michael@0 | 140 | memcpy(local_thunk, &function_code, sizeof(function_code)); |
michael@0 | 141 | |
michael@0 | 142 | return true; |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, |
michael@0 | 146 | void* remote_thunk) { |
michael@0 | 147 | ServiceFullThunk* full_local_thunk = reinterpret_cast<ServiceFullThunk*>( |
michael@0 | 148 | local_thunk); |
michael@0 | 149 | ServiceFullThunk* full_remote_thunk = reinterpret_cast<ServiceFullThunk*>( |
michael@0 | 150 | remote_thunk); |
michael@0 | 151 | |
michael@0 | 152 | // Patch the original code. |
michael@0 | 153 | ServiceEntry local_service; |
michael@0 | 154 | DCHECK_GE(GetInternalThunkSize(), sizeof(local_service)); |
michael@0 | 155 | if (!SetInternalThunk(&local_service, sizeof(local_service), NULL, |
michael@0 | 156 | interceptor_)) |
michael@0 | 157 | return STATUS_UNSUCCESSFUL; |
michael@0 | 158 | |
michael@0 | 159 | // Copy the local thunk buffer to the child. |
michael@0 | 160 | SIZE_T actual; |
michael@0 | 161 | if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, |
michael@0 | 162 | sizeof(ServiceFullThunk), &actual)) |
michael@0 | 163 | return STATUS_UNSUCCESSFUL; |
michael@0 | 164 | |
michael@0 | 165 | if (sizeof(ServiceFullThunk) != actual) |
michael@0 | 166 | return STATUS_UNSUCCESSFUL; |
michael@0 | 167 | |
michael@0 | 168 | // And now change the function to intercept, on the child. |
michael@0 | 169 | if (NULL != ntdll_base_) { |
michael@0 | 170 | // Running a unit test. |
michael@0 | 171 | if (!::WriteProcessMemory(process_, target_, &local_service, |
michael@0 | 172 | sizeof(local_service), &actual)) |
michael@0 | 173 | return STATUS_UNSUCCESSFUL; |
michael@0 | 174 | } else { |
michael@0 | 175 | if (!WriteProtectedChildMemory(process_, target_, &local_service, |
michael@0 | 176 | sizeof(local_service))) |
michael@0 | 177 | return STATUS_UNSUCCESSFUL; |
michael@0 | 178 | } |
michael@0 | 179 | |
michael@0 | 180 | return STATUS_SUCCESS; |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const { |
michael@0 | 184 | NOTREACHED(); |
michael@0 | 185 | return false; |
michael@0 | 186 | } |
michael@0 | 187 | |
michael@0 | 188 | bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const { |
michael@0 | 189 | NOTREACHED(); |
michael@0 | 190 | return false; |
michael@0 | 191 | } |
michael@0 | 192 | |
michael@0 | 193 | } // namespace sandbox |