/* emits the instruction to buf (for tests that wish to do additional checks on * the output) */ static void test_instr_encode_and_decode(void *dc, instr_t *instr, uint len_expect, /* also checks one operand's size */ bool src, uint opnum, opnd_size_t sz, uint bytes) { opnd_t op; opnd_size_t opsz; instr_t *decin; uint len; byte *pc = instr_encode(dc, instr, buf); len = (int) (pc - (byte *)buf); #if VERBOSE disassemble_with_info(dc, buf, STDOUT, true, true); #endif ASSERT(len == len_expect); decin = instr_create(dc); decode(dc, buf, decin); ASSERT(instr_same(instr, decin)); /* PR 245805: variable sizes should be resolved on decode */ if (src) op = instr_get_src(decin, opnum); else op = instr_get_dst(decin, opnum); opsz = opnd_get_size(op); ASSERT(opsz == sz && opnd_size_in_bytes(opsz) == bytes); instr_destroy(dc, instr); instr_destroy(dc, decin); }
/* PR 332254: test xchg vs nop */ static void test_nop_xchg(void *dc) { /* 0x0000000000671460 87 c0 xchg %eax %eax -> %eax %eax * 0x0000000000671460 48 87 c0 xchg %rax %rax -> %rax %rax * 0x0000000000671460 41 87 c0 xchg %r8d %eax -> %r8d %eax * 0x0000000000671460 46 90 nop * 0x0000000000671460 4e 90 nop * 0x0000000000671460 41 90 xchg %r8d %eax -> %r8d %eax */ instr_t *instr; instr = INSTR_CREATE_xchg(dc, opnd_create_reg(REG_EAX), opnd_create_reg(REG_EAX)); test_instr_encode(dc, instr, 2); #ifdef X64 /* we don't do the optimal "48 90" instead of "48 87 c0" */ instr = INSTR_CREATE_xchg(dc, opnd_create_reg(REG_RAX), opnd_create_reg(REG_RAX)); test_instr_encode(dc, instr, 3); /* we don't do the optimal "41 90" instead of "41 87 c0" */ instr = INSTR_CREATE_xchg(dc, opnd_create_reg(REG_R8D), opnd_create_reg(REG_EAX)); test_instr_encode(dc, instr, 3); /* ensure we treat as nop and NOT xchg if doesn't have rex.b */ buf[0] = 0x46; buf[1] = 0x90; instr = instr_create(dc); # if VERBOSE disassemble_with_info(dc, buf, STDOUT, true, true); # endif decode(dc, buf, instr); ASSERT(instr_get_opcode(instr) == OP_nop); instr_destroy(dc, instr); buf[0] = 0x4e; buf[1] = 0x90; instr = instr_create(dc); # if VERBOSE disassemble_with_info(dc, buf, STDOUT, true, true); # endif decode(dc, buf, instr); ASSERT(instr_get_opcode(instr) == OP_nop); instr_destroy(dc, instr); buf[0] = 0x41; buf[1] = 0x90; instr = instr_create(dc); # if VERBOSE disassemble_with_info(dc, buf, STDOUT, true, true); # endif decode(dc, buf, instr); ASSERT(instr_get_opcode(instr) == OP_xchg); instr_destroy(dc, instr); #endif }
/* 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; }
void memory_write(void *pc, void *prev_pc) { void *drcontext = dr_get_current_drcontext(); instr_t *instr = instr_create(drcontext); dr_mcontext_t mctx; opnd_t dst; ctx_t ctx; pc = dr_app_pc_for_decoding(pc); mctx.flags = DR_MC_CONTROL|DR_MC_INTEGER; mctx.size = sizeof(mctx); dr_get_mcontext(drcontext, &mctx); instr_init(drcontext, instr); if (!decode(drcontext, pc, instr)) { dr_printf("Decode of instruction at %p failed\n", pc); return; } ctx.addr = prev_pc; ctx.dr_addr = prev_pc; for (int i = 0; i < instr_num_dsts(instr); i++) { dst = instr_get_dst(instr, i); check_opnd(dst, pc, 0, drcontext, &mctx, &ctx); } instr_destroy(drcontext, instr); }
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 void test_disp_control_helper(void *dc, int disp, bool encode_zero_disp, bool force_full_disp, bool disp16, uint len_expect) { byte *pc; uint len; instr_t *instr = INSTR_CREATE_mov_ld (dc, opnd_create_reg(REG_ECX), opnd_create_base_disp_ex(disp16 ? IF_X64_ELSE(REG_EBX, REG_BX) : REG_XBX, REG_NULL, 0, disp, OPSZ_4, encode_zero_disp, force_full_disp, disp16)); pc = instr_encode(dc, instr, buf); len = (int) (pc - (byte *)buf); #if VERBOSE pc = disassemble_with_info(dc, buf, STDOUT, true, true); #endif ASSERT(len == len_expect); instr_reset(dc, instr); decode(dc, buf, instr); ASSERT(instr_num_srcs(instr) == 1 && opnd_is_base_disp(instr_get_src(instr, 0)) && BOOLS_MATCH(encode_zero_disp, opnd_is_disp_encode_zero(instr_get_src(instr, 0))) && BOOLS_MATCH(force_full_disp, opnd_is_disp_force_full(instr_get_src(instr, 0))) && BOOLS_MATCH(disp16, opnd_is_disp_short_addr(instr_get_src(instr, 0)))); instr_destroy(dc, instr); }
void instrace_thread_exit(void *drcontext) { per_thread_t *data; int i; if (client_arg->instrace_mode == INS_TRACE){ ins_trace(drcontext); } data = drmgr_get_tls_field(drcontext, tls_index); dr_mutex_lock(mutex); num_refs += data->num_refs; dr_mutex_unlock(mutex); dr_close_file(data->outfile); if (log_mode){ dr_close_file(data->logfile); } dr_thread_free(drcontext, data->buf_base, INSTR_BUF_SIZE); dr_thread_free(drcontext, data->output_array, OUTPUT_BUF_SIZE); DEBUG_PRINT("%s - thread id : %d, cloned instructions freeing now - %d\n",ins_pass_name, dr_get_thread_id(drcontext),data->static_ptr); for(i=0 ; i<data->static_ptr; i++){ instr_destroy(dr_get_current_drcontext(),data->static_array[i]); } dr_thread_free(drcontext, data->static_array, sizeof(instr_t *)*client_arg->static_info_size); dr_thread_free(drcontext, data, sizeof(per_thread_t)); DEBUG_PRINT("%s - exiting thread done %d\n", ins_pass_name, dr_get_thread_id(drcontext)); }
/* emits the instruction to buf (for tests that wish to do additional checks on * the output) */ static void test_instr_encode(void *dc, instr_t *instr, uint len_expect) { instr_t *decin; uint len; byte *pc = instr_encode(dc, instr, buf); len = (int) (pc - (byte *)buf); #if VERBOSE disassemble_with_info(dc, buf, STDOUT, true, true); #endif ASSERT(len == len_expect); decin = instr_create(dc); decode(dc, buf, decin); ASSERT(instr_same(instr, decin)); instr_destroy(dc, instr); instr_destroy(dc, decin); }
/* frees the Instrs in the instrlist_t */ void instrlist_clear(dcontext_t *dcontext, instrlist_t *ilist) { instr_t *instr; while (NULL != (instr = instrlist_first(ilist))) { instrlist_remove(ilist, instr); instr_destroy(dcontext, instr); } }
/* Remove the rest instructions from ilist after instr (including instr) */ void instrlist_truncate(void *drcontext, instrlist_t *ilist, instr_t *instr) { instr_t *next_instr; DR_ASSERT_MSG(instr != NULL, "Wrong instr to truncate!"); for(; instr != NULL; instr = next_instr) { next_instr = instr_get_next(instr); instrlist_remove(ilist, instr); instr_destroy(drcontext, instr); } }
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); }
// return the addr of the previous instructions // a bad addr can be return in some specific case but generally work good void *get_prev_instr_pc(void *pc, void *drc) { instr_t *instr = instr_create(drc); void *tmp_pc; for (int ct = 1; ; ct++) { tmp_pc = dr_app_pc_for_decoding(pc - ct); if (decode(drc, tmp_pc, instr)) { if (instr_is_call(instr) && decode_next_pc(drc, tmp_pc) == pc) break; } instr_reuse(drc, instr); } instr_destroy(drc, instr); return tmp_pc; }
/* this function to optimize some register stealing code away * for case like below: * * restore %reg * ... * save %reg * * if the %reg is not updated in between, the save %reg can be * optimized out */ static void optimize_register_stealing(void *drcontext, umbra_info_t *umbra_info, void *tag, instrlist_t *ilist) { instr_t *instr, *nexti; instr_t *reg_restore[NUM_SPILL_REGS]; reg_id_t regs[NUM_SPILL_REGS]; int i; /* skip reg eax */ for (i = 1; i < NUM_SPILL_REGS; i++) { reg_restore[i] = NULL; regs[i] = REG_SPILL_START + i; } for (instr = instrlist_first(ilist); instr != NULL; instr = nexti) { nexti = instr_get_next(instr); if (instr_get_app_pc(instr) != NULL && !instr_is_meta_may_fault(instr)) { /* check if app code update register */ for (i = 1; i < NUM_SPILL_REGS; i++) { if (register_is_updated(instr, regs[i])) reg_restore[i] = NULL; } } else { for (i = 1; i < NUM_SPILL_REGS; i++) { if (instr_is_reg_restore(instr, regs[i], umbra_info)) { reg_restore[i] = instr; break; } else if (instr_is_reg_save(instr, regs[i], umbra_info) && reg_restore[i] != NULL) { instrlist_remove(ilist, instr); instr_destroy(drcontext, instr); break; } } } } }
/* Truncates every basic block to the length specified in the CL option (see dr_init) */ dr_emit_flags_t bb_event_truncate(void *drcontext, void *, instrlist_t *bb, bool, bool) { uint app_instruction_count = 0; instr_t *next, *instr = instrlist_first(bb); while (instr != NULL) { next = instr_get_next(instr); if (!instr_is_meta(instr)) { if (app_instruction_count == bb_truncation_length) { instrlist_remove(bb, instr); instr_destroy(drcontext, instr); } else { app_instruction_count++; } } instr = next; } return DR_EMIT_DEFAULT; }
void memdump_exit_event(void) { int i = 0; md_delete_list(filter_head, false); md_delete_list(done_head, false); md_delete_list(app_pc_head, false); dr_global_free(client_arg, sizeof(client_arg_t)); drmgr_unregister_tls_field(tls_index); if (log_mode){ dr_close_file(logfile); } for (i = 0; i < instr_clone_amount; i++){ instr_destroy(dr_get_current_drcontext(), instr_clones[i]); } dr_mutex_destroy(mutex); drutil_exit(); drmgr_exit(); drwrap_exit(); }
/* make sure the following are consistent (though they could still all be wrong :)) * with respect to instr length and opcode: * - decode_fast * - decode * - INSTR_CREATE_ * - encode */ static void test_all_opcodes(void *dc) { byte *pc, *next_pc; byte *end; instrlist_t *ilist = instrlist_create(dc); instr_t *instr; /* we cannot pass on variadic args as separate args to another * macro, so we must split ours by # args (xref PR 208603) */ # define MEMARG(sz) (opnd_create_base_disp(REG_XCX, REG_NULL, 0, 0x37, sz)) # define IMMARG(sz) opnd_create_immed_int(37, sz) # define TGTARG opnd_create_instr(instrlist_last(ilist)) # define REGARG(reg) opnd_create_reg(REG_##reg) # define X86_ONLY 1 # define X64_ONLY 2 # define OPCODE(opc, icnm, ...) \ int len_##icnm; # include "ir_0args.h" # include "ir_1args.h" # include "ir_2args.h" # include "ir_3args.h" # include "ir_4args.h" # undef OPCODE /* we can encode+fast-decode some instrs cross-platform but we * leave that testing to the regression run on that platform */ # define OPCODE(opc, icnm, flags) do { \ if ((flags & IF_X64_ELSE(X86_ONLY, X64_ONLY)) == 0) { \ instrlist_append(ilist, INSTR_CREATE_##icnm(dc)); \ len_##icnm = instr_length(dc, instrlist_last(ilist)); \ } } while (0); # include "ir_0args.h" # undef OPCODE # define OPCODE(opc, icnm, flags, arg1) do { \ if ((flags & IF_X64_ELSE(X86_ONLY, X64_ONLY)) == 0) { \ instrlist_append(ilist, INSTR_CREATE_##icnm(dc, arg1)); \ len_##icnm = instr_length(dc, instrlist_last(ilist)); \ } } while (0); # include "ir_1args.h" # undef OPCODE # define OPCODE(opc, icnm, flags, arg1, arg2) do { \ if ((flags & IF_X64_ELSE(X86_ONLY, X64_ONLY)) == 0) { \ instrlist_append(ilist, INSTR_CREATE_##icnm(dc, arg1, arg2)); \ len_##icnm = instr_length(dc, instrlist_last(ilist)); \ } } while (0); # include "ir_2args.h" # undef OPCODE # define OPCODE(opc, icnm, flags, arg1, arg2, arg3) do { \ if ((flags & IF_X64_ELSE(X86_ONLY, X64_ONLY)) == 0) { \ instrlist_append(ilist, INSTR_CREATE_##icnm(dc, arg1, arg2, arg3)); \ len_##icnm = instr_length(dc, instrlist_last(ilist)); \ } } while (0); # include "ir_3args.h" # undef OPCODE # define OPCODE(opc, icnm, flags, arg1, arg2, arg3, arg4) do { \ if ((flags & IF_X64_ELSE(X86_ONLY, X64_ONLY)) == 0) { \ instrlist_append(ilist, INSTR_CREATE_##icnm(dc, arg1, arg2, arg3, arg4)); \ len_##icnm = instr_length(dc, instrlist_last(ilist)); \ } } while (0); # include "ir_4args.h" # undef OPCODE end = instrlist_encode(dc, ilist, buf, false); instr = instr_create(dc); pc = buf; # define OPCODE(opc, icnm, flags, ...) do { \ if ((flags & IF_X64_ELSE(X86_ONLY, X64_ONLY)) == 0 && len_##icnm != 0) { \ instr_reset(dc, instr); \ next_pc = decode(dc, pc, instr); \ ASSERT((next_pc - pc) == decode_sizeof(dc, pc, NULL _IF_X64(NULL))); \ ASSERT((next_pc - pc) == len_##icnm); \ ASSERT(instr_get_opcode(instr) == OP_##opc); \ pc = next_pc; \ } } while (0); # include "ir_0args.h" # include "ir_1args.h" # include "ir_2args.h" # include "ir_3args.h" # include "ir_4args.h" # undef OPCODE #if VERBOSE for (pc = buf; pc < end; ) pc = disassemble_with_info(dc, pc, STDOUT, true, true); #endif instr_destroy(dc, instr); instrlist_clear_and_destroy(dc, ilist); }
/* returns false on failure */ static bool decode_syscall_num(void *dcontext, byte *entry, syscall_info_t *info) { /* 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; int num_instr = 0; instr_t *instr; 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); pc = decode(dcontext, pc, instr); if (verbose) dr_print_instr(dcontext, STDOUT, instr, ""); if (pc == NULL || !instr_valid(instr)) break; /* ASSUMPTION: a mov imm of 0x7ffe0300 into edx followed by an * indirect call via edx is a system call on XP and later * On XP SP1 it's call *edx, while on XP SP2 it's call *(edx) * For wow it's a call through fs. * FIXME - core exports various is_*_syscall routines (such as * instr_is_wow64_syscall()) which we could use here instead of * duplicating if they were more flexible about when they could * be called (instr_is_wow64_syscall() for ex. asserts if not * in a wow process). */ if (/* int 2e or x64 or win8 sysenter */ (instr_is_syscall(instr) && found_eax && (expect_int2e || expect_x64 || expect_sysenter)) || /* sysenter case */ (expect_sysenter && found_edx && found_eax && instr_is_call_indirect(instr) && /* XP SP{0,1}, 2003 SP0: call *edx */ ((opnd_is_reg(instr_get_target(instr)) && opnd_get_reg(instr_get_target(instr)) == REG_EDX) || /* XP SP2, 2003 SP1: call *(edx) */ (opnd_is_base_disp(instr_get_target(instr)) && opnd_get_base(instr_get_target(instr)) == REG_EDX && opnd_get_index(instr_get_target(instr)) == REG_NULL && opnd_get_disp(instr_get_target(instr)) == 0))) || /* wow case * we don't require found_ecx b/c win8 does not use ecx */ (expect_wow && found_eax && instr_is_call_indirect(instr) && opnd_is_far_base_disp(instr_get_target(instr)) && opnd_get_base(instr_get_target(instr)) == REG_NULL && opnd_get_index(instr_get_target(instr)) == REG_NULL && opnd_get_segment(instr_get_target(instr)) == SEG_FS)) { found_syscall = true; } else if (instr_is_return(instr)) { if (!found_ret) { process_ret(instr, info); found_ret = true; } break; } else if (instr_is_cti(instr)) { if (instr_get_opcode(instr) == OP_call) { /* handle 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 */ byte *tgt; assert(opnd_is_pc(instr_get_target(instr))); tgt = opnd_get_pc(instr_get_target(instr)); /* we expect only ret or ret imm, and possibly some nops (in gdi32). * XXX: what about jmp to shared ret (seen in the past on some syscalls)? */ if (tgt > pc && tgt <= pc + 16) { bool ok = false; do { if (pc == tgt) { ok = true; break; } instr_reset(dcontext, instr); pc = decode(dcontext, pc, instr); if (verbose) dr_print_instr(dcontext, STDOUT, instr, ""); if (instr_is_return(instr)) { process_ret(instr, info); found_ret = true; } else if (!instr_is_nop(instr)) break; num_instr++; } while (num_instr <= MAX_INSTRS_BEFORE_SYSCALL); if (ok) continue; } } /* assume not a syscall wrapper if we hit a cti */ break; /* give up gracefully */ } 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) { int imm = (int) opnd_get_immed_int(instr_get_src(instr, 0)); if (imm == 0x7ffe0300) 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; }
/* 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; }
static instr_t * analyze_client_code(void *drcontext, instrlist_t *ilist, instr_t *where, ref_info_t *ref_info) { instr_t *next, *lea, *and, *cmp, *jcc, *sub; opnd_t ref, opnd; ref_cache_t *cache; reg_id_t reg; int pos, i; next = instr_get_next(where); if (next == NULL) return NULL; if (instr_get_opcode(where) != OP_lea) return next; /* lea [ref] => r1 */ ref = instr_get_src(where, 0); if (!opnd_is_base_disp(ref) || opnd_get_index(ref) != DR_REG_NULL) return next; lea = where; and = next; cmp = instr_get_next(and); jcc = instr_get_next(cmp); if (instr_get_app_pc(and) == NULL && instr_get_opcode(and) == OP_and && instr_get_app_pc(cmp) == NULL && instr_get_opcode(cmp) == OP_cmp && instr_get_app_pc(jcc) == NULL && instr_get_opcode(jcc) == OP_jz) { /* find pattern of * lea [ref] => reg * and 0xffffffff00000000 reg * cmp cache->tag reg * jz */ opnd = instr_get_src(cmp, 1); cache = opnd_get_addr(opnd) - offsetof(ref_cache_t, tag); for (i = 0; i < 10; ) { lea = instr_get_next(lea); if (!instr_is_label(lea)) i++; } DR_ASSERT(instr_get_opcode(lea) == OP_lea); } else if (instr_get_app_pc(next) == NULL && instr_get_opcode(next) == OP_sub) { opnd = instr_get_src(next, 0); cache = opnd_get_addr(opnd) - offsetof(ref_cache_t, offset); } else { return next; } reg = opnd_get_base(ref); UMBRA_REG_TO_POS(reg, pos); if (ref_info[pos].cache == NULL) { ref_info[pos].cache = cache; } else { sub = instr_get_next(lea); DR_ASSERT(instr_get_opcode(sub) == OP_sub); while (lea != where) { next = instr_get_next(where); instrlist_remove(ilist, where); instr_destroy(drcontext, where); where = next; } opnd = OPND_CREATE_ABSMEM((void *)(reg_t)ref_info[pos].cache + offsetof(ref_cache_t, offset), OPSZ_PTR); instr_set_src(sub, 0, opnd); if (proc_info.client.app_unit_bits > 0 && proc_info.client.shd_unit_bits != 0) next = instr_get_next(sub); /* reg & mask => reg */ if (proc_info.client.orig_addr) { next = instr_get_next(next); /* mov reg => r2 */ next = instr_get_next(next); /* r2 & bit_mask => r2 */ } } next = instr_get_next(lea); return instr_get_next(next); }