This commit is contained in:
Nikita Smith
2025-12-09 09:56:43 -08:00
parent c221286ab8
commit 962f0e30ae
5 changed files with 271 additions and 124 deletions
+17 -12
View File
@@ -1323,7 +1323,7 @@ ctrl_entity_store_apply_events(CTRL_EntityCtxRWStore *store, CTRL_EventList *lis
} }
} }
thread->stack_base = event->stack_base; thread->stack_base = event->stack_base;
ctrl_rip_from_thread(&store->ctx, event->entity); //ctrl_rip_from_thread(&store->ctx, event->entity);
}break; }break;
case CTRL_EventKind_EndThread: case CTRL_EventKind_EndThread:
{ {
@@ -4451,6 +4451,20 @@ ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg,
dmn_process_write(spoof->process, r1u64(spoof->vaddr, spoof->vaddr+size_of_spoof), &spoof->new_ip_value); dmn_process_write(spoof->process, r1u64(spoof->vaddr, spoof->vaddr+size_of_spoof), &spoof->new_ip_value);
} }
// copy spoof info
if(do_spoof)
{
run_ctrls->spoof.process = spoof->process;
run_ctrls->spoof.thread = spoof->thread;
run_ctrls->spoof.vaddr = spoof->vaddr;
run_ctrls->spoof.old_ip = spoof_old_ip_value;
run_ctrls->spoof.size = size_of_spoof;
}
else
{
MemoryZeroStruct(&run_ctrls->spoof);
}
// rjf: run for new events // rjf: run for new events
ProfScope("run for new events") ProfScope("run for new events")
{ {
@@ -4474,6 +4488,7 @@ ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg,
} }
} }
} }
DMN_EventList events = dmn_ctrl_run(scratch.arena, ctrl_ctx, run_ctrls); DMN_EventList events = dmn_ctrl_run(scratch.arena, ctrl_ctx, run_ctrls);
ins_atomic_u64_inc_eval(&ctrl_state->mem_gen); ins_atomic_u64_inc_eval(&ctrl_state->mem_gen);
ins_atomic_u64_inc_eval(&ctrl_state->reg_gen); ins_atomic_u64_inc_eval(&ctrl_state->reg_gen);
@@ -5956,17 +5971,7 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg)
CTRL_Entity *process = ctrl_entity_from_handle(entity_ctx, ctrl_handle_make(CTRL_MachineID_Local, event->process)); CTRL_Entity *process = ctrl_entity_from_handle(entity_ctx, ctrl_handle_make(CTRL_MachineID_Local, event->process));
Arch arch = thread->arch; Arch arch = thread->arch;
U64 thread_rip_vaddr = dmn_rip_from_thread(event->thread); U64 thread_rip_vaddr = dmn_rip_from_thread(event->thread);
CTRL_Entity *module = &ctrl_entity_nil; CTRL_Entity *module = ctrl_module_from_process_vaddr(process, thread_rip_vaddr);
{
for(CTRL_Entity *m = process->first; m != &ctrl_entity_nil; m = m->next)
{
if(m->kind == CTRL_EntityKind_Module && contains_1u64(m->vaddr_range, thread_rip_vaddr))
{
module = m;
break;
}
}
}
////////////////////////// //////////////////////////
//- rjf: extract module-dependent info //- rjf: extract module-dependent info
+34
View File
@@ -218,3 +218,37 @@ dmn_get_trap_inst(void)
return str8_array_fixed(inst); return str8_array_fixed(inst);
} }
internal DMN_ActiveTrap *
dmn_set_trap(Arena *arena, DMN_Trap *trap)
{
DMN_ActiveTrap *result = 0;
String8 trap_inst = dmn_get_trap_inst();
U8 *swap_bytes = push_array(arena, U8, trap_inst.size);
if(dmn_process_read(trap->process, r1u64(trap->vaddr, trap->vaddr + trap_inst.size), swap_bytes) == trap_inst.size)
{
if(dmn_process_write(trap->process, r1u64(trap->vaddr, trap->vaddr + trap_inst.size), trap_inst.str) == trap_inst.size)
{
result = push_array(arena, DMN_ActiveTrap, 1);
result->trap = trap;
result->swap_bytes = str8(swap_bytes, trap_inst.size);
}
else
{
Assert(0 && "failed to write trap instruction");
}
}
else
{
Assert(0 && "failed to read original byte");
}
return result;
}
internal B32
dmn_remove_trap(DMN_ActiveTrap *active_trap)
{
B32 is_removed = dmn_process_write(active_trap->trap->process, r1u64(active_trap->trap->vaddr, active_trap->trap->vaddr + active_trap->swap_bytes.size), active_trap->swap_bytes.str);
Assert(is_removed);
return is_removed;
}
+14 -3
View File
@@ -165,12 +165,20 @@ struct DMN_RunCtrls
{ {
DMN_Handle priority_thread; DMN_Handle priority_thread;
DMN_Handle single_step_thread; DMN_Handle single_step_thread;
B8 ignore_previous_exception;
B8 run_entities_are_unfrozen;
B8 run_entities_are_processes;
DMN_Handle *run_entities; DMN_Handle *run_entities;
U64 run_entity_count; U64 run_entity_count;
DMN_TrapChunkList traps; DMN_TrapChunkList traps;
B8 ignore_previous_exception;
B8 run_entities_are_unfrozen;
B8 run_entities_are_processes;
struct
{
DMN_Handle process;
DMN_Handle thread;
U64 vaddr;
U64 old_ip;
U64 size;
} spoof;
}; };
//////////////////////////////// ////////////////////////////////
@@ -279,4 +287,7 @@ internal void dmn_process_iter_end(DMN_ProcessIter *iter);
//- arch trap //- arch trap
internal String8 dmn_get_trap_inst(void); internal String8 dmn_get_trap_inst(void);
internal DMN_ActiveTrap * dmn_set_trap(Arena *arena, DMN_Trap *trap);
internal B32 dmn_remove_trap(DMN_ActiveTrap *active_trap);
#endif // DEMON_CORE_H #endif // DEMON_CORE_H
+161 -72
View File
@@ -125,7 +125,7 @@ dmn_lnx_ptrace_seize(pid_t pid)
AssertAlways(dmn_lnx_state->tracer_tid == gettid()); AssertAlways(dmn_lnx_state->tracer_tid == gettid());
// TODO: PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE // TODO: PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE
return OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_TRACEEXEC | PTRACE_O_EXITKILL | PTRACE_O_TRACECLONE)); return OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_TRACEEXEC | PTRACE_O_EXITKILL | PTRACE_O_TRACEFORK | PTRACE_O_TRACECLONE));
} }
//////////////////////////////// ////////////////////////////////
@@ -925,6 +925,39 @@ dmn_lnx_process_from_pid(pid_t pid)
return process; return process;
} }
internal void
dmn_lnx_process_trap_probes(DMN_LNX_Entity *process)
{
Assert(process->kind == DMN_LNX_EntityKind_Process);
for EachIndex(i, DMN_LNX_ProbeType_Count)
{
if(process->probes[i] == 0) { continue; }
DMN_Trap *trap = push_array(process->arena, DMN_Trap, 1);
trap->process = dmn_lnx_handle_from_entity(process);
trap->vaddr = process->probes[i]->pc;
trap->id = i;
DMN_ActiveTrap *active_trap = dmn_set_trap(process->arena, trap);
SLLQueuePush(process->first_probe_trap, process->last_probe_trap, active_trap);
if(BUILD_DEBUG && process->arch == Arch_x64)
{
Assert(active_trap->swap_bytes.size == 1 && active_trap->swap_bytes.str[0] == 0x90);
}
}
}
internal void
dmn_lnx_process_untrap_probes(DMN_LNX_Entity *process)
{
Assert(process->kind == DMN_LNX_EntityKind_Process);
for EachNode(active_trap, DMN_ActiveTrap, process->first_probe_trap)
{
dmn_remove_trap(active_trap);
}
}
internal U64 internal U64
dmn_lnx_thread_read_ip(DMN_LNX_Entity *thread) dmn_lnx_thread_read_ip(DMN_LNX_Entity *thread)
{ {
@@ -1419,6 +1452,8 @@ dmn_lnx_set_single_step_flag(DMN_LNX_Entity *thread, B32 is_on)
return is_flag_set; return is_flag_set;
} }
////////////////////////////////
internal void internal void
dmn_lnx_handle_not_attached(Arena *arena, DMN_EventList *events) dmn_lnx_handle_not_attached(Arena *arena, DMN_EventList *events)
{ {
@@ -1427,8 +1462,6 @@ dmn_lnx_handle_not_attached(Arena *arena, DMN_EventList *events)
e->error_kind = DMN_ErrorKind_NotAttached; e->error_kind = DMN_ErrorKind_NotAttached;
} }
////////////////////////////////
internal DMN_LNX_Entity * internal DMN_LNX_Entity *
dmn_lnx_handle_create_thread(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *process, pid_t tid) dmn_lnx_handle_create_thread(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *process, pid_t tid)
{ {
@@ -1537,24 +1570,6 @@ dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, B32 debug_sub
} }
} }
//
// install DL probes
//
U64 probe_vaddrs[DMN_LNX_ProbeType_Count] = {0};
for EachIndex(i, DMN_LNX_ProbeType_Count)
{
if(known_probes[i] == 0) { continue; }
U8 og_byte = 0;
if(!dmn_lnx_read_struct(memory_fd, known_probes[i]->pc, &og_byte)) { Assert(0 && "failed to read original byte"); }
Assert(og_byte == 0x90);
U8 trap = 0xcc;
if(!dmn_lnx_write_struct(memory_fd, known_probes[i]->pc, &trap)) { Assert(0 && "failed to install probe"); }
probe_vaddrs[i] = known_probes[i]->pc;
}
// //
// init process // init process
// //
@@ -1574,17 +1589,15 @@ dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, B32 debug_sub
process->xcr0 = xcr0; process->xcr0 = xcr0;
process->xsave_size = Max(xsave_size, sizeof(X64_XSave)); process->xsave_size = Max(xsave_size, sizeof(X64_XSave));
process->xsave_layout = xsave_layout; process->xsave_layout = xsave_layout;
MemoryCopyTyped(&process->probe_vaddrs[0], &probe_vaddrs[0], DMN_LNX_ProbeType_Count);
hash_table_push_u64_raw(dmn_lnx_state->arena, dmn_lnx_state->pid_ht, pid, process); hash_table_push_u64_raw(dmn_lnx_state->arena, dmn_lnx_state->pid_ht, pid, process);
// push create process event // push create process event
{
DMN_Event *e = dmn_event_list_push(arena, events); DMN_Event *e = dmn_event_list_push(arena, events);
e->kind = DMN_EventKind_CreateProcess; e->kind = DMN_EventKind_CreateProcess;
e->process = dmn_lnx_handle_from_entity(process); e->process = dmn_lnx_handle_from_entity(process);
e->arch = process->arch; e->arch = process->arch;
e->code = pid; e->code = pid;
e->tls_model = DMN_TlsModel_Gnu; // TODO: use dynamic linker path to figure out correct enum here e->tls_model = DMN_TlsModel_Gnu; // TODO: use dynamic linker path to figure out correct enum here
}
// //
// init main thread // init main thread
@@ -1598,6 +1611,8 @@ dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, B32 debug_sub
DMN_LNX_Entity *module = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Module); DMN_LNX_Entity *module = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Module);
module->id = auxv.execfn; module->id = auxv.execfn;
module->base_vaddr = base_vaddr; module->base_vaddr = base_vaddr;
module->module_name_vaddr = auxv.execfn;
module->is_main = 1;
hash_table_push_u64_raw(process->arena, process->loaded_modules_ht, 0, module); hash_table_push_u64_raw(process->arena, process->loaded_modules_ht, 0, module);
hash_table_push_u64_raw(process->arena, process->loaded_modules_ht, base_vaddr, module); hash_table_push_u64_raw(process->arena, process->loaded_modules_ht, base_vaddr, module);
@@ -1707,28 +1722,20 @@ dmn_lnx_handle_exit_thread(Arena *arena, DMN_EventList *events, pid_t tid, U64 e
} }
} }
internal void internal DMN_LNX_Entity *
dmn_lnx_handle_load_module(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *process, U64 name_space_id, U64 new_link_map_vaddr) dmn_lnx_load_module(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *process, U64 name_space_id, U64 module_name_vaddr, U64 base_vaddr)
{ {
GNU_LinkMap64 map = {0}; DMN_LNX_Entity *module = 0;
for(U64 map_vaddr = new_link_map_vaddr; map_vaddr != 0; map_vaddr = map.next_vaddr)
{
// read out new link map item
if(!dmn_lnx_read_linkmap(process->fd, map_vaddr, process->dl_class, &map)) { goto exit; }
// was module with this base already registered?
DMN_LNX_Entity *module = hash_table_search_u64_raw(process->loaded_modules_ht, map.addr_vaddr);
if(module) { continue; }
// parse out module's ELF header // parse out module's ELF header
ELF_Hdr64 module_ehdr = {0}; ELF_Hdr64 module_ehdr = {0};
if(!dmn_lnx_read_ehdr(process->fd, map.addr_vaddr, &module_ehdr)) { goto exit; } if(!dmn_lnx_read_ehdr(process->fd, base_vaddr, &module_ehdr)) { goto exit; }
// gather info about module // gather info about module
U64 module_rebase = module_ehdr.e_type == ELF_Type_Dyn ? map.addr_vaddr : 0; U64 module_rebase = module_ehdr.e_type == ELF_Type_Dyn ? base_vaddr : 0;
U64 module_phdr_vaddr = module_rebase + module_ehdr.e_phoff; U64 module_phdr_vaddr = module_rebase + module_ehdr.e_phoff;
DMN_LNX_PhdrInfo module_phdr_info = dmn_lnx_phdr_info_from_memory(process->fd, module_ehdr.e_ident[ELF_Identifier_Class], module_rebase, module_phdr_vaddr, module_ehdr.e_phentsize, module_ehdr.e_phnum); DMN_LNX_PhdrInfo module_phdr_info = dmn_lnx_phdr_info_from_memory(process->fd, module_ehdr.e_ident[ELF_Identifier_Class], module_rebase, module_phdr_vaddr, module_ehdr.e_phentsize, module_ehdr.e_phnum);
String8 module_name = dmn_lnx_read_string(process->arena, process->fd, map.name_vaddr); String8 module_name = dmn_lnx_read_string(process->arena, process->fd, module_name_vaddr);
// read TLS index and TLS offset // read TLS index and TLS offset
U64 tls_index = max_U64; U64 tls_index = max_U64;
@@ -1737,39 +1744,58 @@ dmn_lnx_handle_load_module(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *
{ {
Rng1U64 tls_modid_range = r1u64(dmn_lnx_state->tls_modid_desc.offset, dmn_lnx_state->tls_modid_desc.offset + dmn_lnx_state->tls_modid_desc.bit_size / 8); Rng1U64 tls_modid_range = r1u64(dmn_lnx_state->tls_modid_desc.offset, dmn_lnx_state->tls_modid_desc.offset + dmn_lnx_state->tls_modid_desc.bit_size / 8);
Rng1U64 tls_offset_range = r1u64(dmn_lnx_state->tls_offset_desc.offset, dmn_lnx_state->tls_offset_desc.offset + dmn_lnx_state->tls_offset_desc.bit_size / 8); Rng1U64 tls_offset_range = r1u64(dmn_lnx_state->tls_offset_desc.offset, dmn_lnx_state->tls_offset_desc.offset + dmn_lnx_state->tls_offset_desc.bit_size / 8);
tls_modid_range = shift_1u64(tls_modid_range, map_vaddr); tls_modid_range = shift_1u64(tls_modid_range, base_vaddr);
tls_offset_range = shift_1u64(tls_offset_range, map_vaddr); tls_offset_range = shift_1u64(tls_offset_range, base_vaddr);
if(!dmn_lnx_read(process->fd, tls_modid_range, &tls_index)) { Assert(0 && "failed to read TLS index"); } if(!dmn_lnx_read(process->fd, tls_modid_range, &tls_index)) { Assert(0 && "failed to read TLS index"); }
if(!dmn_lnx_read(process->fd, tls_offset_range, &tls_offset)) { Assert(0 && "failed to read TLS offset"); } if(!dmn_lnx_read(process->fd, tls_offset_range, &tls_offset)) { Assert(0 && "failed to read TLS offset"); }
} }
// fill out module // fill out module
module = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Module); module = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Module);
module->id = map.name_vaddr; module->id = module_name_vaddr;
module->base_vaddr = map.addr_vaddr; module->base_vaddr = base_vaddr;
module->module_name_vaddr = module_name_vaddr;
if(str8_match(module_name, str8_lit("linux-vdso.so.1"), 0)) { goto exit; }
if(!str8_match(module_name, str8_lit("linux-vdso.so.1"), 0))
{
// push load event // push load event
DMN_Event *e = dmn_event_list_push(arena, events); DMN_Event *e = dmn_event_list_push(arena, events);
e->kind = DMN_EventKind_LoadModule; e->kind = DMN_EventKind_LoadModule;
e->process = dmn_lnx_handle_from_entity(process); e->process = dmn_lnx_handle_from_entity(process);
e->module = dmn_lnx_handle_from_entity(module); e->module = dmn_lnx_handle_from_entity(module);
e->arch = arch_from_elf_machine(module_ehdr.e_machine); e->arch = arch_from_elf_machine(module_ehdr.e_machine);
e->address = map.addr_vaddr; e->address = base_vaddr;
e->size = dim_1u64(module_phdr_info.range); e->size = dim_1u64(module_phdr_info.range);
e->string = module_name; e->string = module_name;
e->elf_phdr_vrange = r1u64(module_phdr_vaddr, module_phdr_vaddr + module_ehdr.e_phentsize * module_ehdr.e_phnum); e->elf_phdr_vrange = r1u64(module_phdr_vaddr, module_phdr_vaddr + module_ehdr.e_phentsize * module_ehdr.e_phnum);
e->elf_phdr_entsize = module_ehdr.e_phentsize; e->elf_phdr_entsize = module_ehdr.e_phentsize;
e->tls_index = tls_index; e->tls_index = tls_index;
e->tls_offset = tls_offset; e->tls_offset = tls_offset;
}
exit:;
return module;
}
internal void
dmn_lnx_handle_load_module(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *process, U64 name_space_id, U64 new_link_map_vaddr)
{
GNU_LinkMap64 map = {0};
for(U64 map_vaddr = new_link_map_vaddr; map_vaddr != 0; map_vaddr = map.next_vaddr)
{
// read out new link map item
if(!dmn_lnx_read_linkmap(process->fd, map_vaddr, process->dl_class, &map)) { break; }
// was module with this base already registered?
DMN_LNX_Entity *module = hash_table_search_u64_raw(process->loaded_modules_ht, map.addr_vaddr);
if(module == 0)
{
// load module
module = dmn_lnx_load_module(arena, events, process, name_space_id, map.name_vaddr, map.addr_vaddr);
// create mapping for base -> module // create mapping for base -> module
hash_table_push_u64_raw(process->arena, process->loaded_modules_ht, map.addr_vaddr, module); hash_table_push_u64_raw(process->arena, process->loaded_modules_ht, map.addr_vaddr, module);
} }
}
exit:;
} }
internal void internal void
@@ -1835,11 +1861,19 @@ dmn_lnx_handle_breakpoint(Arena *arena, DMN_EventList *events, HashTable *active
// is this a probe trap? // is this a probe trap?
DMN_LNX_ProbeType probe_type = DMN_LNX_ProbeType_Null; DMN_LNX_ProbeType probe_type = DMN_LNX_ProbeType_Null;
for EachIndex(i, ArrayCount(process->probe_vaddrs)) for EachNode(active_trap, DMN_ActiveTrap, process->first_probe_trap)
{ {
if(process->probe_vaddrs[i] == ip-1) if(active_trap->trap->vaddr == ip-1)
{
probe_type = active_trap->trap->id;
break;
}
}
for(U64 i = 0; i < DMN_LNX_ProbeType_Count; ++i)
{
if(process->probes[i] != 0 && process->probes[i]->pc == ip-1)
{ {
probe_type = i;
break; break;
} }
} }
@@ -2029,6 +2063,7 @@ dmn_lnx_handle_attach(Arena *arena, DMN_EventList *events, pid_t pid)
{ {
// create process // create process
process = dmn_lnx_handle_create_process(arena, events, 1, 1, pid); process = dmn_lnx_handle_create_process(arena, events, 1, 1, pid);
dmn_lnx_process_trap_probes(process);
// extract threads from /proc/pid/task // extract threads from /proc/pid/task
for(;;) for(;;)
@@ -2313,26 +2348,10 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
DMN_LNX_Entity *process = dmn_lnx_entity_from_handle(trap->process); DMN_LNX_Entity *process = dmn_lnx_entity_from_handle(trap->process);
if(process == dmn_lnx_nil_entity) { continue; } if(process == dmn_lnx_nil_entity) { continue; }
// read original instruction bytes // trap instruction
String8 trap_inst = dmn_get_trap_inst(); DMN_ActiveTrap *active_trap = dmn_set_trap(scratch.arena, trap);
U8 *swap_bytes = push_array(scratch.arena, U8, trap_inst.size);
if(dmn_process_read(trap->process, r1u64(trap->vaddr, trap->vaddr+trap_inst.size), swap_bytes) != trap_inst.size)
{
Assert(0 && "failed to read original byte");
continue;
}
// replace with trap instruction bytes
if(dmn_process_write(trap->process, r1u64(trap->vaddr, trap->vaddr+trap_inst.size), trap_inst.str) != trap_inst.size)
{
Assert(0 && "failed to write trap");
continue;
}
// add trap to the active list // add trap to the active list
DMN_ActiveTrap *active_trap = push_array(scratch.arena, DMN_ActiveTrap, 1);
active_trap->trap = trap;
active_trap->swap_bytes = str8(swap_bytes, trap_inst.size);
SLLQueuePush(active_trap_first, active_trap_last, active_trap); SLLQueuePush(active_trap_first, active_trap_last, active_trap);
// add (address -> trap) // add (address -> trap)
@@ -2464,6 +2483,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
// wait for signals from the running threads // wait for signals from the running threads
{ {
printf("----------------------------------------------\n");
B32 is_halt_done = 0; B32 is_halt_done = 0;
for(U64 stopped_threads = 0;;) for(U64 stopped_threads = 0;;)
{ {
@@ -2495,6 +2515,8 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
int wstopsig = WSTOPSIG(status); int wstopsig = WSTOPSIG(status);
int event_code = (status >> 16); int event_code = (status >> 16);
printf("wstopsig %d, event_code %d\n", wstopsig, event_code);
// intercept signals meant for the process launch sequence // intercept signals meant for the process launch sequence
{ {
// pid -> pending process launch // pid -> pending process launch
@@ -2515,7 +2537,8 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
dmn_lnx_process_launch_list_push_node(&dmn_lnx_state->free_pids, pending_proc); dmn_lnx_process_launch_list_push_node(&dmn_lnx_state->free_pids, pending_proc);
// push create process events // push create process events
dmn_lnx_handle_create_process(arena, &events, pending_proc->debug_subprocesses, 0, wait_id); DMN_LNX_Entity *process = dmn_lnx_handle_create_process(arena, &events, pending_proc->debug_subprocesses, 0, wait_id);
dmn_lnx_process_trap_probes(process);
// override event code so the main code path does not create another process // override event code so the main code path does not create another process
event_code = PTRACE_EVENT_STOP; event_code = PTRACE_EVENT_STOP;
@@ -2637,7 +2660,72 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
}break; }break;
case PTRACE_EVENT_FORK: case PTRACE_EVENT_FORK:
{ {
NotImplemented; // grab child pid
pid_t child_pid;
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_GETEVENTMSG, wait_id, 0, &child_pid)) < 0)
{
AssertAlways(0 && "failed to grab child pid");
}
// at this point kernel cloned memory pages and writes in the child process trigger COW
Temp temp = temp_begin(scratch.arena);
int child_memory_fd = open((char*)str8f(temp.arena, "/proc/%d/mem", child_pid).str, O_RDWR);
// grab parent process
DMN_LNX_Entity *parent_thread = dmn_lnx_thread_from_pid(wait_id);
DMN_LNX_Entity *parent_process = parent_thread->parent;
DMN_Handle parent_process_handle = dmn_lnx_handle_from_entity(parent_process);
DMN_Handle parent_thread_handle = dmn_lnx_handle_from_entity(parent_thread);
// if debugger is steps over a line with fork we have to undo the return address spoof
if(MemoryCompare(&parent_process_handle, &ctrls->spoof.process, sizeof(DMN_Handle)) == 0 &&
MemoryCompare(&parent_thread_handle, &ctrls->spoof.thread, sizeof(DMN_Handle)) == 0)
{
if(!dmn_lnx_write(child_memory_fd, r1u64(ctrls->spoof.vaddr, ctrls->spoof.vaddr + ctrls->spoof.size), &ctrls->spoof.old_ip))
{
AssertAlways(0 && "failed to undo spoof");
}
}
if(parent_process->debug_subprocesses)
{
// create child process entity
dmn_lnx_process_untrap_probes(parent_process);
DMN_LNX_Entity *child_process = dmn_lnx_handle_create_process(arena, &events, 1, 1, child_pid);
dmn_lnx_process_trap_probes(child_process);
// copy modules from the parent process
for(DMN_LNX_Entity *module = parent_process->first; module != dmn_lnx_nil_entity; module = module->next)
{
if(module->kind != DMN_LNX_EntityKind_Module) { continue; }
if(module->is_main) { continue; }
dmn_lnx_load_module(arena, &events, child_process, module->name_space_id, module->module_name_vaddr, module->base_vaddr);
}
}
else
{
// take out probes traps from the child probess
dmn_lnx_process_untrap_probes(parent_process);
// take out traps from the child process
for EachNode(n, DMN_ActiveTrap, active_trap_first)
{
if(MemoryCompare(&n->trap->process, &parent_process_handle, sizeof(DMN_Handle)))
{
B32 is_written = dmn_lnx_write(child_memory_fd, r1u64(n->trap->vaddr, n->trap->vaddr + n->swap_bytes.size), n->swap_bytes.str);
AssertAlways(is_written);
}
}
// detach and continue the child process
if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_DETACH, child_pid, 0, 0) < 0))
{
Assert(0 && "unsuccessful detach");
}
}
close(child_memory_fd);
temp_end(temp);
}break; }break;
case PTRACE_EVENT_VFORK: case PTRACE_EVENT_VFORK:
{ {
@@ -2661,7 +2749,8 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
if(process->debug_subprocesses) if(process->debug_subprocesses)
{ {
dmn_lnx_handle_exit_thread(arena, &events, wait_id, 0); dmn_lnx_handle_exit_thread(arena, &events, wait_id, 0);
dmn_lnx_handle_create_process(arena, &events, 1, 0, wait_id); DMN_LNX_Entity *process = dmn_lnx_handle_create_process(arena, &events, 1, 0, wait_id);
dmn_lnx_process_trap_probes(process);
} }
else else
{ {
+9 -1
View File
@@ -203,7 +203,8 @@ struct DMN_LNX_Entity
ELF_Class dl_class; ELF_Class dl_class;
HashTable *loaded_modules_ht; HashTable *loaded_modules_ht;
DMN_LNX_Probe **probes; DMN_LNX_Probe **probes;
U64 probe_vaddrs[DMN_LNX_ProbeType_Count]; DMN_ActiveTrap *first_probe_trap;
DMN_ActiveTrap *last_probe_trap;
U64 main_thread_exit_code; U64 main_thread_exit_code;
U64 thread_count; U64 thread_count;
@@ -222,11 +223,14 @@ struct DMN_LNX_Entity
U64 thread_local_base; U64 thread_local_base;
// module // module
U64 module_name_vaddr;
U64 base_vaddr; U64 base_vaddr;
U64 phvaddr; U64 phvaddr;
U64 phentsize; U64 phentsize;
U64 phcount; U64 phcount;
U64 name_space_id;
B8 is_live; B8 is_live;
B8 is_main;
}; };
typedef struct DMN_LNX_EntityNode DMN_LNX_EntityNode; typedef struct DMN_LNX_EntityNode DMN_LNX_EntityNode;
@@ -372,6 +376,10 @@ internal DMN_Handle dmn_lnx_handle_from_entity(DMN_LNX_Entity *entity);
internal DMN_LNX_Entity *dmn_lnx_entity_from_handle(DMN_Handle handle); internal DMN_LNX_Entity *dmn_lnx_entity_from_handle(DMN_Handle handle);
internal DMN_LNX_Entity *dmn_lnx_thread_from_pid(pid_t pid); internal DMN_LNX_Entity *dmn_lnx_thread_from_pid(pid_t pid);
//- Process
internal void dmn_lnx_process_install_probes();
//- Thread
internal U64 dmn_lnx_thread_read_ip(DMN_LNX_Entity *thread); internal U64 dmn_lnx_thread_read_ip(DMN_LNX_Entity *thread);
internal U64 dmn_lnx_thread_read_sp(DMN_LNX_Entity *thread); internal U64 dmn_lnx_thread_read_sp(DMN_LNX_Entity *thread);
internal void dmn_lnx_thread_write_ip(DMN_LNX_Entity *thread, U64 ip); internal void dmn_lnx_thread_write_ip(DMN_LNX_Entity *thread, U64 ip);