Skip to content

Commit 31b7474

Browse files
yjugloleavr
authored andcommitted
x86: Improve Interceptor to support shadow stacks
This patch makes Interceptor code compatible with Intel CET shadow stacks. - Detect CPUs that have support for CET shadow stacks. - Make the on_leave_trampoline a legit address to return to from a CET-enabled intercepted function's point of view. - Ensure a proper CALL/RET discipline. On Windows, this means that it will now be possible to intercept functions that live in CETCOMPAT modules when the user shadow stack mitigation is active (though not in strict mode).
1 parent 65aa675 commit 31b7474

File tree

3 files changed

+70
-12
lines changed

3 files changed

+70
-12
lines changed

gum/backend-x86/guminterceptor-x86.c

+63-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Copyright (C) 2008-2022 Ole André Vadla Ravnås <[email protected]>
33
* Copyright (C) 2008 Christian Berentsen <[email protected]>
4+
* Copyright (C) 2024 Yannis Juglaret <[email protected]>
45
*
56
* Licence: wxWindows Library Licence, Version 3.1
67
*/
@@ -46,6 +47,7 @@ struct _GumInterceptorBackend
4647
struct _GumX86FunctionContextData
4748
{
4849
guint redirect_code_size;
50+
gpointer push_to_shadow_stack;
4951
};
5052

5153
G_STATIC_ASSERT (sizeof (GumX86FunctionContextData)
@@ -61,7 +63,7 @@ static void gum_emit_leave_thunk (GumX86Writer * cw);
6163

6264
static void gum_emit_prolog (GumX86Writer * cw,
6365
gssize stack_displacement);
64-
static void gum_emit_epilog (GumX86Writer * cw);
66+
static void gum_emit_epilog (GumX86Writer * cw, GumPointCut point_cut);
6567

6668
GumInterceptorBackend *
6769
_gum_interceptor_backend_create (GRecMutex * mutex,
@@ -171,6 +173,7 @@ _gum_interceptor_backend_create_trampoline (GumInterceptorBackend * self,
171173
GumX86Relocator * rl = &self->relocator;
172174
GumX86FunctionContextData * data = GUM_FCDATA (ctx);
173175
GumAddress function_ctx_ptr;
176+
gpointer after_push_to_shadow_stack;
174177
guint reloc_bytes;
175178

176179
if (!gum_interceptor_backend_prepare_trampoline (self, ctx))
@@ -189,6 +192,27 @@ _gum_interceptor_backend_create_trampoline (GumInterceptorBackend * self,
189192
gum_x86_writer_put_push_near_ptr (cw, function_ctx_ptr);
190193
gum_x86_writer_put_jmp_address (cw, GUM_ADDRESS (self->enter_thunk->data));
191194

195+
if ((cw->cpu_features & GUM_CPU_CET_SS) != 0)
196+
{
197+
/*
198+
* Jumping to push_to_shadow_stack will push the on_leave_trampoline
199+
* address onto the shadow stack, thereby making it a legit address to
200+
* return to. Then it will jump back through XAX.
201+
*/
202+
203+
after_push_to_shadow_stack = gum_x86_writer_cur (cw);
204+
205+
gum_x86_writer_put_lea_reg_reg_offset (cw, GUM_X86_XSP,
206+
GUM_X86_XSP, (gssize) sizeof (gpointer));
207+
208+
gum_x86_writer_put_jmp_reg (cw, GUM_X86_XAX);
209+
210+
data->push_to_shadow_stack = gum_x86_writer_cur (cw);
211+
212+
gum_x86_writer_put_call_address (cw,
213+
GUM_ADDRESS (after_push_to_shadow_stack));
214+
}
215+
192216
ctx->on_leave_trampoline = gum_x86_writer_cur (cw);
193217

194218
gum_x86_writer_put_push_near_ptr (cw, function_ctx_ptr);
@@ -321,6 +345,8 @@ static void
321345
gum_emit_enter_thunk (GumX86Writer * cw)
322346
{
323347
const gssize return_address_stack_displacement = 0;
348+
const gchar * prepare_trap_on_leave = "prepare_trap_on_leave";
349+
gpointer epilog;
324350

325351
gum_emit_prolog (cw, return_address_stack_displacement);
326352

@@ -338,7 +364,26 @@ gum_emit_enter_thunk (GumX86Writer * cw)
338364
GUM_ARG_REGISTER, GUM_X86_XDX,
339365
GUM_ARG_REGISTER, GUM_X86_XCX);
340366

341-
gum_emit_epilog (cw);
367+
if ((cw->cpu_features & GUM_CPU_CET_SS) != 0)
368+
{
369+
gum_x86_writer_put_test_reg_reg (cw, GUM_X86_EAX, GUM_X86_EAX);
370+
gum_x86_writer_put_jcc_short_label (cw, X86_INS_JNE, prepare_trap_on_leave,
371+
GUM_NO_HINT);
372+
373+
epilog = gum_x86_writer_cur (cw);
374+
}
375+
376+
gum_emit_epilog (cw, FALSE);
377+
378+
if ((cw->cpu_features & GUM_CPU_CET_SS) != 0)
379+
{
380+
gum_x86_writer_put_label (cw, prepare_trap_on_leave);
381+
382+
gum_x86_writer_put_mov_reg_address (cw, GUM_X86_XAX, GUM_ADDRESS (epilog));
383+
gum_x86_writer_put_jmp_reg_offset_ptr (cw, GUM_X86_XBX,
384+
G_STRUCT_OFFSET (GumFunctionContext, backend_data) +
385+
G_STRUCT_OFFSET (GumX86FunctionContextData, push_to_shadow_stack));
386+
}
342387
}
343388

344389
static void
@@ -359,7 +404,7 @@ gum_emit_leave_thunk (GumX86Writer * cw)
359404
GUM_ARG_REGISTER, GUM_X86_XSI,
360405
GUM_ARG_REGISTER, GUM_X86_XDX);
361406

362-
gum_emit_epilog (cw);
407+
gum_emit_epilog (cw, TRUE);
363408
}
364409

365410
static void
@@ -401,7 +446,8 @@ gum_emit_prolog (GumX86Writer * cw,
401446
}
402447

403448
static void
404-
gum_emit_epilog (GumX86Writer * cw)
449+
gum_emit_epilog (GumX86Writer * cw,
450+
GumPointCut point_cut)
405451
{
406452
guint8 fxrstor[] = {
407453
0x0f, 0xae, 0x0c, 0x24 /* fxrstor [esp] */
@@ -415,5 +461,17 @@ gum_emit_epilog (GumX86Writer * cw)
415461
GumCpuContext.xip */
416462
gum_x86_writer_put_popax (cw);
417463
gum_x86_writer_put_popfx (cw);
418-
gum_x86_writer_put_ret (cw);
464+
465+
if (point_cut == GUM_POINT_LEAVE)
466+
{
467+
gum_x86_writer_put_ret (cw);
468+
}
469+
else
470+
{
471+
/* Emulate a ret without affecting the shadow stack. */
472+
gum_x86_writer_put_lea_reg_reg_offset (cw, GUM_X86_XSP,
473+
GUM_X86_XSP, sizeof (gpointer));
474+
gum_x86_writer_put_jmp_reg_offset_ptr (cw, GUM_X86_XSP,
475+
-((gssize) sizeof (gpointer)));
476+
}
419477
}

gum/guminterceptor-priv.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Copyright (C) 2008-2022 Ole André Vadla Ravnås <[email protected]>
33
* Copyright (C) 2008 Christian Berentsen <[email protected]>
4+
* Copyright (C) 2024 Yannis Juglaret <[email protected]>
45
*
56
* Licence: wxWindows Library Licence, Version 3.1
67
*/
@@ -68,7 +69,7 @@ struct _GumFunctionContext
6869
G_GNUC_INTERNAL void _gum_interceptor_init (void);
6970
G_GNUC_INTERNAL void _gum_interceptor_deinit (void);
7071

71-
G_GNUC_INTERNAL void _gum_function_context_begin_invocation (
72+
G_GNUC_INTERNAL gboolean _gum_function_context_begin_invocation (
7273
GumFunctionContext * function_ctx, GumCpuContext * cpu_context,
7374
gpointer * caller_ret_addr, gpointer * next_hop);
7475
G_GNUC_INTERNAL void _gum_function_context_end_invocation (

gum/guminterceptor.c

+5-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Copyright (C) 2008-2024 Ole André Vadla Ravnås <[email protected]>
33
* Copyright (C) 2008 Christian Berentsen <[email protected]>
44
* Copyright (C) 2024 Francesco Tamagni <[email protected]>
5+
* Copyright (C) 2024 Yannis Juglaret <[email protected]>
56
*
67
* Licence: wxWindows Library Licence, Version 3.1
78
*/
@@ -1513,7 +1514,7 @@ gum_function_context_find_taken_listener_slot (
15131514
return NULL;
15141515
}
15151516

1516-
void
1517+
gboolean
15171518
_gum_function_context_begin_invocation (GumFunctionContext * function_ctx,
15181519
GumCpuContext * cpu_context,
15191520
gpointer * caller_ret_addr,
@@ -1526,7 +1527,7 @@ _gum_function_context_begin_invocation (GumFunctionContext * function_ctx,
15261527
GumInvocationContext * invocation_ctx = NULL;
15271528
gint system_error;
15281529
gboolean invoke_listeners = TRUE;
1529-
gboolean will_trap_on_leave;
1530+
gboolean will_trap_on_leave = FALSE;
15301531

15311532
g_atomic_int_inc (&function_ctx->trampoline_usage_counter);
15321533

@@ -1663,15 +1664,13 @@ _gum_function_context_begin_invocation (GumFunctionContext * function_ctx,
16631664
*next_hop = function_ctx->on_invoke_trampoline;
16641665
}
16651666

1667+
bypass:
16661668
if (!will_trap_on_leave)
16671669
{
16681670
g_atomic_int_dec_and_test (&function_ctx->trampoline_usage_counter);
16691671
}
16701672

1671-
return;
1672-
1673-
bypass:
1674-
g_atomic_int_dec_and_test (&function_ctx->trampoline_usage_counter);
1673+
return will_trap_on_leave;
16751674
}
16761675

16771676
void

0 commit comments

Comments
 (0)