Skip to content

Commit

Permalink
linux: Fix unreliable injection when libc is absent
Browse files Browse the repository at this point in the history
Such as early in the process lifetime, and in targets without an RTLD.

The range chosen by our ProcessCodeSwapScope could happen to overlap
with the program's ELF header, which we would subsequently parse and
then carry on to fail in spectacular ways.

So for the case when libc is absent, instead of attempting the full
bootstrap right away, only perform mmap() and then return. Since we
now allocate enough for both the bootstrapper and the loader, we can
eliminate the second mmap() in the case when libc is already there.
  • Loading branch information
oleavr committed Mar 7, 2024
1 parent 69d3634 commit 2ca8624
Show file tree
Hide file tree
Showing 11 changed files with 110 additions and 70 deletions.
138 changes: 84 additions & 54 deletions src/linux/frida-helper-backend.vala
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,7 @@ namespace Frida {
LoaderLayout loader_layout = compute_loader_layout (spec, fallback_address);

BootstrapResult bootstrap_result = yield bootstrap (loader_layout.size, cancellable);
var loader_base = (uint64) bootstrap_result.context.loader_base;
var loader_base = (uint64) bootstrap_result.context.allocation_base;

try {
unowned uint8[] loader_code = Frida.Data.HelperBackend.get_loader_bin_blob ().data;
Expand All @@ -915,9 +915,9 @@ namespace Frida {
loader_ctx.libc = (HelperLibcApi *) (loader_base + loader_layout.libc_api_offset);
write_memory (loader_base + loader_layout.ctx_offset, (uint8[]) &loader_ctx);
write_memory (loader_base + loader_layout.libc_api_offset, (uint8[]) &bootstrap_result.libc);
write_memory (loader_base + loader_layout.agent_entrypoint_offset, spec.entrypoint.data);
write_memory (loader_base + loader_layout.agent_data_offset, spec.data.data);
write_memory (loader_base + loader_layout.fallback_address_offset, fallback_address.data);
write_memory_string (loader_base + loader_layout.agent_entrypoint_offset, spec.entrypoint);
write_memory_string (loader_base + loader_layout.agent_data_offset, spec.data);
write_memory_string (loader_base + loader_layout.fallback_address_offset, fallback_address);

return yield launch_loader (FROM_SCRATCH, spec, bootstrap_result, null, fallback_address, loader_layout,
cancellable);
Expand All @@ -940,7 +940,7 @@ namespace Frida {

string fallback_address = make_fallback_address ();
LoaderLayout loader_layout = compute_loader_layout (spec, fallback_address);
uint64 loader_base = (uint64) bootstrap_result.context.loader_base;
uint64 loader_base = (uint64) bootstrap_result.context.allocation_base;
uint64 loader_ctrlfds_location = loader_base + loader_layout.ctx_offset;

if (bootstrap_result.context.enable_ctrlfds) {
Expand Down Expand Up @@ -1014,7 +1014,7 @@ namespace Frida {
Future<RemoteAgent> future_agent =
establish_connection (launch, spec, bres, agent_ctrl, fallback_address, cancellable);

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

var call_builder = new RemoteCallBuilder (loader_base, saved_regs);
call_builder.add_argument (loader_base + loader_layout.ctx_offset);
Expand Down Expand Up @@ -1072,9 +1072,11 @@ namespace Frida {
var result = new BootstrapResult ();

unowned uint8[] bootstrapper_code = Frida.Data.HelperBackend.get_bootstrapper_bin_blob ().data;
uint64 bootstrapper_base = 0;
size_t bootstrapper_size = round_size_to_page_size (bootstrapper_code.length);

uint64 allocation_base = 0;
size_t allocation_size = size_t.max (bootstrapper_size, loader_size);

uint64 remote_mmap = 0;
uint64 remote_munmap = 0;
ProcMapsEntry? remote_libc = ProcMapsEntry.find_by_path (pid, local_libc.path);
Expand All @@ -1085,28 +1087,48 @@ namespace Frida {
}

if (remote_mmap != 0) {
bootstrapper_base = yield allocate_memory (remote_mmap, bootstrapper_size,
allocation_base = yield allocate_memory (remote_mmap, allocation_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;
uint64 code_end = code_start + bootstrapper_size;
maybe_fixup_helper_code (code_start, bootstrapper_code);

var call_builder = new RemoteCallBuilder (code_start, saved_regs);

uint64 bootstrap_ctx_location;
call_builder.reserve_stack_space (sizeof (HelperBootstrapContext), out bootstrap_ctx_location);

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

call_builder.add_argument (bootstrap_ctx_location);

RemoteCall bootstrap_call = call_builder.build (this);
RemoteCallResult bootstrap_result = yield bootstrap_call.execute (cancellable);
var status = (HelperBootstrapStatus) bootstrap_result.return_value;
if (bootstrap_result.status != COMPLETED || status != ALLOCATION_SUCCESS)
throw_bootstrap_error (bootstrap_result, status, code_start, code_end);

uint8[] output_context = read_memory (bootstrap_ctx_location, sizeof (HelperBootstrapContext));
Memory.copy (&bootstrap_ctx, output_context, output_context.length);

allocation_base = (uint64) bootstrap_ctx.allocation_base;

code_swap.revert ();
}

try {
if (bootstrapper_base != 0)
write_memory (bootstrapper_base, bootstrapper_code);
write_memory (allocation_base, bootstrapper_code);
maybe_fixup_helper_code (allocation_base, bootstrapper_code);

HelperBootstrapStatus status = SUCCESS;
do {
uint64 code_start;
ProcessCodeSwapScope? code_swap = null;
if (bootstrapper_base != 0) {
code_start = bootstrapper_base;
} else {
code_swap = yield new ProcessCodeSwapScope (this, bootstrapper_code, cancellable);
code_start = code_swap.code_start;
}
uint64 code_start = allocation_base;
uint64 code_end = code_start + bootstrapper_size;

maybe_fixup_helper_code (code_start, bootstrapper_code);

var call_builder = new RemoteCallBuilder (code_start, saved_regs);

unowned uint8[] fallback_ld_data = fallback_ld.data;
Expand All @@ -1120,10 +1142,11 @@ namespace Frida {
.reserve_stack_space (fallback_libc_data.length + 1, out fallback_libc_location);

var bootstrap_ctx = HelperBootstrapContext ();
bootstrap_ctx.allocation_base = (void *) allocation_base;
bootstrap_ctx.allocation_size = allocation_size;
bootstrap_ctx.page_size = Gum.query_page_size ();
bootstrap_ctx.fallback_ld = (string *) fallback_ld_location;
bootstrap_ctx.fallback_libc = (string *) fallback_libc_location;
bootstrap_ctx.loader_size = loader_size;
bootstrap_ctx.enable_ctrlfds = PidFileDescriptor.getfd_is_supported ();
bootstrap_ctx.libc = (HelperLibcApi *) libc_api_location;
write_memory (bootstrap_ctx_location, (uint8[]) &bootstrap_ctx);
Expand All @@ -1144,26 +1167,8 @@ namespace Frida {
status = (HelperBootstrapStatus) bootstrap_result.return_value;
}

if (!(bootstrap_result.status == COMPLETED && (status == SUCCESS || status == TOO_EARLY))) {
if (bootstrap_result.status == COMPLETED) {
throw new Error.NOT_SUPPORTED ("Bootstrapper failed due to '%s'; " +
"please file a bug",
Marshal.enum_to_nick<HelperBootstrapStatus> (status));
} else {
uint64 pc = bootstrap_result.regs.program_counter;
if (pc >= code_start && pc < code_end) {
throw new Error.NOT_SUPPORTED (
"Bootstrapper crashed with signal %d at offset 0x%x; please file a bug\n%s",
bootstrap_result.stop_signal,
(uint) (pc - code_start),
bootstrap_result.regs.to_string ());
} else {
throw new Error.NOT_SUPPORTED ("Bootstrapper crashed with signal %d; please file a bug\n%s",
bootstrap_result.stop_signal,
bootstrap_result.regs.to_string ());
}
}
}
if (!(bootstrap_result.status == COMPLETED && (status == SUCCESS || status == TOO_EARLY)))
throw_bootstrap_error (bootstrap_result, status, code_start, code_end);

uint8[] output_context = read_memory (bootstrap_ctx_location, sizeof (HelperBootstrapContext));
Memory.copy (&result.context, output_context, output_context.length);
Expand All @@ -1184,19 +1189,13 @@ namespace Frida {
result.libc.dlerror = rebase_pointer ((uintptr) dlerror, local_android_ld, remote_ld);
}

if (code_swap != null)
code_swap.revert ();

if (status == TOO_EARLY)
yield resume_until_execution_reaches ((uint64) result.context.r_brk, cancellable);
} while (status == TOO_EARLY);

if (bootstrapper_base != 0)
yield deallocate_memory (remote_munmap, bootstrapper_base, bootstrapper_size, cancellable);
} catch (GLib.Error e) {
if (bootstrapper_base != 0) {
if (remote_munmap != 0) {
try {
yield deallocate_memory (remote_munmap, bootstrapper_base, bootstrapper_size, null);
yield deallocate_memory (remote_munmap, allocation_base, allocation_size, null);
} catch (GLib.Error e) {
}
}
Expand All @@ -1207,6 +1206,29 @@ namespace Frida {
return result;
}

[NoReturn]
private static void throw_bootstrap_error (RemoteCallResult bootstrap_result, HelperBootstrapStatus status,
uint64 code_start, uint64 code_end) throws Error {
if (bootstrap_result.status == COMPLETED) {
throw new Error.NOT_SUPPORTED ("Bootstrapper failed due to '%s'; " +
"please file a bug",
Marshal.enum_to_nick<HelperBootstrapStatus> (status));
} else {
uint64 pc = bootstrap_result.regs.program_counter;
if (pc >= code_start && pc < code_end) {
throw new Error.NOT_SUPPORTED (
"Bootstrapper crashed with signal %d at offset 0x%x; please file a bug\n%s",
bootstrap_result.stop_signal,
(uint) (pc - code_start),
bootstrap_result.regs.to_string ());
} else {
throw new Error.NOT_SUPPORTED ("Bootstrapper crashed with signal %d; please file a bug\n%s",
bootstrap_result.stop_signal,
bootstrap_result.regs.to_string ());
}
}
}

private static void * rebase_pointer (uintptr local_ptr, ProcMapsEntry local_module, ProcMapsEntry remote_module) {
var offset = local_ptr - local_module.base_address;
return (void *) (remote_module.base_address + offset);
Expand Down Expand Up @@ -1371,8 +1393,8 @@ namespace Frida {
}

public async void deallocate (BootstrapResult bres, Cancellable? cancellable) throws Error, IOError {
yield deallocate_memory ((uint64) bres.libc.munmap, (uint64) bres.context.loader_base, bres.context.loader_size,
cancellable);
yield deallocate_memory ((uint64) bres.libc.munmap, (uint64) bres.context.allocation_base,
bres.context.allocation_size, cancellable);
}
}

Expand Down Expand Up @@ -1672,23 +1694,26 @@ namespace Frida {
}

protected enum HelperBootstrapStatus {
ALLOCATION_SUCCESS,
ALLOCATION_ERROR,

SUCCESS,
AUXV_NOT_FOUND,
TOO_EARLY,
LIBC_LOAD_ERROR,
LIBC_UNSUPPORTED,
MMAP_ERROR,
}

protected struct HelperBootstrapContext {
void * allocation_base;
size_t allocation_size;

size_t page_size;
string * fallback_ld;
string * fallback_libc;
HelperRtldFlavor rtld_flavor;
void * rtld_base;
void * r_brk;
size_t loader_size;
void * loader_base;
bool enable_ctrlfds;
int ctrlfds[2];
HelperLibcApi * libc;
Expand Down Expand Up @@ -2181,6 +2206,11 @@ namespace Frida {
}
}

public void write_memory_string (uint64 address, string str) throws Error {
unowned uint8[] data = str.data;
write_memory (address, data[:data.length + 1]);
}

private static ssize_t process_vm_readv_impl (uint pid,
[CCode (array_length_type = "unsigned long")]
Posix.iovector[] local_iov,
Expand Down
Binary file modified src/linux/helpers/bootstrapper-arm.bin
Binary file not shown.
Binary file modified src/linux/helpers/bootstrapper-arm64.bin
Binary file not shown.
Binary file modified src/linux/helpers/bootstrapper-mips.bin
Binary file not shown.
Binary file modified src/linux/helpers/bootstrapper-mips64.bin
Binary file not shown.
Binary file modified src/linux/helpers/bootstrapper-mips64el.bin
Binary file not shown.
Binary file modified src/linux/helpers/bootstrapper-mipsel.bin
Binary file not shown.
Binary file modified src/linux/helpers/bootstrapper-x86.bin
Binary file not shown.
Binary file modified src/linux/helpers/bootstrapper-x86_64.bin
Binary file not shown.
33 changes: 20 additions & 13 deletions src/linux/helpers/bootstrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,14 @@ frida_bootstrap (FridaBootstrapContext * ctx)
FridaLibcApi * libc = ctx->libc;
FridaProcessLayout process;

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);
return (ctx->allocation_base == MAP_FAILED)
? FRIDA_BOOTSTRAP_ALLOCATION_ERROR
: FRIDA_BOOTSTRAP_ALLOCATION_SUCCESS;
}

if (!frida_probe_process (ctx->page_size, &process))
return FRIDA_BOOTSTRAP_AUXV_NOT_FOUND;

Expand All @@ -174,10 +182,6 @@ frida_bootstrap (FridaBootstrapContext * ctx)
if (!frida_resolve_libc_apis (&process, libc))
return FRIDA_BOOTSTRAP_LIBC_UNSUPPORTED;

ctx->loader_base = mmap (NULL, ctx->loader_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ctx->loader_base == MAP_FAILED)
return FRIDA_BOOTSTRAP_MMAP_ERROR;

ctx->ctrlfds[0] = -1;
ctx->ctrlfds[1] = -1;
if (ctx->enable_ctrlfds)
Expand Down Expand Up @@ -1109,28 +1113,31 @@ frida_prctl (int option, unsigned long arg2, unsigned long arg3, unsigned long a

#ifdef BUILDING_TEST_PROGRAM

#include <assert.h>
#include <stdio.h>
#include <strings.h>

int
main (void)
{
FridaLibcApi libc;
FridaBootstrapContext ctx;
size_t result;

bzero (&libc, sizeof (libc));
FridaBootstrapStatus status;
FridaLibcApi libc;

bzero (&ctx, sizeof (ctx));
ctx.allocation_size = 4096;
status = frida_bootstrap (&ctx);
assert (status == FRIDA_BOOTSTRAP_ALLOCATION_SUCCESS);
printf ("allocation_base: %p\n", ctx.allocation_base);
assert (ctx.allocation_base != NULL);

bzero (&libc, sizeof (libc));
ctx.page_size = getpagesize ();
ctx.loader_size = 4096;
ctx.enable_ctrlfds = true;
ctx.libc = &libc;

result = frida_bootstrap (&ctx);

printf ("result: %zu\n", result);
printf ("loader_base: %p\n", ctx.loader_base);
status = frida_bootstrap (&ctx);
printf ("status: %zu\n", status);

return 0;
}
Expand Down
9 changes: 6 additions & 3 deletions src/linux/helpers/inject-context.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,27 @@ typedef int FridaRtldFlavor;

enum _FridaBootstrapStatus
{
FRIDA_BOOTSTRAP_ALLOCATION_SUCCESS,
FRIDA_BOOTSTRAP_ALLOCATION_ERROR,

FRIDA_BOOTSTRAP_SUCCESS,
FRIDA_BOOTSTRAP_AUXV_NOT_FOUND,
FRIDA_BOOTSTRAP_TOO_EARLY,
FRIDA_BOOTSTRAP_LIBC_LOAD_ERROR,
FRIDA_BOOTSTRAP_LIBC_UNSUPPORTED,
FRIDA_BOOTSTRAP_MMAP_ERROR,
};

struct _FridaBootstrapContext
{
void * allocation_base;
size_t allocation_size;

size_t page_size;
const char * fallback_ld;
const char * fallback_libc;
FridaRtldFlavor rtld_flavor;
void * rtld_base;
void * r_brk;
size_t loader_size;
void * loader_base;
int enable_ctrlfds;
int ctrlfds[2];
FridaLibcApi * libc;
Expand Down

0 comments on commit 2ca8624

Please sign in to comment.