Skip to content

Commit

Permalink
linux: Allocate stack for calls
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 changes the
boostrapper to allocate an auxiliary stack for remote calls 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
oleavr committed Oct 21, 2024
1 parent d0fe3a1 commit 5e28fbe
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 4 deletions.
43 changes: 39 additions & 4 deletions src/linux/frida-helper-backend.vala
Original file line number Diff line number Diff line change
Expand Up @@ -1017,8 +1017,10 @@ namespace Frida {
establish_connection (launch, spec, bres, agent_ctrl, fallback_address, cancellable);

uint64 loader_base = (uintptr) bres.context.allocation_base;

var call_builder = new RemoteCallBuilder (loader_base, saved_regs);
// Set the stack pointer to the allocated stack root.
GPRegs regs = saved_regs;
regs.stack_pointer = bres.allocated_stack.stack_root;
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 @@ -1088,9 +1090,13 @@ namespace Frida {
remote_munmap = remote_libc.base_address + munmap_offset;
}

uint64 stack_base = 0;
size_t stack_size = 64 << 10; // enough for the bootstrapper
if (remote_mmap != 0) {
allocation_base = yield allocate_memory (remote_mmap, allocation_size,
Posix.PROT_READ | Posix.PROT_WRITE | Posix.PROT_EXEC, cancellable);
stack_base = yield allocate_memory (remote_mmap, stack_size,
Posix.PROT_READ | Posix.PROT_WRITE | Posix.PROT_EXEC, cancellable);
} else {
var code_swap = yield new ProcessCodeSwapScope (this, bootstrapper_code, cancellable);
uint64 code_start = code_swap.code_start;
Expand All @@ -1104,6 +1110,7 @@ namespace Frida {

var bootstrap_ctx = HelperBootstrapContext ();
bootstrap_ctx.allocation_size = allocation_size;
bootstrap_ctx.stack_size = stack_size;
write_memory (bootstrap_ctx_location, (uint8[]) &bootstrap_ctx);

call_builder.add_argument (bootstrap_ctx_location);
Expand All @@ -1117,10 +1124,13 @@ namespace Frida {
Memory.copy (&bootstrap_ctx, output_context, output_context.length);

allocation_base = (uintptr) bootstrap_ctx.allocation_base;

stack_base = (uintptr) bootstrap_ctx.stack_base;
code_swap.revert ();
}

result.allocated_stack.stack_base = (void *)stack_base;
result.allocated_stack.stack_size = stack_size;

try {
write_memory (allocation_base, bootstrapper_code);
maybe_fixup_helper_code (allocation_base, bootstrapper_code);
Expand All @@ -1129,7 +1139,9 @@ namespace Frida {

HelperBootstrapStatus status = SUCCESS;
do {
var call_builder = new RemoteCallBuilder (code_start, saved_regs);
GPRegs regs = saved_regs;
regs.stack_pointer = result.allocated_stack.stack_root;
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 +1210,10 @@ namespace Frida {
yield deallocate_memory (remote_munmap, allocation_base, allocation_size, null);
} catch (GLib.Error e) {
}
try {
yield deallocate_memory (remote_munmap, stack_base, stack_size, null);
} catch (GLib.Error e) {
}
}

throw_api_error (e);
Expand Down Expand Up @@ -1395,6 +1411,8 @@ 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);
yield deallocate_memory ((uintptr) bres.libc.munmap, (uintptr) bres.allocated_stack.stack_base,
bres.allocated_stack.stack_size, cancellable);
}
}

Expand All @@ -1416,14 +1434,28 @@ namespace Frida {
}
}

private struct AllocatedStack {
public void * stack_base;
public size_t stack_size;

public uint64 stack_root {
get {
return (uint64) stack_base + (uint64) stack_size;
}
}
}


private class BootstrapResult {
public HelperBootstrapContext context;
public HelperLibcApi libc;
public AllocatedStack allocated_stack;

public BootstrapResult clone () {
var res = new BootstrapResult ();
res.context = context;
res.libc = libc;
res.allocated_stack = allocated_stack;
return res;
}
}
Expand Down Expand Up @@ -1717,6 +1749,9 @@ namespace Frida {
bool enable_ctrlfds;
int ctrlfds[2];
HelperLibcApi * libc;

void * stack_base;
size_t stack_size;
}

protected struct HelperLoaderContext {
Expand Down
Binary file modified src/linux/helpers/bootstrapper-arm64.bin
Binary file not shown.
6 changes: 6 additions & 0 deletions src/linux/helpers/bootstrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ frida_bootstrap (FridaBootstrapContext * ctx)
FridaLibcApi * libc = ctx->libc;
FridaProcessLayout process;

if (ctx->stack_base == NULL && ctx->stack_size > 0)
{
ctx->stack_base = mmap (NULL, ctx->stack_size, PROT_READ | PROT_WRITE , MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ctx->stack_base == MAP_FAILED)
return FRIDA_BOOTSTRAP_ALLOCATION_ERROR;
}
if (ctx->allocation_base == NULL)
{
ctx->allocation_base = mmap (NULL, ctx->allocation_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
Expand Down
3 changes: 3 additions & 0 deletions src/linux/helpers/inject-context.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ struct _FridaBootstrapContext
int enable_ctrlfds;
int ctrlfds[2];
FridaLibcApi * libc;

void * stack_base;
size_t stack_size;
};

struct _FridaLoaderContext
Expand Down

0 comments on commit 5e28fbe

Please sign in to comment.