Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
/* 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;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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);
}
Ejemplo n.º 5
0
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);
}
Ejemplo n.º 6
0
/* 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);
        }
    }
}
Ejemplo n.º 7
0
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
}
Ejemplo n.º 8
0
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;
}
Ejemplo n.º 9
0
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, "");
            });
Ejemplo n.º 10
0
/* 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;
}
Ejemplo n.º 11
0
/* 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;
}
Ejemplo n.º 12
0
/** 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;
}