static dr_emit_flags_t event_app_analysis(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating, OUT void **user_data) { instr_t *inst, *label; bool prev_was_mov_const = false; ptr_int_t val1, val2; *user_data = NULL; /* Look for duplicate mov immediates telling us which subtest we're in */ for (inst = instrlist_first_app(bb); inst != NULL; inst = instr_get_next_app(inst)) { if (instr_is_mov_constant(inst, prev_was_mov_const ? &val2 : &val1)) { if (prev_was_mov_const && val1 == val2 && val1 != 0 && /* rule out xor w/ self */ opnd_is_reg(instr_get_dst(inst, 0)) && opnd_get_reg(instr_get_dst(inst, 0)) == TEST_REG) { *user_data = (void *) val1; label = INSTR_CREATE_label(drcontext); instr_set_translation(label, instr_get_app_pc(inst)); instrlist_meta_postinsert(bb, inst, label); } else prev_was_mov_const = true; } else prev_was_mov_const = false; } return DR_EMIT_DEFAULT; }
/* returns false on failure */ static bool decode_function(void *dcontext, byte *entry) { byte *pc, *pre_pc; int num_instr = 0; bool found_ret = false; instr_t *instr; if (entry == NULL) return false; instr = instr_create(dcontext); pc = entry; while (true) { instr_reset(dcontext, instr); pre_pc = pc; pc = decode(dcontext, pc, instr); instr_set_translation(instr, pre_pc); dr_print_instr(dcontext, STDOUT, instr, ""); if (instr_is_return(instr)) { found_ret = true; break; } num_instr++; if (num_instr > MAX_INSTRS_IN_FUNCTION) { print("ERROR: hit max instr limit %d\n", MAX_INSTRS_IN_FUNCTION); break; } } instr_destroy(dcontext, instr); return found_ret; }
static dr_emit_flags_t bb_event(void* drcontext, void *tag, instrlist_t* bb, bool for_trace, bool translating) { instr_t *instr; instr_t *next_instr; reg_t in_eax = -1; for (instr = instrlist_first(bb); instr != NULL; instr = next_instr) { next_instr = instr_get_next(instr); if (instr_get_opcode(instr) == OP_mov_imm && opnd_get_reg(instr_get_dst(instr, 0)) == REG_EAX) in_eax = opnd_get_immed_int(instr_get_src(instr, 0)); if (instr_is_syscall(instr) && in_eax == SYS_getpid) { instr_t *myval = INSTR_CREATE_mov_imm (drcontext, opnd_create_reg(REG_EAX), OPND_CREATE_INT32(-7)); instr_set_translation(myval, instr_get_app_pc(instr)); instrlist_preinsert(bb, instr, myval); instrlist_remove(bb, instr); instr_destroy(drcontext, instr); } } return DR_EMIT_DEFAULT; }
static inline void check_translation(instrlist_t *ilist, instr_t *inst) { if (ilist->translation_target != NULL && instr_get_translation(inst) == NULL) { instr_set_translation(inst, ilist->translation_target); } if (instrlist_get_our_mangling(ilist)) instr_set_our_mangling(inst, true); }
static void look_for_usercall(void *dcontext, byte *entry, const char *sym, LOADED_IMAGE *img, const char *modpath) { bool found_push_imm = false; int imm = 0; app_pc pc, pre_pc; instr_t *instr; if (entry == NULL) return; instr = instr_create(dcontext); pc = entry; while (true) { instr_reset(dcontext, instr); pre_pc = pc; pc = decode(dcontext, pc, instr); if (verbose) { instr_set_translation(instr, pre_pc); dr_print_instr(dcontext, STDOUT, instr, ""); } if (pc == NULL || !instr_valid(instr)) break; if (instr_get_opcode(instr) == OP_push_imm) { found_push_imm = true; imm = (int) opnd_get_immed_int(instr_get_src(instr, 0)); } else if (instr_is_call_direct(instr) && found_push_imm) { app_pc tgt = opnd_get_pc(instr_get_target(instr)); bool found = false; int i; for (i = 0; i < NUM_USERCALL; i++) { if (tgt == usercall_addr[i]) { dr_printf("Call #0x%02x to %s at %s+0x%x\n", imm, usercall_names[i], sym, pre_pc - entry); found = true; break; } } if (found) break; } else if (instr_is_return(instr)) break; if (pc - entry > MAX_BYTES_BEFORE_USERCALL) break; } instr_destroy(dcontext, instr); }
/* To support multiple non-meta ctis in app2app phase, we mark them meta * before handing to DR to satisfy its bb constraints */ static void drmgr_fix_app_ctis(void *drcontext, instrlist_t *bb) { instr_t *inst; for (inst = instrlist_first(bb); inst != NULL; inst = instr_get_next(inst)) { /* Any CTI with an instr target must have an intra-bb target and thus * we assume it should not be mangled. We mark it meta. */ if (instr_ok_to_mangle(inst) && instr_is_cti(inst) && opnd_is_instr(instr_get_target(inst))) { instr_set_ok_to_mangle(inst, false); /* instrumentation passes should set the translation field * so other passes can see what app pc these app instrs * correspond to: but DR complains if there's a meta instr * w/ a translation but no restore_state event */ instr_set_translation(inst, NULL); } } }
static inline void check_translation(instrlist_t *ilist, instr_t *inst) { if (ilist->translation_target != NULL && instr_get_translation(inst) == NULL) { instr_set_translation(inst, ilist->translation_target); } if (instrlist_get_our_mangling(ilist)) instr_set_our_mangling(inst, true); #if defined(CLIENT_INTERFACE) && defined(ARM) if (instr_is_meta(inst)) { dr_pred_type_t auto_pred = ilist->auto_pred; if (instr_predicate_is_cond(auto_pred)) { CLIENT_ASSERT(!instr_is_cti(inst), "auto-predication does not support cti's"); CLIENT_ASSERT( !TESTANY(EFLAGS_WRITE_NZCV, instr_get_arith_flags(inst, DR_QUERY_INCLUDE_COND_SRCS)), "cannot auto predicate a meta-inst that writes to NZCV"); if (!instr_is_predicated(inst)) instr_set_predicate(inst, auto_pred); } } #endif }
dr_emit_flags_t bb_event(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr, *next_instr; app_pc bb_addr = dr_fragment_app_pc(tag); if (bb_addr == start_pc) { instrument = true; } else if (bb_addr == stop_pc) { instrument = false; } if (!instrument) { return DR_EMIT_DEFAULT; } for (instr = instrlist_first(bb); instr != NULL; instr = next_instr) { next_instr = instr_get_next(instr); /* * Conditional branch. We can determine the target and * fallthrough addresses here, but we need to instrument if we * want to record the edge only if it actually executes at * runtime. Instead of using dr_insert_cbr_instrumentation, * we'll insert separate instrumentation for the taken and not * taken cases and remove it separately after we see each * case. */ if (instr_is_cbr(instr)) { app_pc src = instr_get_app_pc(instr); cbr_state_t state; bool insert_taken, insert_not_taken; /* First look up the state of this branch so we * know what instrumentation to insert, if any. */ elem_t *elem = lookup(table, src); if (elem == NULL) { state = CBR_NONE; insert(table, src, CBR_NONE); } else { state = elem->state; } insert_taken = (state & CBR_TAKEN) == 0; insert_not_taken = (state & CBR_NOT_TAKEN) == 0; if (insert_taken || insert_not_taken) { app_pc fall = (app_pc)decode_next_pc(drcontext, (byte *)src); app_pc targ = instr_get_branch_target_pc(instr); /* * Redirect the cbr to jump to the 'taken' callout. * We'll insert a 'not-taken' callout at fallthrough * address. */ instr_t *label = INSTR_CREATE_label(drcontext); instr_set_meta(instr); instr_set_translation(instr, NULL); /* If this is a short cti, make sure it can reach its new target */ if (instr_is_cti_short(instr)) { /* if jecxz/loop we want to set the target of the long-taken * so set instr to the return value */ instr = instr_convert_short_meta_jmp_to_long(drcontext, bb, instr); } instr_set_target(instr, opnd_create_instr(label)); if (insert_not_taken) { /* * Callout for the not-taken case */ dr_insert_clean_call(drcontext, bb, NULL, (void *)at_not_taken, false /* don't save fp state */, 2 /* 2 args for at_not_taken */, OPND_CREATE_INTPTR((ptr_uint_t)src), OPND_CREATE_INTPTR((ptr_uint_t)fall)); } /* * Jump to the original fall-through address. * (This should not be a meta-instruction). */ instrlist_preinsert( bb, NULL, INSTR_XL8(INSTR_CREATE_jmp(drcontext, opnd_create_pc(fall)), fall)); /* label goes before the 'taken' callout */ MINSERT(bb, NULL, label); if (insert_taken) { /* * Callout for the taken case */ dr_insert_clean_call(drcontext, bb, NULL, (void *)at_taken, false /* don't save fp state */, 2 /* 2 args for at_taken */, OPND_CREATE_INTPTR((ptr_uint_t)src), OPND_CREATE_INTPTR((ptr_uint_t)targ)); } /* * Jump to the original target block (this should * not be a meta-instruction). */ instrlist_preinsert( bb, NULL, INSTR_XL8(INSTR_CREATE_jmp(drcontext, opnd_create_pc(targ)), targ)); } } } /* since our added instrumentation is not constant, we ask to store * translations now */ return DR_EMIT_STORE_TRANSLATIONS; }
std::string raw2trace_t::append_bb_entries(uint tidx, offline_entry_t *in_entry, OUT bool *handled) { uint instr_count = in_entry->pc.instr_count; instr_t instr; trace_entry_t buf_start[MAX_COMBINED_ENTRIES]; app_pc start_pc = modvec[in_entry->pc.modidx].map_base + in_entry->pc.modoffs; app_pc pc, decode_pc = start_pc; if ((in_entry->pc.modidx == 0 && in_entry->pc.modoffs == 0) || modvec[in_entry->pc.modidx].map_base == NULL) { // FIXME i#2062: add support for code not in a module (vsyscall, JIT, etc.). // Once that support is in we can remove the bool return value and handle // the memrefs up here. VPRINT(3, "Skipping ifetch for %u instrs not in a module\n", instr_count); *handled = false; return ""; } else { VPRINT(3, "Appending %u instrs in bb " PFX " in mod %u +" PIFX " = %s\n", instr_count, (ptr_uint_t)start_pc, (uint)in_entry->pc.modidx, (ptr_uint_t)in_entry->pc.modoffs, modvec[in_entry->pc.modidx].path); } bool skip_icache = false; if (instr_count == 0) { // L0 filtering adds a PC entry with a count of 0 prior to each memref. skip_icache = true; instr_count = 1; // We set a flag to avoid peeking forward on instr entries. if (!instrs_are_separate) instrs_are_separate = true; } CHECK(!instrs_are_separate || instr_count == 1, "cannot mix 0-count and >1-count"); instr_init(dcontext, &instr); for (uint i = 0; i < instr_count; ++i) { trace_entry_t *buf = buf_start; app_pc orig_pc = decode_pc - modvec[in_entry->pc.modidx].map_base + modvec[in_entry->pc.modidx].orig_base; bool skip_instr = false; instr_reset(dcontext, &instr); // We assume the default ISA mode and currently require the 32-bit // postprocessor for 32-bit applications. pc = decode(dcontext, decode_pc, &instr); if (pc == NULL || !instr_valid(&instr)) { WARN("Encountered invalid/undecodable instr @ %s+" PFX, modvec[in_entry->pc.modidx].path, (ptr_uint_t)in_entry->pc.modoffs); break; } CHECK(!instr_is_cti(&instr) || i == instr_count - 1, "invalid cti"); if (instr_is_rep_string(&instr)) { // We want it to look like the original rep string instead of the // drutil-expanded loop. if (!prev_instr_was_rep_string) prev_instr_was_rep_string = true; else skip_instr = true; } else prev_instr_was_rep_string = false; // FIXME i#1729: make bundles via lazy accum until hit memref/end. if (!skip_instr) { DO_VERBOSE(3, { instr_set_translation(&instr, orig_pc); dr_print_instr(dcontext, STDOUT, &instr, ""); });
/* replaces inc with add 1, dec with sub 1 * returns true if successful, false if not */ static bool replace_inc_with_add(void *drcontext, instr_t *instr, instrlist_t *trace) { instr_t *in; uint eflags; int opcode = instr_get_opcode(instr); bool ok_to_replace = false; DR_ASSERT(opcode == OP_inc || opcode == OP_dec); #ifdef VERBOSE dr_print_instr(drcontext, STDOUT, instr, "in replace_inc_with_add:\n\t"); #endif /* add/sub writes CF, inc/dec does not, make sure that's ok */ for (in = instr; in != NULL; in = instr_get_next(in)) { eflags = instr_get_eflags(in); if ((eflags & EFLAGS_READ_CF) != 0) { #ifdef VERBOSE dr_print_instr(drcontext, STDOUT, in, "\treads CF => cannot replace inc with add: "); #endif return false; } if (instr_is_exit_cti(in)) { /* to be more sophisticated, examine instructions at * target of exit cti (if it is a direct branch). * for this example, we give up if we hit a branch. */ return false; } /* if writes but doesn't read, ok */ if ((eflags & EFLAGS_WRITE_CF) != 0) { ok_to_replace = true; break; } } if (!ok_to_replace) { #ifdef VERBOSE dr_printf("\tno write to CF => cannot replace inc with add\n"); #endif return false; } if (opcode == OP_inc) { #ifdef VERBOSE dr_printf("\treplacing inc with add\n"); #endif in = INSTR_CREATE_add(drcontext, instr_get_dst(instr, 0), OPND_CREATE_INT8(1)); } else { #ifdef VERBOSE dr_printf("\treplacing dec with sub\n"); #endif in = INSTR_CREATE_sub(drcontext, instr_get_dst(instr, 0), OPND_CREATE_INT8(1)); } if (instr_get_prefix_flag(instr, PREFIX_LOCK)) instr_set_prefix_flag(in, PREFIX_LOCK); instr_set_translation(in, instr_get_app_pc(instr)); instrlist_replace(trace, instr, in); instr_destroy(drcontext, instr); return true; }
/* returns false on failure */ static bool decode_syscall_num(void *dcontext, byte *entry, syscall_info_t *info, LOADED_IMAGE *img) { /* FIXME: would like to fail gracefully rather than have a DR assertion * on non-code! => use DEBUG=0 INTERNAL=1 DR build! */ bool found_syscall = false, found_eax = false, found_edx = false, found_ecx = false; bool found_ret = false; byte *pc, *pre_pc; int num_instr = 0; instr_t *instr; byte *preferred = get_preferred_base(img); if (entry == NULL) return false; info->num_args = -1; /* if find sysnum but not args */ info->sysnum = -1; info->fixup_index = -1; instr = instr_create(dcontext); pc = entry; /* FIXME - we don't support decoding 64bit instructions in 32bit mode, but I want * this to work on 32bit machines. Hack fix based on the wrapper pattern, we skip * the first instruction (mov r10, rcx) here, the rest should decode ok. * Xref PR 236203. */ if (expect_x64 && *pc == 0x4c && *(pc+1) == 0x8b && *(pc+2) == 0xd1) pc += 3; while (true) { instr_reset(dcontext, instr); pre_pc = pc; pc = decode(dcontext, pc, instr); if (verbose) { instr_set_translation(instr, pre_pc); dr_print_instr(dcontext, STDOUT, instr, ""); } if (pc == NULL || !instr_valid(instr)) break; if (instr_is_syscall(instr) || instr_is_call_indirect(instr)) { /* If we see a syscall instr or an indirect call which is not syscall, * we assume this is not a syscall wrapper. */ found_syscall = process_syscall_instr(dcontext, instr, found_eax, found_edx); if (!found_syscall) break; /* assume not a syscall wrapper, give up gracefully */ } else if (instr_is_return(instr)) { /* we must break on return to avoid case like win8 x86 * which has sysenter callee adjacent-"inlined" * ntdll!NtYieldExecution: * 77d7422c b801000000 mov eax,1 * 77d74231 e801000000 call ntdll!NtYieldExecution+0xb (77d74237) * 77d74236 c3 ret * 77d74237 8bd4 mov edx,esp * 77d74239 0f34 sysenter * 77d7423b c3 ret */ if (!found_ret) { process_ret(instr, info); found_ret = true; } break; } else if (instr_get_opcode(instr) == OP_call) { found_syscall = process_syscall_call(dcontext, pc, instr, found_eax, found_edx); /* If we see a call and it is not a sysenter callee, * we assume this is not a syscall wrapper. */ if (!found_syscall) break; /* assume not a syscall wrapper, give up gracefully */ } else if (instr_is_cti(instr)) { /* We expect only ctis like ret or ret imm, syscall, and call, which are * handled above. Give up gracefully if we hit any other cti. * XXX: what about jmp to shared ret (seen in the past on some syscalls)? */ /* Update: win10 TH2 1511 x64 has a cti: * ntdll!NtContinue: * 00007ff9`13185630 4c8bd1 mov r10,rcx * 00007ff9`13185633 b843000000 mov eax,43h * 00007ff9`13185638 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1 * 00007ff9`13185640 7503 jne ntdll!NtContinue+0x15 (00007ff9`13185645) * 00007ff9`13185642 0f05 syscall * 00007ff9`13185644 c3 ret * 00007ff9`13185645 cd2e int 2Eh * 00007ff9`13185647 c3 ret */ if (expect_x64 && instr_is_cbr(instr) && opnd_get_pc(instr_get_target(instr)) == pc + 3/*syscall;ret*/) { /* keep going */ } else break; } else if ((!found_eax || !found_edx || !found_ecx) && instr_get_opcode(instr) == OP_mov_imm && opnd_is_reg(instr_get_dst(instr, 0))) { if (!found_eax && opnd_get_reg(instr_get_dst(instr, 0)) == REG_EAX) { info->sysnum = (int) opnd_get_immed_int(instr_get_src(instr, 0)); found_eax = true; } else if (!found_edx && opnd_get_reg(instr_get_dst(instr, 0)) == REG_EDX) { uint imm = (uint) opnd_get_immed_int(instr_get_src(instr, 0)); if (imm == 0x7ffe0300 || /* On Win10 the immed is ntdll!Wow64SystemServiceCall */ (expect_wow && imm > (ptr_uint_t)preferred && imm < (ptr_uint_t)preferred + img->SizeOfImage)) found_edx = true; } else if (!found_ecx && opnd_get_reg(instr_get_dst(instr, 0)) == REG_ECX) { found_ecx = true; info->fixup_index = (int) opnd_get_immed_int(instr_get_src(instr, 0)); } } else if (instr_get_opcode(instr) == OP_xor && opnd_is_reg(instr_get_src(instr, 0)) && opnd_get_reg(instr_get_src(instr, 0)) == REG_ECX && opnd_is_reg(instr_get_dst(instr, 0)) && opnd_get_reg(instr_get_dst(instr, 0)) == REG_ECX) { /* xor to 0 */ found_ecx = true; info->fixup_index = 0; } num_instr++; if (num_instr > MAX_INSTRS_BEFORE_SYSCALL) /* wrappers should be short! */ break; /* avoid weird cases like NPXEMULATORTABLE */ } instr_destroy(dcontext, instr); return found_syscall; }
/** Combines instr_set_translation and instrlist_meta_preinsert calls. */ instr_t* prexl8(instrlist_t* frag, instr_t* where, instr_t* instr, app_pc pc) { instr_set_translation(instr, pc); instrlist_meta_preinsert(frag, where, instr); return instr; }