void dump_scopetable_entry (struct MemoryCache *mc, struct process *p, int i, address a) { bool b; struct my_SCOPETABLE_ENTRY e; b=MC_ReadBuffer (mc, a, sizeof(struct my_SCOPETABLE_ENTRY), (BYTE*)&e); if (b==false) { L ("%s() cannot read scopetable entry\n", __FUNCTION__); return; }; strbuf sb2=STRBUF_INIT; process_get_sym (p, e.handler, true /* add_module_name */, true /* add_offset */, &sb2); if (e.filter) { strbuf sb1=STRBUF_INIT; process_get_sym (p, e.filter, true /* add_module_name */, true /* add_offset */, &sb1); L ("scopetable entry[%d]. previous try level=%d, filter=0x" PRI_ADR_HEX " (%s) handler=0x" PRI_ADR_HEX " (%s)\n", i, e.previousTryLevel, e.filter, sb1.buf, e.handler, sb2.buf); strbuf_deinit(&sb1); } else L ("scopetable entry[%d]. previous try level=%d, finally=0x" PRI_ADR_HEX " (%s)\n", i, e.previousTryLevel, e.handler, sb2.buf); strbuf_deinit(&sb2); };
void enum_files_in_dir(const char* path, callback_fn cb, void *param) { assert(path); #ifdef _WIN32 strbuf tmp=STRBUF_INIT; strbuf_addf (&tmp, "%s\\*", path); struct _finddata_t d; int fh=_findfirst(tmp.buf, &d); assert(fh!=-1); strbuf tmp2=STRBUF_INIT; do { if (strcasecmp(d.name, ".")==0) continue; strbuf_reinit(&tmp2, 0); strbuf_addf (&tmp2, "%s\\%s", path, d.name); cb (d.name, tmp2.buf, d.size, d.time_write, IS_SET(d.attrib, _A_SUBDIR), param); } while (_findnext(fh, &d)==0); _findclose (fh); strbuf_deinit(&tmp2); strbuf_deinit (&tmp); #elif defined(__linux__) || defined(__CYGWIN__) || defined (__APPLE__) DIR* DIR_V = opendir(path); struct dirent* t = NULL; if (DIR_V==NULL) die ("opendir(%s) failed: %s\n", path, strerror(errno)); strbuf tmp2=STRBUF_INIT; while ((t=readdir(DIR_V))!=NULL) { if (strcasecmp(t->d_name, ".")==0) continue; strbuf_reinit(&tmp2, 0); strbuf_addf (&tmp2, "%s/%s", path, t->d_name); struct stat st; if (stat (tmp2.buf, &st)==-1) die ("stat(%s) failed: %s\n", tmp2.buf, strerror(errno)); // conflict with c99... #ifndef DT_DIR #define DT_DIR 4 #endif cb (t->d_name, tmp2.buf, st.st_size, st.st_mtime, IS_SET(t->d_type, DT_DIR), param); }; strbuf_deinit(&tmp2); closedir(DIR_V); #else #warning "undetermined compiler" #endif };
void add_symbol (address a, char *name, add_symbol_params *params) { module *m=params->m; rbtree *symtbl=m->symbols; oassert(symtbl && "symbols=NULL in module"); MemoryCache *mc=params->mc; if (one_time_int3_bp_re && params->t==SYM_TYPE_PE_EXPORT && module_adr_in_executable_section (m, a)) { strbuf sb=STRBUF_INIT; strbuf_addstr (&sb, get_module_name(m)); strbuf_addc (&sb, '!'); strbuf_addstr (&sb, name); if (regexec (one_time_int3_bp_re, sb.buf, 0, NULL, 0)==0) set_onetime_INT3_BP(a, params->p, m, name, mc); strbuf_deinit (&sb); }; if (dump_seh && string_is_ends_with (name, "security_cookie")) { m->security_cookie_adr=a; m->security_cookie_adr_known=true; if (symbol_c_debug) L ("%s() got address of security_cookie (0x" PRI_REG_HEX ") for %s!%s\n", __FUNCTION__, a, get_module_name(m), name); }; bool dump_symbol=false; if (dump_all_symbols_re) { strbuf sb=STRBUF_INIT; strbuf_addstr (&sb, get_module_name(m)); strbuf_addc (&sb, '!'); strbuf_addstr (&sb, name); if (regexec (dump_all_symbols_re, sb.buf, 0, NULL, 0)==0) dump_symbol=true; strbuf_deinit (&sb); }; if (dump_symbol || (dump_all_symbols_re==NULL && dump_all_symbols)) { dump_PID_if_need(params->p); L("New symbol. Module=[%s], address=[0x" PRI_ADR_HEX "], name=[%s]\n", get_module_name(m), a, name); }; symbol *new_sym=create_symbol(params->t, name); symbol *first_sym=(symbol*)rbtree_lookup(symtbl, (void*)a); if (first_sym) new_sym->next=first_sym; // insert at beginning of list rbtree_insert(symtbl, (void*)a, (void*)new_sym); };
static void dump_stack_not_using_EBP (process *p, thread *t, CONTEXT *ctx, MemoryCache *mc) { HANDLE THDL=t->THDL; address stack_top=TIB_get_stack_top (THDL, mc); address stack_bottom=TIB_get_stack_bottom (THDL, mc); //strbuf sb=STRBUF_INIT; address SP_at_start=CONTEXT_get_SP(ctx); L ("Call stack:\n"); if (thread_c_debug) L ("SP_at_start=0x%x, stack_top=0x%x, stack_bottom=0x%x\n", SP_at_start, stack_top, stack_bottom); for (address a=SP_at_start; a<stack_top; a=a+sizeof(REG)) { REG r; if (MC_ReadREG(mc, a, &r)) { //L ("r=0x" PRI_REG_HEX "\n", r); if (adr_in_executable_section(p, r)) { strbuf sb=STRBUF_INIT; process_get_sym (p, r, true, true, &sb); L ("(SP+0x%x) return address=0x" PRI_ADR_HEX " (%s)\n", a-SP_at_start, r, sb.buf); strbuf_deinit(&sb); }; }; }; };
static void dump_stack_EBP_frame (process *p, thread *t, CONTEXT * ctx, MemoryCache *mem) { HANDLE THDL=t->THDL; address stack_top=TIB_get_stack_top (THDL, mem); address stack_bottom=TIB_get_stack_bottom (THDL, mem); strbuf sb=STRBUF_INIT; L ("Call stack:\n"); address next_BP=CONTEXT_get_BP (ctx); if (TIB_is_ptr_in_stack_limits (THDL, next_BP, mem)==false) { L ("Current frame pointer %s (0x" PRI_ADR_HEX ") is not within current stack limits (top=0x" PRI_ADR_HEX ", bottom=0x" PRI_ADR_HEX ")\n", BP_REGISTER_NAME, CONTEXT_get_BP (ctx), stack_top, stack_bottom); goto exit; }; while (next_BP<=stack_top && next_BP>=stack_bottom) { REG tmp; bool b=MC_ReadREG(mem, next_BP, &tmp); oassert (b); address ret_adr; b=MC_ReadREG(mem, next_BP+sizeof(REG), &ret_adr); oassert (b); if (ret_adr==0) break; strbuf_reinit(&sb, 0); process_get_sym (p, ret_adr, true, true, &sb); L ("return address=0x" PRI_ADR_HEX " (%s)\n", ret_adr, sb.buf); if (next_BP==tmp) break; next_BP=tmp; }; exit: strbuf_deinit (&sb); };
void dump_buf_as_array_of_strings(MemoryCache *mc, address a, size_t size) { strbuf sb=STRBUF_INIT; BYTE *buf=DMALLOC (BYTE, size, "BYTE*"); if (MC_ReadBuffer (mc, a, size, buf)==false) goto exit; for (unsigned i=0; i<size; i+=sizeof(REG)) { address a2=*(REG*)&buf[i]; if (MC_get_any_string(mc, a2, &sb)) { L ("0x" PRI_ADR_HEX "+0x%x: ptr to %s\n", a, i, sb.buf); strbuf_reinit(&sb, 0); }; }; exit: DFREE (buf); strbuf_deinit(&sb); };
// replace %substring% to environment variable, if possible void env_vars_expansion(strbuf *sb, char** env) { oassert(env!=NULL); for (int i=0; env[i]; i++) { char *s=DSTRDUP (env[i], "env"); char *s1=strtok (s, "="); char *s2=strtok (NULL, "="); strbuf percented_env_var=STRBUF_INIT; strbuf_addc (&percented_env_var, '%'); strbuf_addstr (&percented_env_var, s1); strbuf_addc (&percented_env_var, '%'); strbuf_replace_if_possible (sb, percented_env_var.buf, s2); strbuf_deinit(&percented_env_var); DFREE(s); }; };
// FIXME: find another place for this function void set_or_update_DRx_breakpoint(BP *bp, CONTEXT *ctx, unsigned DRx_no) { oassert (bp->a->resolved); if (utils_c_debug) { strbuf sb=STRBUF_INIT; address_to_string(bp->a, &sb); L ("%s(): begin. setting DRx-breakpoint %d for %s at 0x" PRI_ADR_HEX "\n", __func__, DRx_no, sb.buf, bp->a->abs_address); strbuf_deinit (&sb); }; if (bp->t==BP_type_BPF || bp->t==BP_type_BPX) CONTEXT_setDRx_and_DR7 (ctx, DRx_no, bp->a->abs_address); else if (bp->t==BP_type_BPM) BPM_set_or_update_DRx_breakpoint(bp->u.bpm, bp->a->abs_address, DRx_no, ctx); else { oassert(0); fatal_error(); }; if (utils_c_debug) L ("%s() end\n", __func__); };
// returns address of next SEH frame address dump_SEH_frame (fds* s, struct process* p, struct thread* t, struct MemoryCache *mc, address a) { struct my_EXCEPTION_REGISTRATION current_SEH_frame; bool b; b=MC_ReadBuffer (mc, a, sizeof(struct my_EXCEPTION_REGISTRATION), (BYTE*)¤t_SEH_frame); if (b==false) { L ("%s() cannot read current SEH frame\n", __FUNCTION__); return REG_MAX; }; strbuf sb=STRBUF_INIT; process_get_sym (p, current_SEH_frame.handler, true /* add_module_name */, true /* add_offset */, &sb); L ("* SEH frame at 0x" PRI_ADR_HEX " prev=0x" PRI_ADR_HEX " handler=0x" PRI_ADR_HEX " (%s)\n", a, current_SEH_frame.prev, current_SEH_frame.handler, sb.buf); bool SEH3=false, SEH4=false; if (string_is_ends_with (sb.buf, "except_handler3")) SEH3=true; REG security_cookie; bool security_cookie_known=false; if (string_is_ends_with (sb.buf, "except_handler4")) { SEH4=true; struct module *m=find_module_for_address(p, current_SEH_frame.handler); if (m->security_cookie_adr_known) { b=MC_ReadREG (mc, m->security_cookie_adr, &security_cookie); if (b==false) L ("%s() can't read security_cookie at 0x" PRI_ADR_HEX " for %s\n", __FUNCTION__, m->security_cookie_adr, get_module_name (m)); else security_cookie_known=true; } else { L ("SEH4 frame is here, but address of security_cookie is not known\n"); L ("Try to place .PDB or .MAP file here with this symbol in it\n"); }; }; if (SEH3==false && SEH4==false) goto exit; struct my_VC_EXCEPTION_REGISTRATION_RECORD current_SEH3_frame; b=MC_ReadBuffer (mc, a, sizeof(struct my_VC_EXCEPTION_REGISTRATION_RECORD), (BYTE*)¤t_SEH3_frame); if (b==false) { L ("%s() cannot read current SEH3/4 frame\n", __FUNCTION__); return REG_MAX; }; int previous_trylevel=current_SEH3_frame.previous_trylevel; L ("SEH%d frame. previous trylevel=%d\n", SEH3 ? 3 : 4, previous_trylevel); address scopetable_address=current_SEH3_frame.scopetable; if (SEH4 && security_cookie_known) { scopetable_address^=security_cookie; struct my_EH4_SCOPETABLE_HEADER EH4_header; b=MC_ReadBuffer (mc, scopetable_address, sizeof(struct my_EH4_SCOPETABLE_HEADER), (BYTE*)&EH4_header); if (b==false) { L ("%s() cannot read current SEH4 frame header. scopetable_address=0x" PRI_ADR_HEX "\n", __FUNCTION__, scopetable_address); return REG_MAX; }; L ("SEH4 header:\tGSCookieOffset=0x%x GSCookieXOROffset=0x%x\n", EH4_header.GSCookieOffset, EH4_header.GSCookieXOROffset); L ("\t\tEHCookieOffset=0x%x EHCookieXOROffset=0x%x\n", EH4_header.EHCookieOffset, EH4_header.EHCookieXOROffset); unsigned adr_of_EBP=a + ((byte*)¤t_SEH3_frame.EBP - (byte*)¤t_SEH3_frame.prev); if (EH4_header.EHCookieOffset!=-2) check_SEH4_cookie (mc, adr_of_EBP, EH4_header.EHCookieOffset, EH4_header.EHCookieXOROffset, security_cookie, "EH"); if (EH4_header.GSCookieOffset!=-2) check_SEH4_cookie (mc, adr_of_EBP, EH4_header.GSCookieOffset, EH4_header.GSCookieXOROffset, security_cookie, "GS"); scopetable_address+=sizeof(struct my_EH4_SCOPETABLE_HEADER); }; if (previous_trylevel>=0 && (SEH3 || (SEH4 && security_cookie_known))) dump_scopetable(mc, p, scopetable_address, previous_trylevel+1); exit: strbuf_deinit (&sb); return current_SEH_frame.prev; };
void handle_BPM(process *p, thread *t, int bp_no, CONTEXT *ctx, MemoryCache *mc) { BP *bp=breakpoints[bp_no]; BPM *bpm=bp->u.bpm; address a=bp->a->abs_address; bool b; REG val; const char* v_type; switch (bpm->width) { case 1: { v_type="BYTE"; byte tmp; b=MC_ReadByte (mc, a, &tmp); oassert(b); val=tmp; }; break; case 2: { v_type="WORD"; wyde tmp; b=MC_ReadWyde (mc, a, &tmp); oassert(b); val=tmp; }; break; case 4: { v_type="DWORD"; DWORD tmp; b=MC_ReadTetrabyte (mc, a, &tmp); oassert(b); val=tmp; }; break; case 8: { #ifdef _WIN64 v_type="QWORD"; DWORD64 tmp; b=MC_ReadOctabyte (mc, a, &tmp); oassert(b); val=tmp; #else oassert (0); #endif }; break; default: oassert(0); fatal_error(); }; strbuf sb_sym=STRBUF_INIT, sb_bp_adr=STRBUF_INIT; address PC=CONTEXT_get_PC(ctx); process_get_sym (p, PC, true, true, &sb_sym); address_to_string(bp->a, &sb_bp_adr); dump_PID_if_need(p); dump_TID_if_need(p, t); L ("(%d) some code at %s (0x" PRI_ADR_HEX ") %s %s variable at %s (it contain 0x" PRI_REG_HEX " after execution of that instruction)\n", bp_no, sb_sym.buf, PC, bpm->t==BPM_type_RW ? "reading or writting" : "writting", v_type, sb_bp_adr.buf, val); //MC_WriteOctabyte(mc, bp->a->abs_address, 0); // can be option? strbuf_deinit(&sb_bp_adr); strbuf_deinit(&sb_sym); };
void strbuf_reinit(strbuf *sb, size_t size) { strbuf_deinit (sb); strbuf_init (sb, size); };