From fbde52649f17792ddcc09b58599bd212fe4f3d2f Mon Sep 17 00:00:00 2001 From: Andrew Werner Date: Tue, 10 Sep 2024 13:09:35 -0400 Subject: [PATCH] frida-helper-backend: add option to allocate stack For some target programs it's not reasonable to assume that any hijacked thread has a large stack. For example, in Go, stacks are often small and are allocated on the heap. The injector bootstrap program uses kilobytes of stack. In order to side-step this problem, this patch enables an option to allocate an auxiliary stack for the remote call to use and, for the bootstrapper and loader, uses it. The calls to mmap and munmap don't use much stack, so they are fine. Fixes #544 --- meson.build | 6 ++++ meson.options | 6 ++++ src/linux/frida-helper-backend.vala | 56 +++++++++++++++++++++++++++-- 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index c6a5be57d..327e46600 100644 --- a/meson.build +++ b/meson.build @@ -668,6 +668,12 @@ if have_mapper cdata.set('HAVE_MAPPER', 1) endif + +allocate_linux_injector_stack_opt = get_option('allocate-linux-injector-stack') +if allocate_linux_injector_stack_opt.enabled() + vala_flags += ['--define=ALLOCATE_LINUX_INJECTOR_STACK'] +endif + configure_file( output: 'config.h', configuration: cdata) diff --git a/meson.options b/meson.options index 392e29946..20e8d7bf5 100644 --- a/meson.options +++ b/meson.options @@ -161,3 +161,9 @@ option('tests', value: 'auto', description: 'Build tests' ) + +option('allocate-linux-injector-stack', + type: 'feature', + value: 'disabled', + description: 'Allocate a new stack for injected remote calls in linux' +) diff --git a/src/linux/frida-helper-backend.vala b/src/linux/frida-helper-backend.vala index ed84fc4d6..1c9c6dfaa 100644 --- a/src/linux/frida-helper-backend.vala +++ b/src/linux/frida-helper-backend.vala @@ -1018,7 +1018,11 @@ namespace Frida { uint64 loader_base = (uintptr) bres.context.allocation_base; - var call_builder = new RemoteCallBuilder (loader_base, saved_regs); + GPRegs regs = saved_regs; +#if ALLOCATE_LINUX_INJECTOR_STACK + regs.stack_pointer = bres.allocated_stack.stack_root; +#endif + var call_builder = new RemoteCallBuilder (loader_base, regs); call_builder.add_argument (loader_base + loader_layout.ctx_offset); RemoteCall loader_call = call_builder.build (this); RemoteCallResult loader_result = yield loader_call.execute (cancellable); @@ -1078,7 +1082,10 @@ namespace Frida { uint64 allocation_base = 0; size_t allocation_size = size_t.max (bootstrapper_size, loader_size); - +#if ALLOCATE_LINUX_INJECTOR_STACK + uint64 stack_base = 0; + size_t stack_size = 64 << 10; // enough for the bootstrapper +#endif uint64 remote_mmap = 0; uint64 remote_munmap = 0; ProcMapsEntry? remote_libc = ProcMapsEntry.find_by_path (pid, local_libc.path); @@ -1121,6 +1128,16 @@ namespace Frida { code_swap.revert (); } +#if ALLOCATE_LINUX_INJECTOR_STACK + stack_base = yield allocate_memory (remote_mmap, stack_size, + Posix.PROT_READ | Posix.PROT_WRITE | Posix.PROT_EXEC, cancellable); + result.allocated_stack.stack_base = (void *)stack_base; + result.allocated_stack.stack_size = stack_size; + GPRegs regs = saved_regs; + regs.stack_pointer = result.allocated_stack.stack_root; +#else + GPRegs regs = saved_regs; +#endif try { write_memory (allocation_base, bootstrapper_code); maybe_fixup_helper_code (allocation_base, bootstrapper_code); @@ -1129,7 +1146,7 @@ namespace Frida { HelperBootstrapStatus status = SUCCESS; do { - var call_builder = new RemoteCallBuilder (code_start, saved_regs); + var call_builder = new RemoteCallBuilder (code_start, regs); unowned uint8[] fallback_ld_data = fallback_ld.data; unowned uint8[] fallback_libc_data = fallback_libc.data; @@ -1198,6 +1215,12 @@ namespace Frida { yield deallocate_memory (remote_munmap, allocation_base, allocation_size, null); } catch (GLib.Error e) { } +#if ALLOCATE_LINUX_INJECTOR_STACK + try { + yield deallocate_memory (remote_munmap, stack_base, stack_size, null); + } catch (GLib.Error e) { + } +#endif } throw_api_error (e); @@ -1393,8 +1416,15 @@ namespace Frida { } public async void deallocate (BootstrapResult bres, Cancellable? cancellable) throws Error, IOError { + yield deallocate_memory ((uintptr) bres.libc.munmap, (uintptr) bres.context.allocation_base, bres.context.allocation_size, cancellable); +#if ALLOCATE_LINUX_INJECTOR_STACK + if (bres.allocated_stack.stack_base != null) { + yield deallocate_memory ((uintptr) bres.libc.munmap, (uintptr) bres.allocated_stack.stack_base, + (size_t) bres.allocated_stack.stack_size, cancellable); + } +#endif } } @@ -1416,14 +1446,34 @@ namespace Frida { } } +#if ALLOCATE_LINUX_INJECTOR_STACK + private struct AllocatedStack { + public void * stack_base; + public uint64 stack_size; + + public uint64 stack_root { + get { + return ((uint64) stack_base) + stack_size; + } + } + } +#endif + + private class BootstrapResult { public HelperBootstrapContext context; public HelperLibcApi libc; +#if ALLOCATE_LINUX_INJECTOR_STACK + public AllocatedStack allocated_stack; +#endif public BootstrapResult clone () { var res = new BootstrapResult (); res.context = context; res.libc = libc; +#if ALLOCATE_LINUX_INJECTOR_STACK + res.allocated_stack = allocated_stack; +#endif return res; } }