Skip to content

Commit

Permalink
stalker: Add run_on_thread() and run_on_thread_sync()
Browse files Browse the repository at this point in the history
Co-authored-by: Ole André Vadla Ravnås <[email protected]>
  • Loading branch information
WorksButNotTested and oleavr committed Feb 23, 2024
1 parent 1f64c08 commit 283a391
Show file tree
Hide file tree
Showing 10 changed files with 917 additions and 8 deletions.
61 changes: 60 additions & 1 deletion gum/backend-arm/gumstalker-arm.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2023 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2009-2024 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2023 Håvard Sørbø <[email protected]>
*
* Licence: wxWindows Library Licence, Version 3.1
Expand All @@ -16,6 +16,7 @@
#include "gummemory.h"
#include "gummetalhash.h"
#include "gumspinlock.h"
#include "gumstalker-priv.h"
#include "gumthumbreader.h"
#include "gumthumbrelocator.h"
#include "gumthumbwriter.h"
Expand Down Expand Up @@ -1588,6 +1589,64 @@ gum_call_probe_unref (GumCallProbe * probe)
}
}

void
_gum_stalker_modify_to_run_on_thread (GumStalker * self,
GumThreadId thread_id,
GumCpuContext * cpu_context,
GumStalkerRunOnThreadFunc func,
gpointer data)
{
GumExecCtx * ctx;
guint32 pc;
GumArmWriter * cw;
GumAddress cpu_context_copy, infect_body;

ctx = gum_stalker_create_exec_ctx (self, thread_id, NULL, NULL);

if ((cpu_context->cpsr & GUM_PSR_T_BIT) == 0)
pc = cpu_context->pc;
else
pc = cpu_context->pc + 1;

gum_spinlock_acquire (&ctx->code_lock);

gum_stalker_thaw (self, ctx->thunks, self->thunks_size);
cw = &ctx->arm_writer;
gum_arm_writer_reset (cw, ctx->infect_thunk);

cpu_context_copy = GUM_ADDRESS (gum_arm_writer_cur (cw));
gum_arm_writer_put_bytes (cw, (guint8 *) cpu_context, sizeof (GumCpuContext));

infect_body = GUM_ADDRESS (gum_arm_writer_cur (cw));

gum_exec_ctx_write_arm_prolog (ctx, cw);

gum_arm_writer_put_call_address_with_arguments (cw,
GUM_ADDRESS (func), 2,
GUM_ARG_ADDRESS, GUM_ADDRESS (cpu_context_copy),
GUM_ARG_ADDRESS, GUM_ADDRESS (data));

gum_arm_writer_put_call_address_with_arguments (cw,
GUM_ADDRESS (gum_exec_ctx_unfollow), 2,
GUM_ARG_ADDRESS, GUM_ADDRESS (ctx),
GUM_ARG_ADDRESS, GUM_ADDRESS (pc));

gum_exec_ctx_write_arm_epilog (ctx, cw);

gum_arm_writer_put_push_regs (cw, 2, ARM_REG_R0, ARM_REG_PC);
gum_arm_writer_put_ldr_reg_address (cw, ARM_REG_R0, pc);
gum_arm_writer_put_str_reg_reg_offset (cw, ARM_REG_R0, ARM_REG_SP, 4);
gum_arm_writer_put_pop_regs (cw, 2, ARM_REG_R0, ARM_REG_PC);

gum_arm_writer_flush (cw);
gum_stalker_freeze (self, cw->base, gum_arm_writer_offset (cw));

gum_spinlock_release (&ctx->code_lock);

cpu_context->cpsr &= ~GUM_PSR_T_BIT;
cpu_context->pc = infect_body;
}

static GumExecCtx *
gum_stalker_create_exec_ctx (GumStalker * self,
GumThreadId thread_id,
Expand Down
69 changes: 69 additions & 0 deletions gum/backend-arm64/gumstalker-arm64.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "gummemory.h"
#include "gummetalhash.h"
#include "gumspinlock.h"
#include "gumstalker-priv.h"
#ifdef HAVE_LINUX
# include "gum-init.h"
# include "guminterceptor.h"
Expand Down Expand Up @@ -1928,6 +1929,74 @@ gum_call_probe_unref (GumCallProbe * probe)
}
}

void
_gum_stalker_modify_to_run_on_thread (GumStalker * self,
GumThreadId thread_id,
GumCpuContext * cpu_context,
GumStalkerRunOnThreadFunc func,
gpointer data)
{
GumExecCtx * ctx;
GumAddress pc;
GumArm64Writer * cw;
GumAddress cpu_context_copy;

ctx = gum_stalker_create_exec_ctx (self, thread_id, NULL, NULL);

pc = gum_strip_code_address (cpu_context->pc);

gum_spinlock_acquire (&ctx->code_lock);

gum_stalker_thaw (self, ctx->thunks, self->thunks_size);
cw = &ctx->code_writer;
gum_arm64_writer_reset (cw, ctx->infect_thunk);

cpu_context_copy = GUM_ADDRESS (gum_arm64_writer_cur (cw));
gum_arm64_writer_put_bytes (cw, (guint8 *) cpu_context,
sizeof (GumCpuContext));

ctx->infect_body = GUM_ADDRESS (gum_arm64_writer_cur (cw));

#ifdef HAVE_PTRAUTH
ctx->infect_body = GPOINTER_TO_SIZE (ptrauth_sign_unauthenticated (
GSIZE_TO_POINTER (ctx->infect_body),
ptrauth_key_process_independent_code,
ptrauth_string_discriminator ("pc")));
#endif
gum_exec_ctx_write_prolog (ctx, GUM_PROLOG_MINIMAL, cw);

gum_arm64_writer_put_call_address_with_arguments (cw,
GUM_ADDRESS (func), 2,
GUM_ARG_ADDRESS, cpu_context_copy,
GUM_ARG_ADDRESS, GUM_ADDRESS (data));

gum_arm64_writer_put_call_address_with_arguments (cw,
GUM_ADDRESS (gum_exec_ctx_unfollow), 2,
GUM_ARG_ADDRESS, GUM_ADDRESS (ctx),
GUM_ARG_ADDRESS, pc);

gum_exec_ctx_write_epilog (ctx, GUM_PROLOG_MINIMAL, cw);

/*
* Here we spoil x17 since this is a necessity of the AARCH64 architecture
* when performing long branches. However, the documentation states...
*
* "Registers r16 (IP0) and r17 (IP1) may be used by a linker as a scratch
* register between a routine and any subroutine it calls."
*
* This same approach is used elsewhere in Stalker for arm64.
*/
gum_arm64_writer_put_ldr_reg_address (cw, ARM64_REG_X17, pc);
gum_arm64_writer_put_br_reg_no_auth (cw, ARM64_REG_X17);

gum_arm64_writer_flush (cw);
gum_stalker_freeze (self, cw->base, gum_arm64_writer_offset (cw));

gum_spinlock_release (&ctx->code_lock);

cpu_context->pc = ctx->infect_body;
}

static GumExecCtx *
gum_stalker_create_exec_ctx (GumStalker * self,
GumThreadId thread_id,
Expand Down
13 changes: 12 additions & 1 deletion gum/backend-mips/gumstalker-mips.c
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
/*
* Copyright (C) 2009-2023 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2009-2024 Ole André Vadla Ravnås <[email protected]>
*
* Licence: wxWindows Library Licence, Version 3.1
*/

#include "gumstalker.h"

#include "gumstalker-priv.h"

struct _GumStalker
{
GObject parent;
Expand Down Expand Up @@ -183,6 +185,15 @@ gum_stalker_remove_call_probe (GumStalker * self,
{
}

void
_gum_stalker_modify_to_run_on_thread (GumStalker * self,
GumThreadId thread_id,
GumCpuContext * cpu_context,
GumStalkerRunOnThreadFunc func,
gpointer data)
{
}

gboolean
gum_stalker_iterator_next (GumStalkerIterator * self,
const cs_insn ** insn)
Expand Down
79 changes: 78 additions & 1 deletion gum/backend-x86/gumstalker-x86.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2023 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2009-2024 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2010-2013 Karl Trygve Kalleberg <[email protected]>
* Copyright (C) 2020 Duy Phan Thanh <[email protected]>
*
Expand All @@ -16,6 +16,7 @@
#include "gummemory.h"
#include "gumx86relocator.h"
#include "gumspinlock.h"
#include "gumstalker-priv.h"
#ifdef HAVE_WINDOWS
# include "gumexceptor.h"
#endif
Expand Down Expand Up @@ -2172,6 +2173,82 @@ gum_call_probe_unref (GumCallProbe * probe)
}
}

void
_gum_stalker_modify_to_run_on_thread (GumStalker * self,
GumThreadId thread_id,
GumCpuContext * cpu_context,
GumStalkerRunOnThreadFunc func,
gpointer data)
{
GumExecCtx * ctx;
guint8 * pc;
GumX86Writer * cw;
GumAddress cpu_context_copy;

ctx = gum_stalker_create_exec_ctx (self, thread_id, NULL, NULL);

pc = GSIZE_TO_POINTER (GUM_CPU_CONTEXT_XIP (cpu_context));

gum_spinlock_acquire (&ctx->code_lock);

gum_stalker_thaw (self, ctx->thunks, self->thunks_size);
cw = &ctx->code_writer;
gum_x86_writer_reset (cw, ctx->infect_thunk);

cpu_context_copy = GUM_ADDRESS (gum_x86_writer_cur (cw));
gum_x86_writer_put_bytes (cw, (guint8 *) cpu_context, sizeof (GumCpuContext));

#ifdef HAVE_LINUX
/*
* In case the thread is in a Linux system call we prefix with a couple of
* NOPs so that when we restart, we don't re-attempt the syscall. We will
* drop ourselves back to the syscall once we are done.
*/
gum_x86_writer_put_nop_padding (cw, MAX (sizeof (gum_int80_code),
sizeof (gum_syscall_code)));
#endif

ctx->infect_body = GUM_ADDRESS (gum_x86_writer_cur (cw));
gum_exec_ctx_write_prolog (ctx, GUM_PROLOG_MINIMAL, cw);
gum_x86_writer_put_call_address_with_aligned_arguments (cw, GUM_CALL_CAPI,
GUM_ADDRESS (func), 2,
GUM_ARG_ADDRESS, GUM_ADDRESS (cpu_context_copy),
GUM_ARG_ADDRESS, GUM_ADDRESS (data));
gum_x86_writer_put_call_address_with_aligned_arguments (cw, GUM_CALL_CAPI,
GUM_ADDRESS (gum_exec_ctx_unfollow), 2,
GUM_ARG_ADDRESS, GUM_ADDRESS (ctx),
GUM_ARG_ADDRESS, GUM_ADDRESS (pc));
gum_exec_ctx_write_epilog (ctx, GUM_PROLOG_MINIMAL, cw);

#ifdef HAVE_LINUX
if (memcmp (&pc[-sizeof (gum_int80_code)], gum_int80_code,
sizeof (gum_int80_code)) == 0)
{
gum_x86_writer_put_jmp_address (cw,
GUM_ADDRESS (&pc[-sizeof (gum_int80_code)]));
}
else if (memcmp (&pc[-sizeof (gum_syscall_code)], gum_syscall_code,
sizeof (gum_syscall_code)) == 0)
{
gum_x86_writer_put_jmp_address (cw,
GUM_ADDRESS (&pc[-sizeof (gum_syscall_code)]));
}
else
{
gum_x86_writer_put_jmp_address (cw, GUM_ADDRESS (pc));
}
#else
gum_x86_writer_put_jmp_address (cw, GUM_ADDRESS (pc));
#endif

gum_x86_writer_flush (cw);
gum_stalker_freeze (self, cw->base, gum_x86_writer_offset (cw));

gum_spinlock_release (&ctx->code_lock);

GUM_CPU_CONTEXT_XIP (cpu_context) = ctx->infect_body;
}

static GumExecCtx *
gum_stalker_create_exec_ctx (GumStalker * self,
GumThreadId thread_id,
Expand Down
20 changes: 20 additions & 0 deletions gum/gumstalker-priv.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2024 Ole André Vadla Ravnås <[email protected]>
*
* Licence: wxWindows Library Licence, Version 3.1
*/

#ifndef __GUM_STALKER_PRIV_H__
#define __GUM_STALKER_PRIV_H__

#include "gumstalker.h"

G_BEGIN_DECLS

G_GNUC_INTERNAL void _gum_stalker_modify_to_run_on_thread (GumStalker * self,
GumThreadId thread_id, GumCpuContext * cpu_context,
GumStalkerRunOnThreadFunc func, gpointer data);

G_END_DECLS

#endif
Loading

0 comments on commit 283a391

Please sign in to comment.