/* Symbol matching on the stack content. Always useful */ int e2dbg_stack_dump(uint32_t size, eresi_Addr start) { long *i; char logbuf[BUFSIZ]; char *name; elfsh_SAddr off; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); e2dbg_output(" .:: Stack ::.\n"); /* Just a simple loop that dump resolved stack content */ for (i = (long *) start; i < (long *) start + size; i++) { if ((eresi_Addr) i >= E2DBG_KERNELBASE) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot dump anymore : end of stack", -1); name = revm_resolve(world.curjob->curfile, *i, &off); if (!name) name = "?"; if (off) snprintf(logbuf, BUFSIZ - 1, " " XFMT " " XFMT " <%s + " DFMT "> \n", (eresi_Addr) i, (eresi_Addr) *i, name, off); else snprintf(logbuf, BUFSIZ - 1, " " XFMT " " XFMT " <%s> \n", (eresi_Addr) i, (eresi_Addr) *i, name); e2dbg_output(logbuf); } e2dbg_output("\n"); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/* Add a breakpoint without using a script command */ int e2dbg_breakpoint_add(eresi_Addr addr) { int err; char buf[BUFSIZ]; char *name; elfsh_SAddr off; elfshobj_t *file; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Resolve source file */ file = e2dbg_get_parent_object(addr); if (file == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot resolve parent file for bp", -1); /* Resolve breakpoint address */ name = revm_resolve(file, addr, &off); if (off) snprintf(buf, BUFSIZ, "<%s + " DFMT ">", name, off); else snprintf(buf, BUFSIZ, "<%s>", name); /* Really put the breakpoint */ err = elfsh_bp_add(&e2dbgworld.bp, file, buf, addr); if (err < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot add breakpoint", -1); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/* Resolve a register */ void e2dbg_register_dump(char *name, ureg32 reg) { elfsh_SAddr off; char *str; str = revm_resolve(world.curjob->curfile, reg, &off); if (!off) printf("\t [%s] %08X (%010u) <%s> \n", name, reg, reg, (str ? str : "unknown")); else printf("\t [%s] %08X (%010u) <%s + " UFMT "> \n", name, reg, reg, str, off); }
/** * Symbol resolving handler for libasm. * Runtime compatible * @param data * @param vaddr * @param buf * @param maxlen */ void asm_do_resolve(void *data, eresi_Addr vaddr, char *buf, u_int maxlen) { elfshobj_t *file; elfshsect_t *parent; char *name; elfsh_SAddr off; int len; char *sep; uint32_t addr; /* Retreive the nearest symbol */ file = data; parent = elfsh_get_parent_section(file, vaddr, 0); if (NULL != parent && parent->shdr->sh_addr) name = revm_resolve(file, vaddr, &off); else name = NULL; /* Print the symbol name in 'buf' so that libasm can print it */ len = (NULL == name ? 10 : strlen(name) + 25); if (name != NULL && *name != 0x00) { sep = (off > 0 ? " + " : off < 0 ? " - " : ""); len = snprintf(buf, maxlen - 1, "%s%s%s%s", revm_colorfieldstr("<"), revm_colortypestr(name), (off ? revm_colorfieldstr(sep) : ""), (off ? "" : revm_colorfieldstr(">"))); if (off) snprintf(buf + len, maxlen - len - 1, "%s%s", revm_colornumber("%u", (u_int) off), revm_colorfieldstr(">")); } /* We currently only disassemble architecture with 32bits address space, even when 64b proc */ else { addr = (uint32_t) vaddr; snprintf(buf, maxlen, "0x%X", addr); } }
/** * @param str * @param bp * @param pc * @param parent * @return */ void bpdebug(char *str, elfshbp_t *bp, eresi_Addr pc, elfshobj_t *parent) { eresi_Addr addr; int off; char *name; elfshsect_t *sect; elfsh_Sym *sym; off = (bp ? 0 : e2dbgworld.stoppedthread->count == E2DBG_BREAK_HIT ? 3 : 6); addr = (bp ? bp->addr : pc - off); fprintf(stderr, "%s (PC = %08X) ::: parent = %s (BP DESCRIPTOR = %08X) \n", str, pc, (parent ? parent->name : "NO PARENT NAME"), (eresi_Addr) bp); if (!bp) return; sect = elfsh_get_parent_section(parent, addr, NULL); name = revm_resolve(parent, addr, &off); sym = elfsh_get_metasym_by_value(parent, addr, &off, ELFSH_LOWSYM); revm_object_display(sect, sym, 16, off, 0, addr, name, REVM_VIEW_DISASM, 0, 0); }
/** * The Real routine that handles each thread-specific breakpoint state machine. * * breakpoint case: * * 0-1: restore old instr, enable step, set bp addr in ->past, reset pc to orig bp addr * 1-2: do nothing (just executed instr) * 2-3: reinstall, resetstep, thread_contall, curthread->was_step = 0, * stoppedthread->count = NONE; e2dbgworld.curbp = NULL; stoppedthread = RUNNING * * stepping case: * * 0-1: print current instr; reinstall * stoppedthread->state = BREAKING; thread_contall; * was_step = 0; count = NONE; curbp = NULL; stoppedthread->state = RUNNING */ void e2dbg_breakpoint_process() { char buf[32]; elfshbp_t *bp; int prot; char *name; elfsh_SAddr off; int ret; asm_instr ptr; char *s; eresi_Addr *pc; eresi_Addr savedpc; u_int bpsz; elfshsect_t *sect; elfshobj_t *parent; elfsh_Sym *sym; #if __DEBUG_BP__ fprintf(stderr, " [D] Entering breakpoint handler\n"); #endif /* Set all registers as variables and get PC */ e2dbg_user_hooks_install(); e2dbg_getregs(); pc = e2dbg_getpc(); parent = e2dbg_get_parent_object((eresi_Addr) *pc); bpsz = elfsh_get_breaksize(parent); /* Print variables and registers on breakpoints */ //if (!world.state.revm_quiet) //cmd_vlist(); /* Try to find the breakpoint at current instruction pointer */ #if __DEBUG_BP__ fprintf(stderr, " [D] At PC = %08X : Trying to find breakpoint at addr %08X (bpsize = %u)\n", *pc, *pc - bpsz, bpsz); #endif snprintf(buf, sizeof(buf), XFMT, *pc - bpsz); bp = hash_get(&e2dbgworld.bp, buf); #if __DEBUG_BP__ if (bp) fprintf(stderr, " [D] Saved instruction BYTE = %02X and PC-BPSZ BYTE = %02X \n", bp->savedinstr[0], *((u_char *) *pc - bpsz)); else fprintf(stderr, " [D] No BP found at %08X ! \n", *pc); #endif /* Case of processing breakpoint, or if we are single-stepping */ if (!bp || (bp->savedinstr[0] == *((u_char *) *pc - bpsz))) { /* We are single-stepping, display the instruction at $pc */ if (e2dbgworld.stoppedthread->step) { #if __DEBUG_BP__ fprintf(stderr, " [D] Single-stepping -IS- enabled \n"); #endif ret = asm_read_instr(&ptr, (u_char *) *pc, 16, world.curjob->proc); if (!ret) ret++; sect = elfsh_get_parent_section(parent, (eresi_Addr) *pc, NULL); name = revm_resolve(parent, (eresi_Addr) *pc, &off); off = 0; sym = elfsh_get_metasym_by_value(parent, (eresi_Addr) *pc, &off, ELFSH_LOWSYM); #if __DEBUG_BP__ fprintf(stderr, " [D] Found parent = %08X (%s) in step (name = %s, parentsect = %s) off = %u\n", (eresi_Addr) parent, parent->name, name, sect->name, off); #endif revm_instr_display(-1, *pc, 0, 20, name, off, (char *) *pc); e2dbg_display(e2dbgworld.displaycmd, e2dbgworld.displaynbr); if (!e2dbgworld.stoppedthread->trace) e2dbg_entry(NULL); else e2dbg_watch(); e2dbg_breakpoint_reinstall(); return; } #if __DEBUG_BP__ fprintf(stderr, " [D] Single-stepping is -NOT- enabled \n"); #endif /* Here starts the real stuff ** ** count == E2DBG_BREAK_EXEC -> execute restored instruction ** count == E2DBG_BREAK_FINISHED -> restore breakpoint ** count > E2DBG_BREAK_MAX -> e2dbg is getting debugged by a third party debugger */ e2dbgworld.stoppedthread->count++; #if __DEBUG_BP__ fprintf(stderr, " [C] Count %u -> %u for thread ID %u \n", e2dbgworld.stoppedthread->count - 1, e2dbgworld.stoppedthread->count, ((unsigned int) e2dbgworld.stoppedthread->tid)); #endif /* execute the previously restored instruction */ if (e2dbgworld.stoppedthread->count == E2DBG_BREAK_EXEC) { #if __DEBUG_BP__ printf(" [D] At PC = 0x%X : Debuggee executed restored instruction \n", *pc); #endif return; } /* Suggested by andrewg, useful when debugging valgrind */ if (e2dbgworld.stoppedthread->count > E2DBG_BREAK_MAX) { printf(".::- E2DBG WARNING -::.\n" "Breakpoint triggered at location " AFMT " which we don't know about.\n\n" "This may be an anti-debug trick or the program may be inside another\n" "debugger. Exiting (DEBUG: count = " UFMT ", step is off)\n\n", *pc - bpsz, e2dbgworld.stoppedthread->count); return; } e2dbg_breakpoint_reinstall(); /* remove trace flag */ #if __DEBUG_BP__ fprintf(stderr, " [D] At PC = " AFMT " Resetting STEP mode\n", *pc); #endif e2dbg_resetstep(); return; } /* Case of newly hit breakpoint */ else { name = revm_resolve(parent, (eresi_Addr) *pc - bpsz, &off); s = (e2dbg_is_watchpoint(bp) ? "Watch" : "Break"); #if __DEBUG_BP__ fprintf(stderr, " [C] Count set to 1 (HIT) for thread ID %u \n", (unsigned int) e2dbgworld.stoppedthread->tid); #endif if (off) printf(" [*] %spoint found at " XFMT " <%s + " DFMT "> in thread %u \n\n", s, *pc - bpsz, name, off, (unsigned int) e2dbgworld.stoppedthread->tid); else printf(" [*] %spoint found at " XFMT " <%s> in thread %u \n\n", s, *pc - bpsz, name, (unsigned int) e2dbgworld.stoppedthread->tid); revm_doswitch(parent->id); mjr_set_current_context(&world.mjr_session, parent->name); *pc -= bpsz; prot = elfsh_munprotect(bp->obj, *pc, bpsz); memcpy((u_char *) *pc, bp->savedinstr, bpsz); elfsh_mprotect(bp->obj, *pc, bpsz, prot); e2dbg_setstep(); #if __DEBUG_BP__ fprintf(stderr, " [D] Setting Step mode \n"); #endif e2dbgworld.stoppedthread->past = *pc; e2dbgworld.stoppedthread->count = E2DBG_BREAK_HIT; e2dbgworld.curbp = bp; bp->tid = (uint32_t) e2dbgworld.stoppedthread->tid; #if __DEBUG_BP__ fprintf(stderr, " [D] Reset break \n"); #endif if (bp->cmdnbr) e2dbg_display(bp->cmd, bp->cmdnbr); else e2dbg_display(e2dbgworld.displaycmd, e2dbgworld.displaynbr); #if __DEBUG_BP__ fprintf(stderr, " [D] After BP display \n"); #endif #if __DEBUG_BP__ fprintf(stderr, " [D] PC before entry is addr 0x%X \n", *pc); #endif savedpc = *pc; e2dbg_entry(NULL); *pc = savedpc; #if __DEBUG_BP__ fprintf(stderr, " [D] At PC = 0x%X : returned from BP handler with step enabled\n", *pc); #endif } }
/* Breakpoint command */ int cmd_bp() { char *str; int ret; eresi_Addr addr; char logbuf[BUFSIZ]; int idx; int index; elfsh_SAddr off = 0; char *name; elfshbp_t *cur; char **keys; int keynbr; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* build argc */ for (idx = 0; world.curjob->curcmd->param[idx] != NULL; idx++); str = revm_lookup_string(world.curjob->curcmd->param[0]); /* Select subcommand */ switch (idx) { /* List breakpoints */ case 0: e2dbg_output(" .:: Breakpoints ::.\n\n"); keys = hash_get_keys(&e2dbgworld.bp, &keynbr); for (index = 0; index < keynbr; index++) { cur = hash_get(&e2dbgworld.bp, keys[index]); name = revm_resolve(world.curjob->curfile, (eresi_Addr) cur->addr, &off); if (off) snprintf(logbuf, BUFSIZ, " %c [%02u] " XFMT " <%s + " UFMT ">\n", (e2dbg_is_watchpoint(cur) ? 'W' : 'B'), cur->id, cur->addr, name, off); else snprintf(logbuf, BUFSIZ, " %c [%02u] " XFMT " <%s>\n", (e2dbg_is_watchpoint(cur) ? 'W' : 'B'), cur->id, cur->addr, name); e2dbg_output(logbuf); } hash_free_keys(keys); if (!index) e2dbg_output(" [*] No breakpoints\n"); e2dbg_output("\n"); break; /* Supply a new breakpoint */ case 1: if (!elfsh_is_runtime_mode()) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Not in dynamic or debugger mode", -1); if (!str || !(*str)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid argument", -1); /* Break on a supplied virtual address */ if (IS_VADDR(str)) { if (sscanf(str + 2, AFMT, &addr) != 1) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid virtual address requested", (-1)); } /* Resolve first a function name */ else { addr = e2dbg_breakpoint_find_addr(str); if (addr == 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Requested symbol address unknown", -1); } /* Add the breakpoint */ ret = e2dbg_breakpoint_add(addr); if (ret < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Breakpoint insertion failed\n", (-1)); if (ret >= 0) { name = revm_resolve(world.curjob->curfile, addr, &off); if (!off) snprintf(logbuf, BUFSIZ - 1, " [*] Breakpoint added at <%s> (" XFMT ")\n\n", name, addr); else snprintf(logbuf, BUFSIZ - 1, " [*] Breakpoint added at <%s + " UFMT "> (" XFMT ")\n\n", name, off, addr); e2dbg_output(logbuf); } break; /* Wrong command syntax */ default: PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Wrong arg number", (-1)); } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (ret)); }