Skip to content

Commit

Permalink
frida-helper-backend: add option to allocate stack
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ajwerner committed Sep 10, 2024
1 parent 9539981 commit fbde526
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 3 deletions.
6 changes: 6 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions meson.options
Original file line number Diff line number Diff line change
Expand Up @@ -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'
)
56 changes: 53 additions & 3 deletions src/linux/frida-helper-backend.vala
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
}
}

Expand All @@ -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;
}
}
Expand Down

0 comments on commit fbde526

Please sign in to comment.