Skip to content

Commit

Permalink
x86: Improve Interceptor to support shadow stacks
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
yjugl authored and oleavr committed May 12, 2024
1 parent 65aa675 commit 31b7474
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 12 deletions.
68 changes: 63 additions & 5 deletions gum/backend-x86/guminterceptor-x86.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2008-2022 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2008 Christian Berentsen <[email protected]>
* Copyright (C) 2024 Yannis Juglaret <[email protected]>
*
* Licence: wxWindows Library Licence, Version 3.1
*/
Expand Down Expand Up @@ -46,6 +47,7 @@ struct _GumInterceptorBackend
struct _GumX86FunctionContextData
{
guint redirect_code_size;
gpointer push_to_shadow_stack;
};

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

static void gum_emit_prolog (GumX86Writer * cw,
gssize stack_displacement);
static void gum_emit_epilog (GumX86Writer * cw);
static void gum_emit_epilog (GumX86Writer * cw, GumPointCut point_cut);

GumInterceptorBackend *
_gum_interceptor_backend_create (GRecMutex * mutex,
Expand Down Expand Up @@ -171,6 +173,7 @@ _gum_interceptor_backend_create_trampoline (GumInterceptorBackend * self,
GumX86Relocator * rl = &self->relocator;
GumX86FunctionContextData * data = GUM_FCDATA (ctx);
GumAddress function_ctx_ptr;
gpointer after_push_to_shadow_stack;
guint reloc_bytes;

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

if ((cw->cpu_features & GUM_CPU_CET_SS) != 0)
{
/*
* Jumping to push_to_shadow_stack will push the on_leave_trampoline
* address onto the shadow stack, thereby making it a legit address to
* return to. Then it will jump back through XAX.
*/

after_push_to_shadow_stack = gum_x86_writer_cur (cw);

gum_x86_writer_put_lea_reg_reg_offset (cw, GUM_X86_XSP,
GUM_X86_XSP, (gssize) sizeof (gpointer));

gum_x86_writer_put_jmp_reg (cw, GUM_X86_XAX);

data->push_to_shadow_stack = gum_x86_writer_cur (cw);

gum_x86_writer_put_call_address (cw,
GUM_ADDRESS (after_push_to_shadow_stack));
}

ctx->on_leave_trampoline = gum_x86_writer_cur (cw);

gum_x86_writer_put_push_near_ptr (cw, function_ctx_ptr);
Expand Down Expand Up @@ -321,6 +345,8 @@ static void
gum_emit_enter_thunk (GumX86Writer * cw)
{
const gssize return_address_stack_displacement = 0;
const gchar * prepare_trap_on_leave = "prepare_trap_on_leave";
gpointer epilog;

gum_emit_prolog (cw, return_address_stack_displacement);

Expand All @@ -338,7 +364,26 @@ gum_emit_enter_thunk (GumX86Writer * cw)
GUM_ARG_REGISTER, GUM_X86_XDX,
GUM_ARG_REGISTER, GUM_X86_XCX);

gum_emit_epilog (cw);
if ((cw->cpu_features & GUM_CPU_CET_SS) != 0)
{
gum_x86_writer_put_test_reg_reg (cw, GUM_X86_EAX, GUM_X86_EAX);
gum_x86_writer_put_jcc_short_label (cw, X86_INS_JNE, prepare_trap_on_leave,
GUM_NO_HINT);

epilog = gum_x86_writer_cur (cw);
}

gum_emit_epilog (cw, FALSE);

if ((cw->cpu_features & GUM_CPU_CET_SS) != 0)
{
gum_x86_writer_put_label (cw, prepare_trap_on_leave);

gum_x86_writer_put_mov_reg_address (cw, GUM_X86_XAX, GUM_ADDRESS (epilog));
gum_x86_writer_put_jmp_reg_offset_ptr (cw, GUM_X86_XBX,
G_STRUCT_OFFSET (GumFunctionContext, backend_data) +
G_STRUCT_OFFSET (GumX86FunctionContextData, push_to_shadow_stack));
}
}

static void
Expand All @@ -359,7 +404,7 @@ gum_emit_leave_thunk (GumX86Writer * cw)
GUM_ARG_REGISTER, GUM_X86_XSI,
GUM_ARG_REGISTER, GUM_X86_XDX);

gum_emit_epilog (cw);
gum_emit_epilog (cw, TRUE);
}

static void
Expand Down Expand Up @@ -401,7 +446,8 @@ gum_emit_prolog (GumX86Writer * cw,
}

static void
gum_emit_epilog (GumX86Writer * cw)
gum_emit_epilog (GumX86Writer * cw,
GumPointCut point_cut)
{
guint8 fxrstor[] = {
0x0f, 0xae, 0x0c, 0x24 /* fxrstor [esp] */
Expand All @@ -415,5 +461,17 @@ gum_emit_epilog (GumX86Writer * cw)
GumCpuContext.xip */
gum_x86_writer_put_popax (cw);
gum_x86_writer_put_popfx (cw);
gum_x86_writer_put_ret (cw);

if (point_cut == GUM_POINT_LEAVE)
{
gum_x86_writer_put_ret (cw);
}
else
{
/* Emulate a ret without affecting the shadow stack. */
gum_x86_writer_put_lea_reg_reg_offset (cw, GUM_X86_XSP,
GUM_X86_XSP, sizeof (gpointer));
gum_x86_writer_put_jmp_reg_offset_ptr (cw, GUM_X86_XSP,
-((gssize) sizeof (gpointer)));
}
}
3 changes: 2 additions & 1 deletion gum/guminterceptor-priv.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2008-2022 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2008 Christian Berentsen <[email protected]>
* Copyright (C) 2024 Yannis Juglaret <[email protected]>
*
* Licence: wxWindows Library Licence, Version 3.1
*/
Expand Down Expand Up @@ -68,7 +69,7 @@ struct _GumFunctionContext
G_GNUC_INTERNAL void _gum_interceptor_init (void);
G_GNUC_INTERNAL void _gum_interceptor_deinit (void);

G_GNUC_INTERNAL void _gum_function_context_begin_invocation (
G_GNUC_INTERNAL gboolean _gum_function_context_begin_invocation (
GumFunctionContext * function_ctx, GumCpuContext * cpu_context,
gpointer * caller_ret_addr, gpointer * next_hop);
G_GNUC_INTERNAL void _gum_function_context_end_invocation (
Expand Down
11 changes: 5 additions & 6 deletions gum/guminterceptor.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Copyright (C) 2008-2024 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2008 Christian Berentsen <[email protected]>
* Copyright (C) 2024 Francesco Tamagni <[email protected]>
* Copyright (C) 2024 Yannis Juglaret <[email protected]>
*
* Licence: wxWindows Library Licence, Version 3.1
*/
Expand Down Expand Up @@ -1513,7 +1514,7 @@ gum_function_context_find_taken_listener_slot (
return NULL;
}

void
gboolean
_gum_function_context_begin_invocation (GumFunctionContext * function_ctx,
GumCpuContext * cpu_context,
gpointer * caller_ret_addr,
Expand All @@ -1526,7 +1527,7 @@ _gum_function_context_begin_invocation (GumFunctionContext * function_ctx,
GumInvocationContext * invocation_ctx = NULL;
gint system_error;
gboolean invoke_listeners = TRUE;
gboolean will_trap_on_leave;
gboolean will_trap_on_leave = FALSE;

g_atomic_int_inc (&function_ctx->trampoline_usage_counter);

Expand Down Expand Up @@ -1663,15 +1664,13 @@ _gum_function_context_begin_invocation (GumFunctionContext * function_ctx,
*next_hop = function_ctx->on_invoke_trampoline;
}

bypass:
if (!will_trap_on_leave)
{
g_atomic_int_dec_and_test (&function_ctx->trampoline_usage_counter);
}

return;

bypass:
g_atomic_int_dec_and_test (&function_ctx->trampoline_usage_counter);
return will_trap_on_leave;
}

void
Expand Down

0 comments on commit 31b7474

Please sign in to comment.