Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Allow Stalker.unfollow() during transform (#341)
Kudos to Giovanni Rocca for helping fix this.
  • Loading branch information
oleavr committed Jul 30, 2019
1 parent e8d1fb7 commit c4ab181
Show file tree
Hide file tree
Showing 6 changed files with 365 additions and 18 deletions.
53 changes: 44 additions & 9 deletions gum/backend-arm64/gumstalker-arm64.c
Expand Up @@ -128,9 +128,11 @@ struct _GumExecCtx
GumArm64Relocator relocator;

GumStalkerTransformer * transformer;
gboolean calling_transformer;
GQueue callout_entries;
GumSpinlock callout_lock;
GumEventSink * sink;
gboolean sink_started;
GumEventType sink_mask;
void (* sink_process_impl) (GumEventSink * self, const GumEvent * ev);
GumEvent tmp_event;
Expand Down Expand Up @@ -274,6 +276,7 @@ static void gum_stalker_invalidate_caches (GumStalker * self);
static void gum_stalker_thaw (GumStalker * self, gpointer code, gsize size);
static void gum_stalker_freeze (GumStalker * self, gpointer code, gsize size);

static void gum_exec_ctx_prepare_for_unfollow (GumExecCtx * ctx);
static void gum_exec_ctx_dispose_callouts (GumExecCtx * ctx);
static void gum_exec_ctx_free (GumExecCtx * ctx);
static GumSlab * gum_exec_ctx_add_slab (GumExecCtx * ctx);
Expand Down Expand Up @@ -575,10 +578,15 @@ _gum_stalker_do_follow_me (GumStalker * self,
ctx->current_block = gum_exec_ctx_obtain_block_for (ctx, ret_addr,
&code_address);

gum_event_sink_start (sink);
if (ctx->state == GUM_EXEC_CTX_UNFOLLOW_PENDING)
{
gum_exec_ctx_prepare_for_unfollow (ctx);
gum_exec_ctx_unfollow (ctx, ret_addr);
return ret_addr;
}

g_assert (ctx != NULL);
g_assert (gum_stalker_get_exec_ctx (self) != NULL);
gum_event_sink_start (sink);
ctx->sink_started = TRUE;

return code_address;
}
Expand All @@ -589,11 +597,16 @@ gum_stalker_unfollow_me (GumStalker * self)
GumExecCtx * ctx;

ctx = gum_stalker_get_exec_ctx (self);
g_assert (ctx != NULL);
if (ctx == NULL)
return;

gum_exec_ctx_dispose_callouts (ctx);
if (ctx->calling_transformer)
{
ctx->state = GUM_EXEC_CTX_UNFOLLOW_PENDING;
return;
}

gum_event_sink_stop (ctx->sink);
gum_exec_ctx_prepare_for_unfollow (ctx);

if (ctx->current_block != NULL &&
ctx->current_block->has_call_to_excluded_range)
Expand Down Expand Up @@ -659,9 +672,7 @@ gum_stalker_unfollow (GumStalker * self,
GumExecCtx * ctx = (GumExecCtx *) cur->data;
if (ctx->thread_id == thread_id && ctx->state == GUM_EXEC_CTX_ACTIVE)
{
gum_exec_ctx_dispose_callouts (ctx);

gum_event_sink_stop (ctx->sink);
gum_exec_ctx_prepare_for_unfollow (ctx);

if (gum_exec_ctx_has_executed (ctx))
{
Expand Down Expand Up @@ -908,6 +919,7 @@ gum_stalker_create_exec_ctx (GumStalker * self,
ctx->transformer = g_object_ref (transformer);
else
ctx->transformer = gum_stalker_transformer_make_default ();
ctx->calling_transformer = FALSE;
g_queue_init (&ctx->callout_entries);
gum_spinlock_init (&ctx->callout_lock);
ctx->sink = (GumEventSink *) g_object_ref (sink);
Expand Down Expand Up @@ -970,6 +982,19 @@ gum_stalker_freeze (GumStalker * self,
gum_clear_cache (code, size);
}

static void
gum_exec_ctx_prepare_for_unfollow (GumExecCtx * ctx)
{
gum_exec_ctx_dispose_callouts (ctx);

if (ctx->sink_started)
{
gum_event_sink_stop (ctx->sink);

ctx->sink_started = FALSE;
}
}

static void
gum_exec_ctx_dispose_callouts (GumExecCtx * ctx)
{
Expand Down Expand Up @@ -1145,6 +1170,12 @@ gum_exec_ctx_replace_current_block_with (GumExecCtx * ctx,
{
ctx->current_block = gum_exec_ctx_obtain_block_for (ctx, start_address,
&ctx->resume_at);
if (ctx->state == GUM_EXEC_CTX_UNFOLLOW_PENDING)
{
gum_exec_ctx_prepare_for_unfollow (ctx);

gum_exec_ctx_unfollow (ctx, start_address);
}
}

return ctx->resume_at;
Expand Down Expand Up @@ -1233,9 +1264,13 @@ gum_exec_ctx_obtain_block_for (GumExecCtx * ctx,
gum_arm64_writer_put_ldp_reg_reg_reg_offset (cw, ARM64_REG_X16, ARM64_REG_X17,
ARM64_REG_SP, 16 + GUM_RED_ZONE_SIZE, GUM_INDEX_POST_ADJUST);

ctx->calling_transformer = TRUE;

gum_stalker_transformer_transform_block (ctx->transformer, &iterator,
(GumStalkerWriter *) cw);

ctx->calling_transformer = FALSE;

if (gc.continuation_real_address != NULL)
{
GumBranchTarget continue_target = { 0, };
Expand Down
56 changes: 48 additions & 8 deletions gum/backend-x86/gumstalker-x86.c
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2018 Ole André Vadla Ravnås <oleavr@nowsecure.com>
* Copyright (C) 2009-2019 Ole André Vadla Ravnås <oleavr@nowsecure.com>
* Copyright (C) 2010-2013 Karl Trygve Kalleberg <karltk@boblycat.org>
*
* Licence: wxWindows Library Licence, Version 3.1
Expand Down Expand Up @@ -137,9 +137,11 @@ struct _GumExecCtx
GumX86Relocator relocator;

GumStalkerTransformer * transformer;
gboolean calling_transformer;
GQueue callout_entries;
GumSpinlock callout_lock;
GumEventSink * sink;
gboolean sink_started;
GumEventType sink_mask;
void (* sink_process_impl) (GumEventSink * self, const GumEvent * ev);
GumEvent tmp_event;
Expand Down Expand Up @@ -305,6 +307,7 @@ static GumExecCtx * gum_stalker_create_exec_ctx (GumStalker * self,
static GumExecCtx * gum_stalker_get_exec_ctx (GumStalker * self);
static void gum_stalker_invalidate_caches (GumStalker * self);

static void gum_exec_ctx_prepare_for_unfollow (GumExecCtx * ctx);
static void gum_exec_ctx_dispose_callouts (GumExecCtx * ctx);
static void gum_exec_ctx_free (GumExecCtx * ctx);
static void gum_exec_ctx_unfollow (GumExecCtx * ctx, gpointer resume_at);
Expand Down Expand Up @@ -708,11 +711,21 @@ _gum_stalker_do_follow_me (GumStalker * self,
ctx = gum_stalker_create_exec_ctx (self, gum_process_get_current_thread_id (),
transformer, sink);
gum_tls_key_set_value (self->exec_ctx, ctx);

ctx->current_block = gum_exec_ctx_obtain_block_for (ctx, *ret_addr_ptr,
&code_address);
*ret_addr_ptr = code_address;

if (ctx->state == GUM_EXEC_CTX_UNFOLLOW_PENDING)
{
gum_exec_ctx_prepare_for_unfollow (ctx);
gum_exec_ctx_unfollow (ctx, *ret_addr_ptr);
return;
}

gum_event_sink_start (sink);
ctx->sink_started = TRUE;

*ret_addr_ptr = code_address;
}

void
Expand All @@ -721,11 +734,16 @@ gum_stalker_unfollow_me (GumStalker * self)
GumExecCtx * ctx;

ctx = gum_stalker_get_exec_ctx (self);
g_assert (ctx != NULL);
if (ctx == NULL)
return;

gum_exec_ctx_dispose_callouts (ctx);
if (ctx->calling_transformer)
{
ctx->state = GUM_EXEC_CTX_UNFOLLOW_PENDING;
return;
}

gum_event_sink_stop (ctx->sink);
gum_exec_ctx_prepare_for_unfollow (ctx);

if (ctx->current_block != NULL &&
ctx->current_block->has_call_to_excluded_range)
Expand Down Expand Up @@ -791,9 +809,7 @@ gum_stalker_unfollow (GumStalker * self,
GumExecCtx * ctx = (GumExecCtx *) cur->data;
if (ctx->thread_id == thread_id && ctx->state == GUM_EXEC_CTX_ACTIVE)
{
gum_exec_ctx_dispose_callouts (ctx);

gum_event_sink_stop (ctx->sink);
gum_exec_ctx_prepare_for_unfollow (ctx);

if (gum_exec_ctx_has_executed (ctx))
{
Expand Down Expand Up @@ -1042,6 +1058,7 @@ gum_stalker_create_exec_ctx (GumStalker * self,
ctx->transformer = g_object_ref (transformer);
else
ctx->transformer = gum_stalker_transformer_make_default ();
ctx->calling_transformer = FALSE;
g_queue_init (&ctx->callout_entries);
gum_spinlock_init (&ctx->callout_lock);
ctx->sink = (GumEventSink *) g_object_ref (sink);
Expand Down Expand Up @@ -1082,6 +1099,19 @@ gum_stalker_invalidate_caches (GumStalker * self)
GUM_STALKER_UNLOCK (self);
}

static void
gum_exec_ctx_prepare_for_unfollow (GumExecCtx * ctx)
{
gum_exec_ctx_dispose_callouts (ctx);

if (ctx->sink_started)
{
gum_event_sink_stop (ctx->sink);

ctx->sink_started = FALSE;
}
}

static void
gum_exec_ctx_dispose_callouts (GumExecCtx * ctx)
{
Expand Down Expand Up @@ -1236,6 +1266,12 @@ gum_exec_ctx_replace_current_block_with (GumExecCtx * ctx,
{
ctx->current_block = gum_exec_ctx_obtain_block_for (ctx, start_address,
&ctx->resume_at);
if (ctx->state == GUM_EXEC_CTX_UNFOLLOW_PENDING)
{
gum_exec_ctx_prepare_for_unfollow (ctx);

gum_exec_ctx_unfollow (ctx, start_address);
}
}

return ctx->resume_at;
Expand Down Expand Up @@ -1382,9 +1418,13 @@ gum_exec_ctx_obtain_block_for (GumExecCtx * ctx,
iterator.instruction.end = NULL;
iterator.requirements = GUM_REQUIRE_NOTHING;

ctx->calling_transformer = TRUE;

gum_stalker_transformer_transform_block (ctx->transformer, &iterator,
(GumStalkerWriter *) cw);

ctx->calling_transformer = FALSE;

if (gc.continuation_real_address != NULL)
{
GumBranchTarget continue_target = { 0, };
Expand Down
9 changes: 9 additions & 0 deletions tests/core/arch-arm64/stalker-arm64-fixture.c
Expand Up @@ -183,6 +183,7 @@ silence_warnings (void)

typedef struct _StalkerVictimContext StalkerVictimContext;
typedef guint StalkerVictimState;
typedef struct _UnfollowTransformContext UnfollowTransformContext;

struct _StalkerVictimContext
{
Expand All @@ -202,3 +203,11 @@ enum _StalkerVictimState
STALKER_VICTIM_READY_FOR_SHUTDOWN,
STALKER_VICTIM_IS_SHUTDOWN
};

struct _UnfollowTransformContext
{
GumStalker * stalker;
guint num_blocks_transformed;
guint target_block;
gint max_instructions;
};

0 comments on commit c4ab181

Please sign in to comment.