diff --git a/gum/backend-x86/guminterceptor-x86.c b/gum/backend-x86/guminterceptor-x86.c index a861510d8e..7fe60665d8 100644 --- a/gum/backend-x86/guminterceptor-x86.c +++ b/gum/backend-x86/guminterceptor-x86.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2008-2022 Ole André Vadla Ravnås * Copyright (C) 2008 Christian Berentsen + * Copyright (C) 2024 Yannis Juglaret * * Licence: wxWindows Library Licence, Version 3.1 */ @@ -46,6 +47,7 @@ struct _GumInterceptorBackend struct _GumX86FunctionContextData { guint redirect_code_size; + gpointer push_to_shadow_stack; }; G_STATIC_ASSERT (sizeof (GumX86FunctionContextData) @@ -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, @@ -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)) @@ -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); @@ -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); @@ -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 @@ -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 @@ -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] */ @@ -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))); + } } diff --git a/gum/guminterceptor-priv.h b/gum/guminterceptor-priv.h index 89a06c31f5..962e91082b 100644 --- a/gum/guminterceptor-priv.h +++ b/gum/guminterceptor-priv.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2008-2022 Ole André Vadla Ravnås * Copyright (C) 2008 Christian Berentsen + * Copyright (C) 2024 Yannis Juglaret * * Licence: wxWindows Library Licence, Version 3.1 */ @@ -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 ( diff --git a/gum/guminterceptor.c b/gum/guminterceptor.c index b2b3d54895..94b1fed5c3 100644 --- a/gum/guminterceptor.c +++ b/gum/guminterceptor.c @@ -2,6 +2,7 @@ * Copyright (C) 2008-2024 Ole André Vadla Ravnås * Copyright (C) 2008 Christian Berentsen * Copyright (C) 2024 Francesco Tamagni + * Copyright (C) 2024 Yannis Juglaret * * Licence: wxWindows Library Licence, Version 3.1 */ @@ -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, @@ -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); @@ -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