void ltrace_init(int argc, char **argv) { struct opt_p_t *opt_p_tmp; atexit(normal_exit); signal(SIGINT, signal_exit); /* Detach processes when interrupted */ signal(SIGTERM, signal_exit); /* ... or killed */ argv = process_options(argc, argv); init_global_config(); while (opt_F) { /* If filename begins with ~, expand it to the user's home */ /* directory. This does not correctly handle ~yoda, but that */ /* isn't as bad as it seems because the shell will normally */ /* be doing the expansion for us; only the hardcoded */ /* ~/.ltrace.conf should ever use this code. */ if (opt_F->filename[0] == '~') { char path[PATH_MAX]; char *home_dir = getenv("HOME"); if (home_dir) { strncpy(path, home_dir, PATH_MAX - 1); path[PATH_MAX - 1] = '\0'; strncat(path, opt_F->filename + 1, PATH_MAX - strlen(path) - 1); read_config_file(path); } } else { read_config_file(opt_F->filename); } struct opt_F_t *next = opt_F->next; if (opt_F->own_filename) free(opt_F->filename); free(opt_F); opt_F = next; } if (command) { /* Check that the binary ABI is supported before * calling execute_program. */ struct ltelf lte = {}; open_elf(<e, command); do_close_elf(<e); pid_t pid = execute_program(command, argv); struct Process *proc = open_program(command, pid); if (proc == NULL) { fprintf(stderr, "couldn't open program '%s': %s\n", command, strerror(errno)); exit(EXIT_FAILURE); } trace_set_options(proc); continue_process(pid); } opt_p_tmp = opt_p; while (opt_p_tmp) { open_pid(opt_p_tmp->pid); opt_p_tmp = opt_p_tmp->next; } }
void open_pid(pid_t pid) { Process *proc; char *filename; if (trace_pid(pid) < 0) { fprintf(stderr, "Cannot attach to pid %u: %s\n", pid, strerror(errno)); return; } filename = pid2name(pid); if (!filename) { fprintf(stderr, "Cannot trace pid %u: %s\n", pid, strerror(errno)); return; } proc = open_program(filename, pid); continue_process(pid); proc->breakpoints_enabled = 1; //modify for android attach_child_thread(proc); }
static void handle_clone(Event * event) { Process *p; debug(DEBUG_FUNCTION, "handle_clone(pid=%d)", event->proc->pid); p = malloc(sizeof(Process)); if (!p) { perror("malloc()"); exit(1); } memcpy(p, event->proc, sizeof(Process)); p->pid = event->e_un.newpid; p->parent = event->proc; /* We save register values to the arch pointer, and these need to be per-thread. */ p->arch_ptr = NULL; if (pending_new(p->pid)) { pending_new_remove(p->pid); if (p->event_handler != NULL) destroy_event_handler(p); if (event->proc->state == STATE_ATTACHED && options.follow) { p->state = STATE_ATTACHED; } else { p->state = STATE_IGNORED; } continue_process(p->pid); add_process(p); } else { p->state = STATE_BEING_CREATED; add_process(p); } if (p->leader == p) clone_breakpoints(p, event->proc->leader); else /* Thread groups share breakpoints. */ p->breakpoints = NULL; if (event->type == EVENT_VFORK) continue_after_vfork(p); else continue_process(event->proc->pid); }
static void handle_arch_sysret(Event *event) { debug(DEBUG_FUNCTION, "handle_arch_sysret(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); if (event->proc->state != STATE_IGNORED) { if (opt_T || options.summary) { calc_time_spent(event->proc); } if (options.syscalls) { output_right(LT_TOF_SYSCALLR, event->proc, arch_sysname(event->proc, event->e_un.sysnum)); } callstack_pop(event->proc); } continue_process(event->proc->pid); }
static void handle_arch_syscall(Event *event) { debug(DEBUG_FUNCTION, "handle_arch_syscall(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); if (event->proc->state != STATE_IGNORED) { callstack_push_syscall(event->proc, 0xf0000 + event->e_un.sysnum); if (options.syscalls) { output_left(LT_TOF_SYSCALL, event->proc, arch_sysname(event->proc, event->e_un.sysnum)); } if (event->proc->breakpoints_enabled == 0) { enable_all_breakpoints(event->proc); } } continue_process(event->proc->pid); }
static void handle_new(Event * event) { Process * proc; debug(DEBUG_FUNCTION, "handle_new(pid=%d)", event->e_un.newpid); proc = pid2proc(event->e_un.newpid); if (!proc) { pending_new_insert(event->e_un.newpid); } else { assert(proc->state == STATE_BEING_CREATED); if (options.follow) { proc->state = STATE_ATTACHED; } else { proc->state = STATE_IGNORED; } continue_process(proc->pid); } }
void ltrace_init(int argc, char **argv) { setlocale(LC_ALL, ""); struct opt_p_t *opt_p_tmp; atexit(normal_exit); signal(SIGINT, signal_exit); /* Detach processes when interrupted */ signal(SIGTERM, signal_exit); /* ... or killed */ argv = process_options(argc, argv); init_global_config(); if (command) { /* Check that the binary ABI is supported before * calling execute_program. */ { struct ltelf lte; if (ltelf_init(<e, command) == 0) ltelf_destroy(<e); else exit(EXIT_FAILURE); } pid_t pid = execute_program(command, argv); struct process *proc = open_program(command, pid); if (proc == NULL) { fprintf(stderr, "couldn't open program '%s': %s\n", command, strerror(errno)); exit(EXIT_FAILURE); } trace_set_options(proc); continue_process(pid); } opt_p_tmp = opt_p; while (opt_p_tmp) { open_pid(opt_p_tmp->pid); opt_p_tmp = opt_p_tmp->next; } }
//-------------------------------------------------------------------------- static int idaapi callback(void * /*user_data*/, int notification_code, va_list va) { switch ( notification_code ) { case dbg_process_start: // reset instruction counter g_nb_insn = 0; break; case dbg_run_to: msg("tracer: entrypoint reached\n"); enable_insn_trace(true); continue_process(); break; // A step occured (one instruction was executed). This event // notification is only generated if step tracing is enabled. case dbg_trace: { /*thid_t tid =*/ va_arg(va, thid_t); ea_t ip = va_arg(va, ea_t); msg("[%d] tracing over: %a\n", g_nb_insn, ip); if ( g_nb_insn == g_max_insn ) { // stop the trace mode and suspend the process disable_step_trace(); suspend_process(); msg("process suspended (traced %d instructions)\n", g_max_insn); } else { g_nb_insn++; } } break; case dbg_process_exit: unhook_from_notification_point(HT_DBG, callback, NULL); break; } return 0; }
static void handle_exec(Event * event) { Process * proc = event->proc; pid_t saved_pid; debug(DEBUG_FUNCTION, "handle_exec(pid=%d)", proc->pid); if (proc->state == STATE_IGNORED) { untrace_pid(proc->pid); remove_process(proc); return; } output_line(proc, "--- Called exec() ---"); proc->mask_32bit = 0; proc->personality = 0; proc->arch_ptr = NULL; free(proc->filename); proc->filename = pid2name(proc->pid); saved_pid = proc->pid; proc->pid = 0; breakpoints_init(proc, 0); proc->pid = saved_pid; proc->callstack_depth = 0; continue_process(proc->pid); }
static void handle_breakpoint(Event *event) { int i, j; Breakpoint *sbp; Process *leader = event->proc->leader; /* The leader has terminated. */ if (leader == NULL) { continue_process(event->proc->pid); return; } debug(DEBUG_FUNCTION, "handle_breakpoint(pid=%d, addr=%p)", event->proc->pid, event->e_un.brk_addr); debug(2, "event: breakpoint (%p)", event->e_un.brk_addr); #ifdef __powerpc__ /* Need to skip following NOP's to prevent a fake function from being stacked. */ long stub_addr = (long) get_count_register(event->proc); Breakpoint *stub_bp = NULL; char nop_instruction[] = PPC_NOP; stub_bp = address2bpstruct(leader, event->e_un.brk_addr); if (stub_bp) { unsigned char *bp_instruction = stub_bp->orig_value; if (memcmp(bp_instruction, nop_instruction, PPC_NOP_LENGTH) == 0) { if (stub_addr != (long) event->e_un.brk_addr) { set_instruction_pointer (event->proc, event->e_un.brk_addr + 4); continue_process(event->proc->pid); return; } } } #endif for (i = event->proc->callstack_depth - 1; i >= 0; i--) { if (event->e_un.brk_addr == event->proc->callstack[i].return_addr) { #ifdef __powerpc__ /* * PPC HACK! (XXX FIXME TODO) * The PLT gets modified during the first call, * so be sure to re-enable the breakpoint. */ unsigned long a; struct library_symbol *libsym = event->proc->callstack[i].c_un.libfunc; void *addr = sym2addr(event->proc, libsym); if (libsym->plt_type != LS_TOPLT_POINT) { unsigned char break_insn[] = BREAKPOINT_VALUE; sbp = address2bpstruct(leader, addr); assert(sbp); a = ptrace(PTRACE_PEEKTEXT, event->proc->pid, addr); if (memcmp(&a, break_insn, BREAKPOINT_LENGTH)) { sbp->enabled--; insert_breakpoint(event->proc, addr, libsym, 1); } } else { sbp = dict_find_entry(leader->breakpoints, addr); /* On powerpc, the breakpoint address may end up being actual entry point of the library symbol, not the PLT address we computed. In that case, sbp is NULL. */ if (sbp == NULL || addr != sbp->addr) { insert_breakpoint(event->proc, addr, libsym, 1); } } #elif defined(__mips__) void *addr = NULL; struct library_symbol *sym= event->proc->callstack[i].c_un.libfunc; struct library_symbol *new_sym; assert(sym); addr = sym2addr(event->proc, sym); sbp = dict_find_entry(leader->breakpoints, addr); if (sbp) { if (addr != sbp->addr) { insert_breakpoint(event->proc, addr, sym, 1); } } else { new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1); memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1); new_sym->next = leader->list_of_symbols; leader->list_of_symbols = new_sym; insert_breakpoint(event->proc, addr, new_sym, 1); } #endif for (j = event->proc->callstack_depth - 1; j > i; j--) { callstack_pop(event->proc); } if (event->proc->state != STATE_IGNORED) { if (opt_T || options.summary) { calc_time_spent(event->proc); } } event->proc->return_addr = event->e_un.brk_addr; if (event->proc->state != STATE_IGNORED) { mock_return(LT_TOF_FUNCTIONR, event->proc, event->proc->callstack[i].c_un.libfunc->name); output_right(LT_TOF_FUNCTIONR, event->proc, event->proc->callstack[i].c_un.libfunc->name); } callstack_pop(event->proc); sbp = address2bpstruct(leader, event->e_un.brk_addr); continue_after_breakpoint(event->proc, sbp); return; } } if ((sbp = address2bpstruct(leader, event->e_un.brk_addr))) { if (sbp->libsym == NULL) { continue_after_breakpoint(event->proc, sbp); return; } if (strcmp(sbp->libsym->name, "") == 0) { debug(DEBUG_PROCESS, "Hit _dl_debug_state breakpoint!\n"); arch_check_dbg(leader); } if (event->proc->state != STATE_IGNORED) { event->proc->stack_pointer = get_stack_pointer(event->proc); event->proc->return_addr = get_return_addr(event->proc, event->proc->stack_pointer); callstack_push_symfunc(event->proc, sbp->libsym); output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym->name); } #ifdef PLT_REINITALISATION_BP if (event->proc->need_to_reinitialize_breakpoints && (strcmp(sbp->libsym->name, PLTs_initialized_by_here) == 0)) reinitialize_breakpoints(leader); #endif continue_after_breakpoint(event->proc, sbp); return; } if (event->proc->state != STATE_IGNORED && !options.no_plt) { output_line(event->proc, "unexpected breakpoint at %p", (void *)event->e_un.brk_addr); } continue_process(event->proc->pid); }
//-------------------------------------------------------------------------- static int idaapi callback( void * /*user_data*/, int notification_code, va_list va) { static int stage = 0; static bool is_dll; static char needed_file[QMAXPATH]; switch ( notification_code ) { case dbg_process_start: case dbg_process_attach: get_input_file_path(needed_file, sizeof(needed_file)); // no break case dbg_library_load: if ( stage == 0 ) { const debug_event_t *pev = va_arg(va, const debug_event_t *); if ( !strieq(pev->modinfo.name, needed_file) ) break; if ( notification_code == dbg_library_load ) is_dll = true; // remember the current module bounds if ( pev->modinfo.rebase_to != BADADDR ) curmod.startEA = pev->modinfo.rebase_to; else curmod.startEA = pev->modinfo.base; curmod.endEA = curmod.startEA + pev->modinfo.size; deb(IDA_DEBUG_PLUGIN, "UUNP: module space %a-%a\n", curmod.startEA, curmod.endEA); ++stage; } break; case dbg_library_unload: if ( stage != 0 && is_dll ) { const debug_event_t *pev = va_arg(va, const debug_event_t *); if ( curmod.startEA == pev->modinfo.base || curmod.startEA == pev->modinfo.rebase_to ) { deb(IDA_DEBUG_PLUGIN, "UUNP: unload unpacked module\n"); if ( stage > 2 ) enable_step_trace(false); stage = 0; curmod.startEA = 0; curmod.endEA = 0; _hide_wait_box(); } } break; case dbg_run_to: // Parameters: const debug_event_t *event dbg->stopped_at_debug_event(true); bp_gpa = get_name_ea(BADADDR, "kernel32_GetProcAddress"); #ifndef __X64__ if( (LONG)GetVersion() < 0 ) // win9x mode -- use thunk's { is_9x = true; win9x_resolve_gpa_thunk(); } #endif if ( bp_gpa == BADADDR ) { bring_debugger_to_front(); warning("Sorry, could not find kernel32.GetProcAddress"); FORCE_STOP: stage = 4; // last stage clear_requests_queue(); request_exit_process(); run_requests(); break; } else if( !my_add_bpt(bp_gpa) ) { bring_debugger_to_front(); warning("Sorry, can not set bpt to kernel32.GetProcAddress"); goto FORCE_STOP; } else { ++stage; set_wait_box("Waiting for a call to GetProcAddress()"); } continue_process(); break; case dbg_bpt: // A user defined breakpoint was reached. // Parameters: thid_t tid // ea_t breakpoint_ea // int *warn = -1 // Return (in *warn): // -1 - to display a breakpoint warning dialog // if the process is suspended. // 0 - to never display a breakpoint warning dialog. // 1 - to always display a breakpoint warning dialog. { thid_t tid = va_arg(va, thid_t); qnotused(tid); ea_t ea = va_arg(va, ea_t); //int *warn = va_arg(va, int*); if ( stage == 2 ) { if ( ea == bp_gpa ) { regval_t rv; if ( get_reg_val(REGNAME_ESP, &rv) ) { ea_t esp = ea_t(rv.ival); invalidate_dbgmem_contents(esp, 1024); ea_t gpa_caller = getPtr(esp); if ( !is_library_entry(gpa_caller) ) { ea_t nameaddr; if ( ptrSz == 4 ) { nameaddr = get_long(esp+8); } else { get_reg_val(REGNAME_ECX, &rv); nameaddr = ea_t(rv.ival); } invalidate_dbgmem_contents(nameaddr, 1024); char name[MAXSTR]; size_t len = get_max_ascii_length(nameaddr, ASCSTR_C, ALOPT_IGNHEADS); name[0] = '\0'; get_ascii_contents2(nameaddr, len, ASCSTR_C, name, sizeof(name)); if ( !ignore_win32_api(name) ) { deb(IDA_DEBUG_PLUGIN, "%a: found a call to GetProcAddress(%s)\n", gpa_caller, name); if ( !my_del_bpt(bp_gpa) || !my_add_bpt(gpa_caller) ) error("Can not modify breakpoint"); } } } } else if ( ea == bpt_ea ) { my_del_bpt(ea); if ( !is_library_entry(ea) ) { msg("Uunp: reached unpacker code at %a, switching to trace mode\n", ea); enable_step_trace(true); ++stage; uint64 eax; if ( get_reg_val(REGNAME_EAX, &eax) ) an_imported_func = ea_t(eax); set_wait_box("Waiting for the unpacker to finish"); } else { warning("%a: bpt in library code", ea); // how can it be? my_add_bpt(bp_gpa); } } // not our bpt? skip it else { // hide the wait box to allow others plugins to properly stop _hide_wait_box(); break; } } } // while continue_process() would work here too, request+run is more universal // because they do not ignore the request queue request_continue_process(); run_requests(); break; case dbg_trace: // A step occured (one instruction was executed). This event // notification is only generated if step tracing is enabled. // Parameter: none if ( stage == 3 ) { thid_t tid = va_arg(va, thid_t); qnotused(tid); ea_t ip = va_arg(va, ea_t); // ip reached the OEP range? if ( oep_area.contains(ip) ) { // stop the trace mode enable_step_trace(false); msg("Uunp: reached OEP %a\n", ip); set_wait_box("Reanalyzing the unpacked code"); // reanalyze the unpacked code do_unknown_range(oep_area.startEA, oep_area.size(), DOUNK_EXPAND); auto_make_code(ip); // plan to make code noUsed(oep_area.startEA, oep_area.endEA); // plan to reanalyze auto_mark_range(oep_area.startEA, oep_area.endEA, AU_FINAL); // plan to analyze move_entry(ip); // mark the program's entry point _hide_wait_box(); // inform the user bring_debugger_to_front(); if ( askyn_c(1, "HIDECANCEL\n" "The universal unpacker has finished its work.\n" "Do you want to take a memory snapshot and stop now?\n" "(you can do it yourself if you want)\n") > 0 ) { set_wait_box("Recreating the import table"); invalidate_dbgmem_config(); if ( is_9x ) find_thunked_imports(); create_impdir(); set_wait_box("Storing resources to 'resource.res'"); if ( resfile[0] != '\0' ) extract_resource(resfile); _hide_wait_box(); if ( take_memory_snapshot(true) ) goto FORCE_STOP; } suspend_process(); unhook_from_notification_point(HT_DBG, callback, NULL); } } break; case dbg_process_exit: { stage = 0; // stop the tracing _hide_wait_box(); unhook_from_notification_point(HT_DBG, callback, NULL); if ( success ) jumpto(inf.beginEA, -1); else tell_about_failure(); } break; case dbg_exception:// Parameters: const debug_event_t *event // int *warn = -1 // Return (in *warn): // -1 - to display an exception warning dialog // if the process is suspended. // 0 - to never display an exception warning dialog. // 1 - to always display an exception warning dialog. { // const debug_event_t *event = va_arg(va, const debug_event_t *); // int *warn = va_arg(va, int *); // FIXME: handle code which uses SEH to unpack itself if ( askyn_c(1, "AUTOHIDE DATABASE\n" "HIDECANCEL\n" "An exception occurred in the program.\n" "UUNP does not support exceptions yet.\n" "The execution has been suspended.\n" "Do you want to continue the unpacking?") <= 0 ) { _hide_wait_box(); stage = 0; enable_step_trace(false); // stop the trace mode suspend_process(); } else { continue_process(); } } break; case dbg_request_error: // An error occured during the processing of a request. // Parameters: ui_notification_t failed_command // dbg_notification_t failed_dbg_notification { ui_notification_t failed_cmd = va_arg(va, ui_notification_t); dbg_notification_t failed_dbg_notification = va_arg(va, dbg_notification_t); _hide_wait_box(); stage = 0; warning("dbg request error: command: %d notification: %d", failed_cmd, failed_dbg_notification); } break; } return 0; }
static enum callback_status start_one_pid(Process * proc, void * data) { continue_process(proc->pid); return CBS_CONT; }