/* code cache to hold the call to "clean_call" and return to DR code cache */ static void code_cache_init(void) { void *drcontext; instrlist_t *ilist; instr_t *where; byte *end; drcontext = dr_get_current_drcontext(); code_cache = dr_nonheap_alloc(PAGE_SIZE, DR_MEMPROT_READ | DR_MEMPROT_WRITE | DR_MEMPROT_EXEC); ilist = instrlist_create(drcontext); /* The lean procecure simply performs a clean call, and then jump back */ /* jump back to the DR's code cache */ where = INSTR_CREATE_jmp_ind(drcontext, opnd_create_reg(DR_REG_XCX)); instrlist_meta_append(ilist, where); /* clean call */ dr_insert_clean_call(drcontext, ilist, where, (void *)clean_call_ins_trace, false, 0); /* Encodes the instructions into memory and then cleans up. */ end = instrlist_encode(drcontext, ilist, code_cache, false); DR_ASSERT((end - code_cache) < PAGE_SIZE); instrlist_clear_and_destroy(drcontext, ilist); /* set the memory as just +rx now */ dr_memory_protect(code_cache, PAGE_SIZE, DR_MEMPROT_READ | DR_MEMPROT_EXEC); }
static void callee_info_free(dcontext_t *dcontext, callee_info_t *ci) { if (ci->ilist != NULL) { ASSERT(ci->opt_inline); instrlist_clear_and_destroy(GLOBAL_DCONTEXT, ci->ilist); } HEAP_TYPE_FREE(GLOBAL_DCONTEXT, ci, callee_info_t, ACCT_CLEANCALL, PROTECTED); }
static void test_instr_as_immed(void) { void *drcontext = dr_get_current_drcontext(); instrlist_t *ilist = instrlist_create(drcontext); byte *pc; instr_t *ins0, *ins1, *ins2; opnd_t opnd; byte *highmem = PREFERRED_ADDR; pc = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE| DR_MEMPROT_EXEC, highmem); ASSERT(pc == highmem); /* Test push_imm of instr */ ins0 = INSTR_CREATE_nop(drcontext); instrlist_append(ilist, ins0); instrlist_insert_push_instr_addr(drcontext, ins0, highmem, ilist, NULL, &ins1, &ins2); ASSERT(ins2 != NULL); instrlist_append(ilist, INSTR_CREATE_pop (drcontext, opnd_create_reg(DR_REG_RAX))); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, highmem, true); instrlist_clear(drcontext, ilist); ASSERT(pc < highmem + PAGE_SIZE); pc = ((byte* (*)(void))highmem)(); ASSERT(pc == highmem); /* Test mov_imm of instr */ ins0 = INSTR_CREATE_nop(drcontext); instrlist_append(ilist, ins0); /* Beyond TOS, but a convenient mem dest */ opnd = opnd_create_base_disp(DR_REG_RSP, DR_REG_NULL, 0, -8, OPSZ_8); instrlist_insert_mov_instr_addr(drcontext, ins0, highmem, opnd, ilist, NULL, &ins1, &ins2); ASSERT(ins2 != NULL); instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_RAX), opnd)); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, highmem, true); instrlist_clear(drcontext, ilist); ASSERT(pc < highmem + PAGE_SIZE); pc = ((byte* (*)(void))highmem)(); ASSERT(pc == highmem); instrlist_clear_and_destroy(drcontext, ilist); dr_raw_mem_free(highmem, PAGE_SIZE); }
DR_EXPORT void dr_init(client_id_t id) { /* Generate the "slowpath" which just returns to eax. */ void *dc = dr_get_current_drcontext(); instrlist_t *ilist = instrlist_create(dc); PRE(ilist, NULL, INSTR_CREATE_jmp_ind(dc, opnd_create_reg(DR_REG_XAX))); slowpath = dr_nonheap_alloc(SLOWPATH_SIZE, (DR_MEMPROT_READ| DR_MEMPROT_WRITE| DR_MEMPROT_EXEC)); instrlist_encode(dc, ilist, slowpath, false /*no relative jumps*/); instrlist_clear_and_destroy(dc, ilist); dr_register_bb_event(event_bb); dr_register_exit_event(event_exit); }
static void reachability_test(void) { void *drcontext = dr_get_current_drcontext(); instrlist_t *ilist = instrlist_create(drcontext); byte *gencode = (byte *) dr_nonheap_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE|DR_MEMPROT_EXEC); byte *pc; int res; byte *highmem = PREFERRED_ADDR; pc = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE| DR_MEMPROT_EXEC, highmem); ASSERT(pc == highmem); dr_fprintf(STDERR, " reachability test..."); /* Test auto-magically turning rip-rel that won't reach but targets xax * into absmem. */ instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_EAX), opnd_create_rel_addr(highmem, OPSZ_4))); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, gencode, false); instrlist_clear(drcontext, ilist); ASSERT(pc < gencode + PAGE_SIZE); *(int*)highmem = 0x12345678; res = ((int (*)(void))gencode)(); ASSERT(res == 0x12345678); /* Test auto-magically turning a reachable absmem into a rip-rel. */ instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_ECX), opnd_create_abs_addr(highmem + 0x800, OPSZ_4))); instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_EAX), opnd_create_reg(DR_REG_ECX))); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, highmem, false); instrlist_clear(drcontext, ilist); ASSERT(pc < highmem + PAGE_SIZE); *(int*)(highmem + 0x800) = 0x12345678; res = ((int (*)(void))highmem)(); ASSERT(res == 0x12345678); dr_raw_mem_free(highmem, PAGE_SIZE); /* Test targeting upper 2GB of low 4GB */ highmem = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE| DR_MEMPROT_EXEC, (byte *)0xabcd0000); instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_ECX), opnd_create_abs_addr(highmem, OPSZ_4))); instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_EAX), opnd_create_reg(DR_REG_ECX))); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, gencode, false); instrlist_clear(drcontext, ilist); ASSERT(pc < gencode + PAGE_SIZE); *(int*)highmem = 0x12345678; res = ((int (*)(void))gencode)(); ASSERT(res == 0x12345678); dr_raw_mem_free(highmem, PAGE_SIZE); /* Test targeting lower 2GB of low 4GB */ highmem = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE| DR_MEMPROT_EXEC, (byte *)0x143d0000); instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_ECX), opnd_create_abs_addr(highmem, OPSZ_4))); instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_EAX), opnd_create_reg(DR_REG_ECX))); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, gencode, false); instrlist_clear(drcontext, ilist); ASSERT(pc < gencode + PAGE_SIZE); *(int*)highmem = 0x12345678; res = ((int (*)(void))gencode)(); ASSERT(res == 0x12345678); dr_raw_mem_free(highmem, PAGE_SIZE); instrlist_clear_and_destroy(drcontext, ilist); dr_nonheap_free(gencode, PAGE_SIZE); test_instr_as_immed(); dr_fprintf(STDERR, "success\n"); }
/* 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); }
void Shade::detour(void *address, void *target, void *&trampoline) { const size_t instr_max = 17; auto list = instrlist_create(dr); byte instr_data[instr_max]; byte *current = (byte *)address; byte *min_pos = (byte *)address + 5; size_t size = 0; while(current < min_pos) { read(current, instr_data, instr_max); auto instr = instr_create(dr); byte *decoded = decode_from_copy(dr, instr_data, current, instr); if(!decoded) error("Unknown instruction"); instrlist_append(list, instr); instr_make_persistent(dr, instr); current += (size_t)(decoded - instr_data); size += instr_length(dr, instr); } auto instr = INSTR_CREATE_jmp(dr, opnd_create_pc(current)); size += instr_length(dr, instr); instrlist_append(list, instr); auto local_trampoline = alloca(size); if(!local_trampoline) error("Out of memory"); void *remote = code_section.allocate(size, 4); if(!instrlist_encode_to_copy(dr, list, (byte *)local_trampoline, (byte *)remote, 0, true)) error("Unable to encode instructions"); instrlist_clear_and_destroy(dr, list); write(remote, local_trampoline, size); trampoline = remote; char code[5]; DWORD offset = (size_t)target - (size_t)address - 5; code[0] = 0xE9; *(DWORD *)(code + 1) = offset; access(address, 5, [&] { write(address, code, 5); }); }