Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: sandbox/win/wow_helper/service64_resolver.cc

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

Powered by Google App Engine
This is Rietveld 408576698