From 3327d733730c2e740c3ec871be4756537d073cfb Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Wed, 19 Jun 2024 11:32:44 -0700 Subject: [PATCH] notes on memory-execution-exception spoof implementation situation, and toggles to switch to old behavior, for later investigation --- src/ctrl/ctrl_core.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/ctrl/ctrl_core.c b/src/ctrl/ctrl_core.c index d0cfcdc4..f370f687 100644 --- a/src/ctrl/ctrl_core.c +++ b/src/ctrl/ctrl_core.c @@ -1,6 +1,46 @@ // Copyright (c) 2024 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) +//////////////////////////////// +//~ rjf: Bug Hunt Behavior Enabling (2024/06/19) +// +// NOTE(rjf): For one reason or another, our approach of "spoofs" - +// overwriting a return address in a thread's stack to get a debug +// event when the thread returns from a particular frame - seemed +// to cause issues in particular cases. We previously would overwrite +// a return address on the stack to 911, and we'd get an invalid memory +// execution debug event whenever the thread moved there. Then, we'd +// simply move the thread back to where it was before, and continue. +// +// It seemed like, possibly, some code was seeing this event and/or +// other adjacent effects (e.g. a thread attempting to execute at an +// invalid address), and was freaking out, but it is very difficult +// to know exactly what is going on, since from the debugger's +// perspective, everything was fine, but upon resuming (after this +// memory-execution exception was caught), the process would +// immediately exit. So some external code must be doing something +// that we have not accounted for. +// +// To address this unknown, I've moved the "spoofs" back to being +// implemented by allocating an extra code page in target processes, +// and writing over them with int3s, and then instead of writing 911 +// into the stack, just writing the base address of that page. Hitting +// an unexpected int3 is a much more expected debugger pattern, and +// so my theory was that this would be less suspect to code which +// could be doing e.g. validation checks over code. The spoof still +// works the same way beyond that; we simply check that the int3 is +// within the "spoof page", and if it is, we return the thread back +// to the correct address. +// +// But, given my uncertainty about the exact cause/effect situation, +// I have left this comment, and a #define below to toggle the old +// behavior, so that we can investigate in the future. +// +// @nick - this is what you'd want to change from `0` to `1` if you +// want to take a look at this. + +#define CTRL_ENABLE_MEMORY_EXECUTE_EXCEPTION_SPOOFS 0 + //////////////////////////////// //~ rjf: Generated Code @@ -3621,11 +3661,19 @@ ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg, void *regs_block = push_array(scratch.arena, U8, regs_block_size_from_architecture(arch)); dmn_thread_read_reg_block(spoof->thread, regs_block); U64 spoof_thread_rip = regs_rip_from_arch_block(arch, regs_block); +#if CTRL_ENABLE_MEMORY_EXECUTE_EXCEPTION_SPOOFS + if(spoof_thread_rip == spoof->new_ip_value) + { + regs_arch_block_write_rip(arch, regs_block, spoof_old_ip_value); + ctrl_thread_write_reg_block(CTRL_MachineID_Local, spoof->thread, regs_block); + } +#else if(spoof_thread_rip == spoof->new_ip_value+1) { regs_arch_block_write_rip(arch, regs_block, spoof_old_ip_value); ctrl_thread_write_reg_block(CTRL_MachineID_Local, spoof->thread, regs_block); } +#endif } //- rjf: push ctrl events associated with this demon event @@ -4015,7 +4063,11 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) DMN_Handle target_thread = msg->entity; DMN_Handle target_process = msg->parent; CTRL_Entity *target_process_entity = ctrl_entity_from_machine_id_handle(ctrl_state->ctrl_thread_entity_store, msg->machine_id, target_process); +#if CTRL_ENABLE_MEMORY_EXECUTE_EXCEPTION_SPOOFS + U64 spoof_ip_vaddr = 911; +#else U64 spoof_ip_vaddr = target_process_entity->vaddr_range.min; +#endif log_infof("ctrl_thread__run:\n{\n"); ////////////////////////////// @@ -4562,6 +4614,29 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) // //{ +#if CTRL_ENABLE_MEMORY_EXECUTE_EXCEPTION_SPOOFS + ////////////////////////// + //- rjf: handle if hitting a spoof + // + B32 exception_stop = 0; + B32 hit_spoof = 0; + if(!hard_stop && use_stepping_logic && event->kind == DMN_EventKind_Exception) + { + if(spoof_mode && + dmn_handle_match(target_process, event->process) && + dmn_handle_match(target_thread, event->thread) && + spoof.new_ip_value == event->instruction_pointer) + { + hit_spoof = 1; + log_infof("hit_spoof\n"); + } + else + { + exception_stop = 1; + use_stepping_logic = 0; + } + } +#else ////////////////////////// //- rjf: handle if hitting an exception // @@ -4587,6 +4662,7 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) log_infof("hit_spoof\n"); } } +#endif //- rjf: handle spoof hit if(hit_spoof)