diff --git a/src/ctrl/ctrl_core.c b/src/ctrl/ctrl_core.c index 779f4761..06b3dcba 100644 --- a/src/ctrl/ctrl_core.c +++ b/src/ctrl/ctrl_core.c @@ -60,6 +60,20 @@ ctrl_exception_kind_from_dmn(DMN_ExceptionKind kind) return result; } +internal CTRL_TlsModel +ctrl_dynamic_linker_type_from_dmn(DMN_TlsModel type) +{ + CTRL_TlsModel result = CTRL_TlsModel_Null; + switch(type) + { + default:{}break; + case DMN_TlsModel_Null: {result = CTRL_TlsModel_Null;}break; + case DMN_TlsModel_WinodwsNt: {result = CTRL_TlsModel_WinodwsNt;}break; + case DMN_TlsModel_Gnu: {result = CTRL_TlsModel_Gnu;}break; + } + return result; +} + internal String8 ctrl_string_from_event_kind(CTRL_EventKind kind) { @@ -616,6 +630,9 @@ ctrl_serialized_string_from_event(Arena *arena, CTRL_Event *event, U64 max) str8_serial_push_struct(scratch.arena, &srl, &event->exception_code); str8_serial_push_struct(scratch.arena, &srl, &event->rgba); str8_serial_push_struct(scratch.arena, &srl, &event->bp_flags); + str8_serial_push_struct(scratch.arena, &srl, &event->tls_model); + str8_serial_push_struct(scratch.arena, &srl, &event->tls_index); + str8_serial_push_struct(scratch.arena, &srl, &event->tls_offset); String8 string = event->string; string.size = Min(string.size, max-srl.total_size); str8_serial_push_struct(scratch.arena, &srl, &string.size); @@ -649,6 +666,9 @@ ctrl_event_from_serialized_string(Arena *arena, String8 string) read_off += str8_deserial_read_struct(string, read_off, &event.exception_code); read_off += str8_deserial_read_struct(string, read_off, &event.rgba); read_off += str8_deserial_read_struct(string, read_off, &event.bp_flags); + read_off += str8_deserial_read_struct(string, read_off, &event.tls_model); + read_off += str8_deserial_read_struct(string, read_off, &event.tls_index); + read_off += str8_deserial_read_struct(string, read_off, &event.tls_offset); read_off += str8_deserial_read_struct(string, read_off, &event.string.size); event.string.str = push_array_no_zero(arena, U8, event.string.size); read_off += str8_deserial_read(string, read_off, event.string.str, event.string.size, 1); @@ -1249,6 +1269,7 @@ ctrl_entity_store_apply_events(CTRL_EntityCtxRWStore *store, CTRL_EventList *lis { CTRL_Entity *machine = ctrl_entity_from_handle(&store->ctx, ctrl_handle_make(event->entity.machine_id, dmn_handle_zero())); CTRL_Entity *process = ctrl_entity_alloc(store, machine, CTRL_EntityKind_Process, event->arch, event->entity, (U64)event->entity_id); + process->tls_model = event->tls_model; }break; case CTRL_EventKind_EndProc: { @@ -1370,6 +1391,8 @@ ctrl_entity_store_apply_events(CTRL_EntityCtxRWStore *store, CTRL_EventList *lis ctrl_entity_equip_string(store, module, event->string); module->timestamp = event->timestamp; module->vaddr_range = event->vaddr_rng; + module->tls_index = event->tls_index; + module->tls_offset = event->tls_offset; CTRL_Entity *first_module = ctrl_entity_child_from_kind(process, CTRL_EntityKind_Module); if(first_module == module) { @@ -1723,26 +1746,6 @@ ctrl_entry_point_voff_from_module(CTRL_Handle module_handle) return result; } -internal Rng1U64 -ctrl_tls_vaddr_range_from_module(CTRL_Handle module_handle) -{ - Rng1U64 result = {0}; - U64 hash = ctrl_hash_from_handle(module_handle); - U64 slot_idx = hash%ctrl_state->module_image_info_cache.slots_count; - U64 stripe_idx = slot_idx%ctrl_state->module_image_info_cache.stripes_count; - CTRL_ModuleImageInfoCacheSlot *slot = &ctrl_state->module_image_info_cache.slots[slot_idx]; - CTRL_ModuleImageInfoCacheStripe *stripe = &ctrl_state->module_image_info_cache.stripes[stripe_idx]; - MutexScopeR(stripe->rw_mutex) for(CTRL_ModuleImageInfoCacheNode *n = slot->first; n != 0; n = n->next) - { - if(ctrl_handle_match(n->module, module_handle)) - { - result = n->tls_vaddr_range; - break; - } - } - return result; -} - internal String8 ctrl_initial_debug_info_path_from_module(Arena *arena, CTRL_Handle module_handle) { @@ -3734,7 +3737,6 @@ ctrl_thread__module_open(CTRL_Handle process, CTRL_Handle module, Rng1U64 vaddr_ Arena *arena = arena_alloc(); U64 entry_point_voff = 0; - Rng1U64 tls_vaddr_range = {0}; U32 rdi_dbg_time = 0; Guid rdi_dbg_guid = {0}; String8 exe_dbg_path = {0}; @@ -3855,34 +3857,6 @@ ctrl_thread__module_open(CTRL_Handle process, CTRL_Handle module, Rng1U64 vaddr_ dmn_process_read(process.dmn_handle, r1u64(vaddr_range.min + pdatas_voff_range.min, vaddr_range.min + pdatas_voff_range.max), pdatas); } - // rjf: extract tls header - PE_TLSHeader64 tls_header = {0}; - if(data_dir_count > PE_DataDirectoryIndex_TLS) - { - PE_DataDirectory dir = {0}; - dmn_process_read_struct(process.dmn_handle, vaddr_range.min + opt_ext_off_range.min + reported_data_dir_offset + sizeof(PE_DataDirectory)*PE_DataDirectoryIndex_TLS, &dir); - Rng1U64 tls_voff_range = r1u64((U64)dir.virt_off, (U64)dir.virt_off + (U64)dir.virt_size); - switch(file_header.machine) - { - default:{}break; - case COFF_MachineType_X86: - { - PE_TLSHeader32 tls_header32 = {0}; - dmn_process_read_struct(process.dmn_handle, vaddr_range.min + tls_voff_range.min, &tls_header32); - tls_header.raw_data_start = (U64)tls_header32.raw_data_start; - tls_header.raw_data_end = (U64)tls_header32.raw_data_end; - tls_header.index_address = (U64)tls_header32.index_address; - tls_header.callbacks_address = (U64)tls_header32.callbacks_address; - tls_header.zero_fill_size = (U64)tls_header32.zero_fill_size; - tls_header.characteristics = (U64)tls_header32.characteristics; - }break; - case COFF_MachineType_X64: - { - dmn_process_read_struct(process.dmn_handle, vaddr_range.min + tls_voff_range.min, &tls_header); - }break; - } - } - // rjf: extract sections U64 sec_array_off = opt_ext_off_range.max; U64 sec_count = file_header.section_count; @@ -3892,9 +3866,6 @@ ctrl_thread__module_open(CTRL_Handle process, CTRL_Handle module, Rng1U64 vaddr_ // rjf: grab entry point vaddr entry_point_voff = entry_point; - // rjf: calculate TLS vaddr range - tls_vaddr_range = r1u64(tls_header.index_address, tls_header.index_address+sizeof(U32)); - // rjf: grab data about debug info if(data_dir_count > PE_DataDirectoryIndex_DEBUG) { @@ -4469,11 +4440,12 @@ ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg, case DMN_EventKind_CreateProcess: { CTRL_Event *out_evt = ctrl_event_list_push(scratch.arena, &evts); - out_evt->kind = CTRL_EventKind_NewProc; - out_evt->msg_id = msg->msg_id; - out_evt->entity = ctrl_handle_make(CTRL_MachineID_Local, event->process); - out_evt->arch = event->arch; - out_evt->entity_id = event->code; + out_evt->kind = CTRL_EventKind_NewProc; + out_evt->msg_id = msg->msg_id; + out_evt->entity = ctrl_handle_make(CTRL_MachineID_Local, event->process); + out_evt->arch = event->arch; + out_evt->entity_id = event->code; + out_evt->tls_model = ctrl_dynamic_linker_type_from_dmn(event->tls_model); ctrl_state->process_counter += 1; }break; case DMN_EventKind_CreateThread: @@ -4508,6 +4480,8 @@ ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg, out_evt1->rip_vaddr = event->address; out_evt1->timestamp = exe_timestamp; out_evt1->string = module_path; + out_evt1->tls_index = event->tls_index; + out_evt1->tls_offset = event->tls_offset; CTRL_Event *out_evt2 = ctrl_event_list_push(scratch.arena, &evts); String8 initial_debug_info_path = ctrl_initial_debug_info_path_from_module(scratch.arena, module_handle); U64 debug_info_timestamp = os_properties_from_file_path(initial_debug_info_path).modified; diff --git a/src/ctrl/ctrl_core.h b/src/ctrl/ctrl_core.h index 59a9de51..69d629a8 100644 --- a/src/ctrl/ctrl_core.h +++ b/src/ctrl/ctrl_core.h @@ -59,6 +59,17 @@ struct CTRL_UserBreakpointList U64 count; }; +//////////////////////////////// +//~ Dynamic Linker Types + +typedef U32 CTRL_TlsModel; +enum +{ + CTRL_TlsModel_Null, + CTRL_TlsModel_WinodwsNt, + CTRL_TlsModel_Gnu +}; + //////////////////////////////// //~ rjf: Entity Handle Types @@ -119,6 +130,9 @@ struct CTRL_Entity U64 timestamp; CTRL_UserBreakpointFlags bp_flags; String8 string; + CTRL_TlsModel tls_model; + U64 tls_index; + U64 tls_offset; }; typedef struct CTRL_EntityNode CTRL_EntityNode; @@ -512,11 +526,14 @@ struct CTRL_Event U64 rip_vaddr; U64 stack_base; U64 tls_root; + U64 tls_index; + U64 tls_offset; U64 timestamp; U32 exception_code; U32 rgba; CTRL_UserBreakpointFlags bp_flags; String8 string; + CTRL_TlsModel tls_model; }; typedef struct CTRL_EventNode CTRL_EventNode; @@ -603,7 +620,6 @@ struct CTRL_ModuleImageInfoCacheNode EH_FrameHdr eh_frame_hdr; EH_PtrCtx eh_ptr_ctx; U64 entry_point_voff; - Rng1U64 tls_vaddr_range; String8 initial_debug_info_path; Rng1U64 raddbg_section_voff_range; String8 raddbg_data; @@ -776,6 +792,7 @@ internal U64 ctrl_hash_from_string(String8 string); internal U64 ctrl_hash_from_handle(CTRL_Handle handle); internal CTRL_EventCause ctrl_event_cause_from_dmn_event_kind(DMN_EventKind event_kind); internal CTRL_ExceptionKind ctrl_exception_kind_from_dmn(DMN_ExceptionKind kind); +internal CTRL_TlsModel ctrl_dynamic_linker_type_from_dmn(DMN_TlsModel type); internal String8 ctrl_string_from_event_kind(CTRL_EventKind kind); internal String8 ctrl_string_from_msg_kind(CTRL_MsgKind kind); internal CTRL_EntityKind ctrl_entity_kind_from_string(String8 string); @@ -916,7 +933,6 @@ internal B32 ctrl_thread_write_reg_block(CTRL_Handle thread, void *block); //- rjf: cache lookups internal PE_IntelPdata *ctrl_intel_pdata_from_module_voff(Arena *arena, CTRL_Handle module_handle, U64 voff); internal U64 ctrl_entry_point_voff_from_module(CTRL_Handle module_handle); -internal Rng1U64 ctrl_tls_vaddr_range_from_module(CTRL_Handle module_handle); internal String8 ctrl_initial_debug_info_path_from_module(Arena *arena, CTRL_Handle module_handle); internal String8 ctrl_raddbg_data_from_module(Arena *arena, CTRL_Handle module_handle); @@ -993,7 +1009,7 @@ internal void ctrl_thread__module_close(CTRL_Handle process, CTRL_Handle module, //- rjf: attached process running/event gathering internal DMN_Event *ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg, DMN_RunCtrls *run_ctrls, CTRL_Spoof *spoof); -//- rjf: eval helpers +//- rjf: eval helpers internal U64 ctrl_eval_space_gen(E_Space space); internal B32 ctrl_eval_space_read(E_Space space, void *out, Rng1U64 vaddr_range); diff --git a/src/dbg_engine/dbg_engine_core.c b/src/dbg_engine/dbg_engine_core.c index 0d7587ca..a0a9295f 100644 --- a/src/dbg_engine/dbg_engine_core.c +++ b/src/dbg_engine/dbg_engine_core.c @@ -984,80 +984,68 @@ d_tls_base_vaddr_from_process_root_rip(CTRL_Entity *process, U64 root_vaddr, U64 { ProfBeginFunction(); U64 base_vaddr = 0; - Temp scratch = scratch_begin(0, 0); if(!d_ctrl_targets_running()) { + Temp scratch = scratch_begin(0, 0); + //- rjf: unpack module info - CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr); - Rng1U64 tls_vaddr_range = ctrl_tls_vaddr_range_from_module(module->handle); - U64 addr_size = bit_size_from_arch(process->arch)/8; - - //- rjf: read module's TLS index - U64 tls_index = 0; - if(addr_size != 0) + CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr); + U64 addr_size = byte_size_from_arch(process->arch); + + switch(process->tls_model) { - CTRL_ProcessMemorySlice tls_index_slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process->handle, tls_vaddr_range, 0, 0); - if(tls_index_slice.data.size >= addr_size) + case CTRL_TlsModel_Null: {}break; + case CTRL_TlsModel_WinodwsNt: { - tls_index = *(U64 *)tls_index_slice.data.str; - } - } - - //- rjf: PE path - if(addr_size != 0) - { - U64 thread_info_addr = root_vaddr; - U64 tls_addr_off = tls_index*addr_size; - U64 tls_addr_array = 0; - CTRL_ProcessMemorySlice tls_addr_array_slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process->handle, r1u64(thread_info_addr, thread_info_addr+addr_size), 0, 0); - String8 tls_addr_array_data = tls_addr_array_slice.data; - if(tls_addr_array_data.size >= 8) - { - MemoryCopy(&tls_addr_array, tls_addr_array_data.str, sizeof(U64)); - } - CTRL_ProcessMemorySlice result_slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process->handle, r1u64(tls_addr_array + tls_addr_off, tls_addr_array + tls_addr_off + addr_size), 0, 0); - String8 result_data = result_slice.data; - if(result_data.size >= 8) - { - MemoryCopy(&base_vaddr, result_data.str, sizeof(U64)); - } - } - - //- rjf: non-PE path (not implemented) -#if 0 - if(!bin_is_pe) - { - // TODO(rjf): not supported. old code from the prototype that Nick had sketched out: - // TODO(nick): This code works only if the linked c runtime library is glibc. - // Implement CRT detection here. - - U64 dtv_addr = UINT64_MAX; - demon_read_memory(process->demon_handle, &dtv_addr, thread_info_addr, addr_size); - - /* - union delta_thread_vector + // read thread local base pointer out of TEB + U64 thread_local_base = root_vaddr; + U64 tls_addr_array = 0; + CTRL_ProcessMemorySlice tls_addr_array_slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process->handle, r1u64(thread_local_base, thread_local_base + addr_size), 0, 0); + String8 tls_array_vaddr_data = tls_addr_array_slice.data; + if(tls_array_vaddr_data.size == addr_size) { - size_t counter; - struct + U64 tls_array_vaddr = 0; + MemoryCopyStr8(&tls_array_vaddr, tls_array_vaddr_data); + + // read thread local storage pointer (array of TLS pointers, one per module) + U64 tls_ptr_vaddr = tls_array_vaddr + module->tls_index * addr_size; + CTRL_ProcessMemorySlice result_slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process->handle, r1u64(tls_ptr_vaddr, tls_ptr_vaddr + addr_size), 0, 0); + String8 result_data = result_slice.data; + if(result_data.size == addr_size) { - void *value; - void *to_free; - } pointer; - }; - */ - - U64 dtv_size = 16; - U64 dtv_count = 0; - demon_read_memory(process->demon_handle, &dtv_count, dtv_addr - dtv_size, addr_size); - - if (tls_index > 0 && tls_index < dtv_count) + MemoryCopyStr8(&base_vaddr, result_data); + } + } + }break; + case CTRL_TlsModel_Gnu: { - demon_read_memory(process->demon_handle, &result, dtv_addr + dtv_size*tls_index, addr_size); - } + if(module->tls_index > 0) // zero is reserved for the generation counter + { + // read dynamic thread vector pointer (one per dynamic module) + U64 dtv_base = root_vaddr; + U64 dtv_size = addr_size * 2; // union dtv { size_t counter; struct dtv_pointer { void *val, *to_free; }; }; + U64 dtv_pointer_vaddr = dtv_base + module->tls_index * dtv_size; + CTRL_ProcessMemorySlice dtv_pointer_slice = ctrl_process_memory_slice_from_vaddr_range(scratch.arena, process->handle, r1u64(dtv_pointer_vaddr, dtv_pointer_vaddr + dtv_size), 0, 0); + String8 dtv_pointer_data = dtv_pointer_slice.data; + if(dtv_pointer_data.size == dtv_size) + { + U64 dtv_pointer = 0; + MemoryCopyStr8(&dtv_pointer, dtv_pointer_data); + + // verify that TLS block was allocated + U64 tls_dtv_unallocated = addr_size == 4 ? max_U32 : max_U64; + if(dtv_pointer != tls_dtv_unallocated) + { + base_vaddr = dtv_pointer + module->tls_offset; // add tls_offset because DT_NEEDED modules share TLS block with the main exe + } + } + } + }break; + default: {InvalidPath;}break; } -#endif + + scratch_end(scratch); } - scratch_end(scratch); ProfEnd(); return base_vaddr; } diff --git a/src/demon/demon_core.h b/src/demon/demon_core.h index f0b4ce54..5dec068e 100644 --- a/src/demon/demon_core.h +++ b/src/demon/demon_core.h @@ -18,6 +18,17 @@ struct DMN_CtrlCtx U64 u64[1]; }; +//////////////////////////////// +//~ Dynamic Linker Types + +typedef U32 DMN_TlsModel; +enum +{ + DMN_TlsModel_Null, + DMN_TlsModel_WinodwsNt, + DMN_TlsModel_Gnu +}; + //////////////////////////////// //~ rjf: Handle Types @@ -82,6 +93,9 @@ struct DMN_Event U64 stack_pointer; U64 user_data; B32 exception_repeated; + U64 tls_index; + U64 tls_offset; + DMN_TlsModel tls_model; }; typedef struct DMN_EventNode DMN_EventNode; diff --git a/src/demon/linux/demon_core_linux.c b/src/demon/linux/demon_core_linux.c index 35325711..142b69c6 100644 --- a/src/demon/linux/demon_core_linux.c +++ b/src/demon/linux/demon_core_linux.c @@ -1401,6 +1401,42 @@ dmn_lnx_handle_not_attached(Arena *arena, DMN_EventList *events) e->error_kind = DMN_ErrorKind_NotAttached; } +internal DMN_LNX_Entity * +dmn_lnx_handle_create_thread(Arena *arena, DMN_EventList *events, DMN_LNX_Entity *process, pid_t tid) +{ + DMN_LNX_Entity *thread = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Thread); + thread->id = tid; + thread->arch = process->arch; + thread->reg_block = push_array(process->arena, U8, regs_block_size_from_arch(process->arch)); + thread->thread_state = DMN_LNX_ThreadState_Stopped; + thread->is_reg_block_dirty = !dmn_lnx_thread_read_reg_block(thread); + hash_table_push_u64_raw(dmn_lnx_state->arena, dmn_lnx_state->tid_ht, thread->id, thread); + + if(!thread->is_reg_block_dirty) + { + switch(thread->arch) + { + case Arch_Null: break; + case Arch_x64: thread->thread_local_base = ((REGS_RegBlockX64 *)thread->reg_block)->fsbase.u64; break; + case Arch_x86: + case Arch_arm32: + case Arch_arm64: { NotImplemented; } break; + default: { InvalidPath; } break; + } + } + + DMN_Event *e = dmn_event_list_push(arena, events); + e->kind = DMN_EventKind_CreateThread; + e->process = dmn_lnx_handle_from_entity(process); + e->thread = dmn_lnx_handle_from_entity(thread); + e->arch = thread->arch; + e->code = thread->id; + + process->thread_count += 1; + + return thread; +} + internal void dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, pid_t pid) { @@ -1529,32 +1565,17 @@ dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, pid_t pid) // push create process event { DMN_Event *e = dmn_event_list_push(arena, events); - e->kind = DMN_EventKind_CreateProcess; - e->process = dmn_lnx_handle_from_entity(process); - e->arch = process->arch; - e->code = pid; + e->kind = DMN_EventKind_CreateProcess; + e->process = dmn_lnx_handle_from_entity(process); + e->arch = process->arch; + e->code = pid; + e->tls_model = DMN_TlsModel_Gnu; // TODO: use dynamic linker path to figure out correct enum here } // // init main thread // - DMN_LNX_Entity *thread = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Thread); - thread->id = pid; - thread->arch = process->arch; - thread->is_main_thread = 1; - thread->thread_state = DMN_LNX_ThreadState_Stopped; - thread->reg_block = push_array(process->arena, U8, regs_block_size_from_arch(process->arch)); - thread->is_reg_block_dirty = !dmn_lnx_thread_read_reg_block(thread); - hash_table_push_u64_raw(dmn_lnx_state->arena, dmn_lnx_state->tid_ht, thread->id, thread); - // push create thread event - { - DMN_Event *e = dmn_event_list_push(arena, events); - e->kind = DMN_EventKind_CreateThread; - e->process = dmn_lnx_handle_from_entity(process); - e->thread = dmn_lnx_handle_from_entity(thread); - e->arch = thread->arch; - e->code = thread->id; - } + DMN_LNX_Entity *thread = dmn_lnx_handle_create_thread(arena, events, process, pid); // // init main module @@ -1580,6 +1601,8 @@ dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, pid_t pid) e->string = dmn_lnx_read_string(arena, process->fd, auxv.execfn); e->elf_phdr_vrange = r1u64(auxv.phdr, auxv.phdr + auxv.phent * auxv.phnum); e->elf_phdr_entsize = auxv.phent; + e->tls_index = 1; + e->tls_offset = 0; } } @@ -1643,30 +1666,6 @@ dmn_lnx_handle_exit_process(Arena *arena, DMN_EventList *events, pid_t pid) dmn_lnx_state->active_process_count -= 1; } -internal void -dmn_lnx_handle_create_thread(Arena *arena, DMN_EventList *events, pid_t tid, pid_t new_tid) -{ - DMN_LNX_Entity *thread = dmn_lnx_thread_from_pid(tid); - DMN_LNX_Entity *process = thread->parent; - - DMN_LNX_Entity *new_thread = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Thread); - new_thread->id = new_tid; - new_thread->arch = process->arch; - new_thread->reg_block = push_array(process->arena, U8, regs_block_size_from_arch(process->arch)); - new_thread->thread_state = DMN_LNX_ThreadState_Stopped; - new_thread->is_reg_block_dirty = !dmn_lnx_thread_read_reg_block(new_thread); - hash_table_push_u64_raw(dmn_lnx_state->arena, dmn_lnx_state->tid_ht, new_thread->id, new_thread); - - DMN_Event *e = dmn_event_list_push(arena, events); - e->kind = DMN_EventKind_CreateThread; - e->process = dmn_lnx_handle_from_entity(process); - e->thread = dmn_lnx_handle_from_entity(new_thread); - e->arch = new_thread->arch; - e->code = new_thread->id; - - process->thread_count += 1; -} - internal void dmn_lnx_handle_exit_thread(Arena *arena, DMN_EventList *events, pid_t tid, U64 exit_code) { @@ -1716,14 +1715,27 @@ dmn_lnx_handle_load_module(Arena *arena, DMN_EventList *events, DMN_LNX_Entity * 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); + // read TLS index and TLS offset + U64 tls_index = max_U64; + U64 tls_offset = max_U64; + if(dmn_lnx_state->is_tls_detected) + { + 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); + tls_modid_range = shift_1u64(tls_modid_range, map_vaddr); + tls_offset_range = shift_1u64(tls_offset_range, map_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_offset_range, &tls_offset)) { Assert(0 && "failed to read TLS offset"); } + } + // fill out module - module = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Module); - module->id = map.name_vaddr; - module->base_vaddr = map.addr_vaddr; + module = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Module); + module->id = map.name_vaddr; + module->base_vaddr = map.addr_vaddr; - // push load event if(!str8_match(module_name, str8_lit("linux-vdso.so.1"), 0)) { + // push load event DMN_Event *e = dmn_event_list_push(arena, events); e->kind = DMN_EventKind_LoadModule; e->process = dmn_lnx_handle_from_entity(process); @@ -1734,6 +1746,8 @@ dmn_lnx_handle_load_module(Arena *arena, DMN_EventList *events, DMN_LNX_Entity * 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_entsize = module_ehdr.e_phentsize; + e->tls_index = tls_index; + e->tls_offset = tls_offset; } // create mapping for base -> module @@ -2007,19 +2021,40 @@ internal void dmn_init(void) { local_persist B32 was_inited; - local_persist DMN_LNX_State state; - AssertAlways(!was_inited); - was_inited = 1; + if(!was_inited) + { + was_inited = 1; - dmn_lnx_state = &state; - dmn_lnx_state->arena = arena_alloc(); - dmn_lnx_state->access_mutex = mutex_alloc(); - dmn_lnx_state->entities_arena = arena_alloc(.reserve_size = GB(32), .commit_size = KB(64), .flags = ArenaFlag_NoChain); - dmn_lnx_state->entities_base = push_array(dmn_lnx_state->entities_arena, DMN_LNX_Entity, 0); - dmn_lnx_state->tid_ht = hash_table_init(dmn_lnx_state->arena, 0x2000); - dmn_lnx_state->pid_ht = hash_table_init(dmn_lnx_state->arena, 0x400); - dmn_lnx_state->halter_mutex = mutex_alloc(); - dmn_lnx_entity_alloc(dmn_lnx_nil_entity, DMN_LNX_EntityKind_Root); + local_persist DMN_LNX_State state; + dmn_lnx_state = &state; + + dmn_lnx_state->arena = arena_alloc(); + dmn_lnx_state->access_mutex = mutex_alloc(); + dmn_lnx_state->entities_arena = arena_alloc(.reserve_size = GB(32), .commit_size = KB(64), .flags = ArenaFlag_NoChain); + dmn_lnx_state->entities_base = push_array(dmn_lnx_state->entities_arena, DMN_LNX_Entity, 0); + dmn_lnx_state->tid_ht = hash_table_init(dmn_lnx_state->arena, 0x2000); + dmn_lnx_state->pid_ht = hash_table_init(dmn_lnx_state->arena, 0x400); + dmn_lnx_state->halter_mutex = mutex_alloc(); + dmn_lnx_entity_alloc(dmn_lnx_nil_entity, DMN_LNX_EntityKind_Root); + + // find offsets of TLS index and TLS offset in the link_map struct + // + // TODO: assuming that target is using same libc version as debugger + { + DMN_LNX_DbDesc *tls_modid_desc = dlsym(RTLD_DEFAULT, "_thread_db_link_map_l_tls_modid"); + DMN_LNX_DbDesc *tls_offset_desc = dlsym(RTLD_DEFAULT, "_thread_db_link_map_l_tls_offset"); + if(tls_modid_desc && tls_offset_desc) + { + if(tls_modid_desc->bit_size <= 64 && tls_offset_desc->bit_size <= 64) + { + dmn_lnx_state->tls_modid_desc = *tls_modid_desc; + dmn_lnx_state->tls_offset_desc = *tls_offset_desc; + dmn_lnx_state->is_tls_detected = 1; + } + else { Assert(0 && "invalid TLS desc"); } + } + } + } } //////////////////////////////// @@ -2518,7 +2553,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) pid_t new_pid; if(OS_LNX_RETRY_ON_EINTR(ptrace(PTRACE_GETEVENTMSG, wait_id, 0, &new_pid)) >= 0) { - dmn_lnx_handle_create_thread(arena, &events, wait_id, new_pid); + dmn_lnx_handle_create_thread(arena, &events, dmn_lnx_thread_from_pid(wait_id), new_pid); } else { Assert(0 && "failed to get new tid"); } }break; @@ -2742,7 +2777,33 @@ dmn_stack_base_vaddr_from_thread(DMN_Handle handle) internal U64 dmn_tls_root_vaddr_from_thread(DMN_Handle handle) { - return 0; + U64 tls_root_vaddr = max_U64; + DMN_AccessScope + { + DMN_LNX_Entity *thread = dmn_lnx_entity_from_handle(handle); + switch (thread->arch) + { + case Arch_Null: {} break; + case Arch_x64: + { + DMN_LNX_Entity *process = thread->parent; + REGS_RegBlockX64 *reg_block = thread->reg_block; + U64 dtv_pointer = 0; + if(dmn_lnx_read_struct(process->fd, reg_block->fsbase.u64 + 8, &dtv_pointer)) + { + tls_root_vaddr = dtv_pointer; + } + } break; + case Arch_x86: + case Arch_arm32: + case Arch_arm64: + { + NotImplemented; + } break; + default: { InvalidPath; } break; + } + } + return tls_root_vaddr; } internal B32 diff --git a/src/demon/linux/demon_core_linux.h b/src/demon/linux/demon_core_linux.h index 8f9867fd..c23bf2c1 100644 --- a/src/demon/linux/demon_core_linux.h +++ b/src/demon/linux/demon_core_linux.h @@ -61,6 +61,15 @@ struct DMN_LNX_UserX64 }; StaticAssert(sizeof(DMN_LNX_UserX64) == 912, g_dmn_lnx_user_x64_size_check); +//////////////////////////////// + +typedef struct +{ + U32 bit_size; + U32 count; + U32 offset; +} DMN_LNX_DbDesc; + //////////////////////////////// //~ SDT Probes @@ -211,6 +220,7 @@ struct DMN_LNX_Entity B32 pass_through_signal; U64 pass_through_signo; DMN_LNX_ThreadState thread_state; + U64 thread_local_base; // module U64 base_vaddr; @@ -292,6 +302,11 @@ struct DMN_LNX_State U64 halt_code; U64 halt_user_data; B32 is_halting; + + // TLS + B32 is_tls_detected; + DMN_LNX_DbDesc tls_modid_desc; + DMN_LNX_DbDesc tls_offset_desc; }; //////////////////////////////// diff --git a/src/demon/win32/demon_core_win32.c b/src/demon/win32/demon_core_win32.c index 8ccf1fa7..b2fe81f9 100644 --- a/src/demon/win32/demon_core_win32.c +++ b/src/demon/win32/demon_core_win32.c @@ -465,7 +465,7 @@ dmn_w32_image_info_from_process_base_vaddr(HANDLE process, U64 base_vaddr) U32 pe_offset = 0; { U64 dos_magic_off = base_vaddr; - U16 dos_magic = 0; + U16 dos_magic = 0; dmn_w32_process_read_struct(process, dos_magic_off, &dos_magic); if(dos_magic == PE_DOS_MAGIC) { @@ -475,56 +475,107 @@ dmn_w32_image_info_from_process_base_vaddr(HANDLE process, U64 base_vaddr) } // rjf: get COFF header - B32 got_coff_header = 0; - U64 coff_header_off = 0; - COFF_FileHeader coff_header = {0}; + B32 got_coff_header = 0; + U64 coff_header_off = 0; + COFF_FileHeader coff_header = {0}; if(pe_offset > 0) { U64 pe_magic_off = base_vaddr + pe_offset; - U32 pe_magic = 0; + U32 pe_magic = 0; dmn_w32_process_read_struct(process, pe_magic_off, &pe_magic); if(pe_magic == PE_MAGIC) { coff_header_off = pe_magic_off + sizeof(pe_magic); - if(dmn_w32_process_read_struct(process, coff_header_off, &coff_header)) - { - got_coff_header = 1; - } + got_coff_header = dmn_w32_process_read_struct(process, coff_header_off, &coff_header); } } - // rjf: get arch and size DMN_W32_ImageInfo result = zero_struct; if(got_coff_header) { - U64 optional_size_off = 0; - Arch arch = Arch_Null; - switch(coff_header.machine) + U64 optional_off = coff_header_off + sizeof(COFF_FileHeader); + + Arch arch = arch_from_coff_machine(coff_header.machine); + U64 image_size = 0; + U64 data_dir_count = 0; + U64 data_dir_off = max_U64; + U64 tls_index = max_U64; + + // parse optional header + if(pe_has_plus_header(coff_header.machine)) { - case COFF_MachineType_X86: + PE_OptionalHeader32Plus opt_header = {0}; + if(dmn_w32_process_read_struct(process, optional_off, &opt_header)) { - arch = Arch_x86; - optional_size_off = OffsetOf(PE_OptionalHeader32, sizeof_image); - }break; - case COFF_MachineType_X64: - { - arch = Arch_x64; - optional_size_off = OffsetOf(PE_OptionalHeader32Plus, sizeof_image); - }break; - default: - {}break; - } - if(arch != Arch_Null) - { - U64 optional_off = coff_header_off + sizeof(coff_header); - U32 size = 0; - if(dmn_w32_process_read_struct(process, optional_off+optional_size_off, &size) >= sizeof(size)) - { - result.arch = arch; - result.size = size; + image_size = opt_header.sizeof_image; + data_dir_count = opt_header.data_dir_count; + data_dir_off = optional_off + sizeof(opt_header); } + else { Assert(0 && "failed to read optional header"); } } + else + { + PE_OptionalHeader32 opt_header = {0}; + if(dmn_w32_process_read_struct(process, optional_off, &opt_header)) + { + image_size = opt_header.sizeof_image; + data_dir_count = opt_header.data_dir_count; + data_dir_off = optional_off + sizeof(opt_header); + } + else { Assert(0 && "failed to read optional header"); } + } + + // extract TLS index + if(PE_DataDirectoryIndex_TLS < data_dir_count) + { + U64 tls_dir_vaddr = data_dir_off + PE_DataDirectoryIndex_TLS * sizeof(PE_DataDirectory); + PE_DataDirectory tls_dir = {0}; + if(dmn_w32_process_read_struct(process, tls_dir_vaddr, &tls_dir)) + { + if(pe_has_plus_header(coff_header.machine)) + { + if(tls_dir.virt_size == sizeof(PE_TLSHeader64)) + { + PE_TLSHeader64 tls_header = {0}; + if(dmn_w32_process_read_struct(process, base_vaddr + tls_dir.virt_off, &tls_header)) + { + U64 tls_index64 = 0; + if(dmn_w32_process_read_struct(process, tls_header.index_address, &tls_index64)) + { + tls_index = tls_index64; + } + else { Assert(0 && "failed to read TLS Index 64"); } + } + else { Assert(0 && "failed to read TLS Header 64"); } + } + } + else + { + if(tls_dir.virt_size == sizeof(PE_TLSHeader32)) + { + PE_TLSHeader32 tls_header = {0}; + if(dmn_w32_process_read_struct(process, base_vaddr + tls_dir.virt_off, &tls_header)) + { + U32 tls_index32 = 0; + if(dmn_w32_process_read_struct(process, tls_header.index_address, &tls_index32)) + { + tls_index = tls_index32; + } + else { Assert(0 && "failed to read TLS Index 32"); } + } + else { Assert(0 && "failed to read TLS Header32"); } + } + } + } + else { Assert(0 && "failed to read TLS directory"); } + } + + // fill out result + result.arch = arch; + result.size = image_size; + result.tls_index = tls_index; } + else { Assert(0 && "failed to find COFF file header"); } return result; } @@ -2003,10 +2054,11 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) // rjf: create process { DMN_Event *e = dmn_event_list_push(arena, &events); - e->kind = DMN_EventKind_CreateProcess; - e->process = dmn_w32_handle_from_entity(process); - e->arch = image_info.arch; - e->code = evt.dwProcessId; + e->kind = DMN_EventKind_CreateProcess; + e->process = dmn_w32_handle_from_entity(process); + e->arch = image_info.arch; + e->code = evt.dwProcessId; + e->tls_model = DMN_TlsModel_WinodwsNt; } // rjf: create thread @@ -2022,13 +2074,14 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) // rjf: load module { DMN_Event *e = dmn_event_list_push(arena, &events); - e->kind = DMN_EventKind_LoadModule; - e->process = dmn_w32_handle_from_entity(process); - e->module = dmn_w32_handle_from_entity(module); - e->arch = image_info.arch; - e->address = module_base; - e->size = image_info.size; - e->string = dmn_w32_full_path_from_module(arena, module); + e->kind = DMN_EventKind_LoadModule; + e->process = dmn_w32_handle_from_entity(process); + e->module = dmn_w32_handle_from_entity(module); + e->arch = image_info.arch; + e->address = module_base; + e->size = image_info.size; + e->string = dmn_w32_full_path_from_module(arena, module); + e->tls_index = image_info.tls_index; } } }break; @@ -2195,13 +2248,14 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) // rjf: generate event { DMN_Event *e = dmn_event_list_push(arena, &events); - e->kind = DMN_EventKind_LoadModule; - e->process = dmn_w32_handle_from_entity(process); - e->module = dmn_w32_handle_from_entity(module); - e->arch = module->arch; - e->address = module_base; - e->size = image_info.size; - e->string = dmn_w32_full_path_from_module(arena, module); + e->kind = DMN_EventKind_LoadModule; + e->process = dmn_w32_handle_from_entity(process); + e->module = dmn_w32_handle_from_entity(module); + e->arch = module->arch; + e->address = module_base; + e->size = image_info.size; + e->string = dmn_w32_full_path_from_module(arena, module); + e->tls_index = image_info.tls_index; } }break; diff --git a/src/demon/win32/demon_core_win32.h b/src/demon/win32/demon_core_win32.h index c33f71c6..f5f9265e 100644 --- a/src/demon/win32/demon_core_win32.h +++ b/src/demon/win32/demon_core_win32.h @@ -255,6 +255,7 @@ struct DMN_W32_ImageInfo { Arch arch; U32 size; + U64 tls_index; }; ////////////////////////////////