Skip to content

Commit c4ab181

Browse files
authored
Allow Stalker.unfollow() during transform (#341)
Kudos to Giovanni Rocca for helping fix this.
1 parent e8d1fb7 commit c4ab181

File tree

6 files changed

+365
-18
lines changed

6 files changed

+365
-18
lines changed

gum/backend-arm64/gumstalker-arm64.c

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,11 @@ struct _GumExecCtx
128128
GumArm64Relocator relocator;
129129

130130
GumStalkerTransformer * transformer;
131+
gboolean calling_transformer;
131132
GQueue callout_entries;
132133
GumSpinlock callout_lock;
133134
GumEventSink * sink;
135+
gboolean sink_started;
134136
GumEventType sink_mask;
135137
void (* sink_process_impl) (GumEventSink * self, const GumEvent * ev);
136138
GumEvent tmp_event;
@@ -274,6 +276,7 @@ static void gum_stalker_invalidate_caches (GumStalker * self);
274276
static void gum_stalker_thaw (GumStalker * self, gpointer code, gsize size);
275277
static void gum_stalker_freeze (GumStalker * self, gpointer code, gsize size);
276278

279+
static void gum_exec_ctx_prepare_for_unfollow (GumExecCtx * ctx);
277280
static void gum_exec_ctx_dispose_callouts (GumExecCtx * ctx);
278281
static void gum_exec_ctx_free (GumExecCtx * ctx);
279282
static GumSlab * gum_exec_ctx_add_slab (GumExecCtx * ctx);
@@ -575,10 +578,15 @@ _gum_stalker_do_follow_me (GumStalker * self,
575578
ctx->current_block = gum_exec_ctx_obtain_block_for (ctx, ret_addr,
576579
&code_address);
577580

578-
gum_event_sink_start (sink);
581+
if (ctx->state == GUM_EXEC_CTX_UNFOLLOW_PENDING)
582+
{
583+
gum_exec_ctx_prepare_for_unfollow (ctx);
584+
gum_exec_ctx_unfollow (ctx, ret_addr);
585+
return ret_addr;
586+
}
579587

580-
g_assert (ctx != NULL);
581-
g_assert (gum_stalker_get_exec_ctx (self) != NULL);
588+
gum_event_sink_start (sink);
589+
ctx->sink_started = TRUE;
582590

583591
return code_address;
584592
}
@@ -589,11 +597,16 @@ gum_stalker_unfollow_me (GumStalker * self)
589597
GumExecCtx * ctx;
590598

591599
ctx = gum_stalker_get_exec_ctx (self);
592-
g_assert (ctx != NULL);
600+
if (ctx == NULL)
601+
return;
593602

594-
gum_exec_ctx_dispose_callouts (ctx);
603+
if (ctx->calling_transformer)
604+
{
605+
ctx->state = GUM_EXEC_CTX_UNFOLLOW_PENDING;
606+
return;
607+
}
595608

596-
gum_event_sink_stop (ctx->sink);
609+
gum_exec_ctx_prepare_for_unfollow (ctx);
597610

598611
if (ctx->current_block != NULL &&
599612
ctx->current_block->has_call_to_excluded_range)
@@ -659,9 +672,7 @@ gum_stalker_unfollow (GumStalker * self,
659672
GumExecCtx * ctx = (GumExecCtx *) cur->data;
660673
if (ctx->thread_id == thread_id && ctx->state == GUM_EXEC_CTX_ACTIVE)
661674
{
662-
gum_exec_ctx_dispose_callouts (ctx);
663-
664-
gum_event_sink_stop (ctx->sink);
675+
gum_exec_ctx_prepare_for_unfollow (ctx);
665676

666677
if (gum_exec_ctx_has_executed (ctx))
667678
{
@@ -908,6 +919,7 @@ gum_stalker_create_exec_ctx (GumStalker * self,
908919
ctx->transformer = g_object_ref (transformer);
909920
else
910921
ctx->transformer = gum_stalker_transformer_make_default ();
922+
ctx->calling_transformer = FALSE;
911923
g_queue_init (&ctx->callout_entries);
912924
gum_spinlock_init (&ctx->callout_lock);
913925
ctx->sink = (GumEventSink *) g_object_ref (sink);
@@ -970,6 +982,19 @@ gum_stalker_freeze (GumStalker * self,
970982
gum_clear_cache (code, size);
971983
}
972984

985+
static void
986+
gum_exec_ctx_prepare_for_unfollow (GumExecCtx * ctx)
987+
{
988+
gum_exec_ctx_dispose_callouts (ctx);
989+
990+
if (ctx->sink_started)
991+
{
992+
gum_event_sink_stop (ctx->sink);
993+
994+
ctx->sink_started = FALSE;
995+
}
996+
}
997+
973998
static void
974999
gum_exec_ctx_dispose_callouts (GumExecCtx * ctx)
9751000
{
@@ -1145,6 +1170,12 @@ gum_exec_ctx_replace_current_block_with (GumExecCtx * ctx,
11451170
{
11461171
ctx->current_block = gum_exec_ctx_obtain_block_for (ctx, start_address,
11471172
&ctx->resume_at);
1173+
if (ctx->state == GUM_EXEC_CTX_UNFOLLOW_PENDING)
1174+
{
1175+
gum_exec_ctx_prepare_for_unfollow (ctx);
1176+
1177+
gum_exec_ctx_unfollow (ctx, start_address);
1178+
}
11481179
}
11491180

11501181
return ctx->resume_at;
@@ -1233,9 +1264,13 @@ gum_exec_ctx_obtain_block_for (GumExecCtx * ctx,
12331264
gum_arm64_writer_put_ldp_reg_reg_reg_offset (cw, ARM64_REG_X16, ARM64_REG_X17,
12341265
ARM64_REG_SP, 16 + GUM_RED_ZONE_SIZE, GUM_INDEX_POST_ADJUST);
12351266

1267+
ctx->calling_transformer = TRUE;
1268+
12361269
gum_stalker_transformer_transform_block (ctx->transformer, &iterator,
12371270
(GumStalkerWriter *) cw);
12381271

1272+
ctx->calling_transformer = FALSE;
1273+
12391274
if (gc.continuation_real_address != NULL)
12401275
{
12411276
GumBranchTarget continue_target = { 0, };

gum/backend-x86/gumstalker-x86.c

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2009-2018 Ole André Vadla Ravnås <oleavr@nowsecure.com>
2+
* Copyright (C) 2009-2019 Ole André Vadla Ravnås <oleavr@nowsecure.com>
33
* Copyright (C) 2010-2013 Karl Trygve Kalleberg <karltk@boblycat.org>
44
*
55
* Licence: wxWindows Library Licence, Version 3.1
@@ -137,9 +137,11 @@ struct _GumExecCtx
137137
GumX86Relocator relocator;
138138

139139
GumStalkerTransformer * transformer;
140+
gboolean calling_transformer;
140141
GQueue callout_entries;
141142
GumSpinlock callout_lock;
142143
GumEventSink * sink;
144+
gboolean sink_started;
143145
GumEventType sink_mask;
144146
void (* sink_process_impl) (GumEventSink * self, const GumEvent * ev);
145147
GumEvent tmp_event;
@@ -305,6 +307,7 @@ static GumExecCtx * gum_stalker_create_exec_ctx (GumStalker * self,
305307
static GumExecCtx * gum_stalker_get_exec_ctx (GumStalker * self);
306308
static void gum_stalker_invalidate_caches (GumStalker * self);
307309

310+
static void gum_exec_ctx_prepare_for_unfollow (GumExecCtx * ctx);
308311
static void gum_exec_ctx_dispose_callouts (GumExecCtx * ctx);
309312
static void gum_exec_ctx_free (GumExecCtx * ctx);
310313
static void gum_exec_ctx_unfollow (GumExecCtx * ctx, gpointer resume_at);
@@ -708,11 +711,21 @@ _gum_stalker_do_follow_me (GumStalker * self,
708711
ctx = gum_stalker_create_exec_ctx (self, gum_process_get_current_thread_id (),
709712
transformer, sink);
710713
gum_tls_key_set_value (self->exec_ctx, ctx);
714+
711715
ctx->current_block = gum_exec_ctx_obtain_block_for (ctx, *ret_addr_ptr,
712716
&code_address);
713-
*ret_addr_ptr = code_address;
717+
718+
if (ctx->state == GUM_EXEC_CTX_UNFOLLOW_PENDING)
719+
{
720+
gum_exec_ctx_prepare_for_unfollow (ctx);
721+
gum_exec_ctx_unfollow (ctx, *ret_addr_ptr);
722+
return;
723+
}
714724

715725
gum_event_sink_start (sink);
726+
ctx->sink_started = TRUE;
727+
728+
*ret_addr_ptr = code_address;
716729
}
717730

718731
void
@@ -721,11 +734,16 @@ gum_stalker_unfollow_me (GumStalker * self)
721734
GumExecCtx * ctx;
722735

723736
ctx = gum_stalker_get_exec_ctx (self);
724-
g_assert (ctx != NULL);
737+
if (ctx == NULL)
738+
return;
725739

726-
gum_exec_ctx_dispose_callouts (ctx);
740+
if (ctx->calling_transformer)
741+
{
742+
ctx->state = GUM_EXEC_CTX_UNFOLLOW_PENDING;
743+
return;
744+
}
727745

728-
gum_event_sink_stop (ctx->sink);
746+
gum_exec_ctx_prepare_for_unfollow (ctx);
729747

730748
if (ctx->current_block != NULL &&
731749
ctx->current_block->has_call_to_excluded_range)
@@ -791,9 +809,7 @@ gum_stalker_unfollow (GumStalker * self,
791809
GumExecCtx * ctx = (GumExecCtx *) cur->data;
792810
if (ctx->thread_id == thread_id && ctx->state == GUM_EXEC_CTX_ACTIVE)
793811
{
794-
gum_exec_ctx_dispose_callouts (ctx);
795-
796-
gum_event_sink_stop (ctx->sink);
812+
gum_exec_ctx_prepare_for_unfollow (ctx);
797813

798814
if (gum_exec_ctx_has_executed (ctx))
799815
{
@@ -1042,6 +1058,7 @@ gum_stalker_create_exec_ctx (GumStalker * self,
10421058
ctx->transformer = g_object_ref (transformer);
10431059
else
10441060
ctx->transformer = gum_stalker_transformer_make_default ();
1061+
ctx->calling_transformer = FALSE;
10451062
g_queue_init (&ctx->callout_entries);
10461063
gum_spinlock_init (&ctx->callout_lock);
10471064
ctx->sink = (GumEventSink *) g_object_ref (sink);
@@ -1082,6 +1099,19 @@ gum_stalker_invalidate_caches (GumStalker * self)
10821099
GUM_STALKER_UNLOCK (self);
10831100
}
10841101

1102+
static void
1103+
gum_exec_ctx_prepare_for_unfollow (GumExecCtx * ctx)
1104+
{
1105+
gum_exec_ctx_dispose_callouts (ctx);
1106+
1107+
if (ctx->sink_started)
1108+
{
1109+
gum_event_sink_stop (ctx->sink);
1110+
1111+
ctx->sink_started = FALSE;
1112+
}
1113+
}
1114+
10851115
static void
10861116
gum_exec_ctx_dispose_callouts (GumExecCtx * ctx)
10871117
{
@@ -1236,6 +1266,12 @@ gum_exec_ctx_replace_current_block_with (GumExecCtx * ctx,
12361266
{
12371267
ctx->current_block = gum_exec_ctx_obtain_block_for (ctx, start_address,
12381268
&ctx->resume_at);
1269+
if (ctx->state == GUM_EXEC_CTX_UNFOLLOW_PENDING)
1270+
{
1271+
gum_exec_ctx_prepare_for_unfollow (ctx);
1272+
1273+
gum_exec_ctx_unfollow (ctx, start_address);
1274+
}
12391275
}
12401276

12411277
return ctx->resume_at;
@@ -1382,9 +1418,13 @@ gum_exec_ctx_obtain_block_for (GumExecCtx * ctx,
13821418
iterator.instruction.end = NULL;
13831419
iterator.requirements = GUM_REQUIRE_NOTHING;
13841420

1421+
ctx->calling_transformer = TRUE;
1422+
13851423
gum_stalker_transformer_transform_block (ctx->transformer, &iterator,
13861424
(GumStalkerWriter *) cw);
13871425

1426+
ctx->calling_transformer = FALSE;
1427+
13881428
if (gc.continuation_real_address != NULL)
13891429
{
13901430
GumBranchTarget continue_target = { 0, };

tests/core/arch-arm64/stalker-arm64-fixture.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ silence_warnings (void)
183183

184184
typedef struct _StalkerVictimContext StalkerVictimContext;
185185
typedef guint StalkerVictimState;
186+
typedef struct _UnfollowTransformContext UnfollowTransformContext;
186187

187188
struct _StalkerVictimContext
188189
{
@@ -202,3 +203,11 @@ enum _StalkerVictimState
202203
STALKER_VICTIM_READY_FOR_SHUTDOWN,
203204
STALKER_VICTIM_IS_SHUTDOWN
204205
};
206+
207+
struct _UnfollowTransformContext
208+
{
209+
GumStalker * stalker;
210+
guint num_blocks_transformed;
211+
guint target_block;
212+
gint max_instructions;
213+
};

0 commit comments

Comments
 (0)