bool ref_is_far_var(mem_ref_t *ref) { if (opnd_is_far_base_disp(ref->opnd)) return true; return false; }
static bool ref_is_interested(mem_ref_t *ref) { /* I still have no idea how far memory is calculated */ if (opnd_is_far_base_disp(ref->opnd)) return false; return true; }
bool ref_is_string_ref(mem_ref_t *ref) { if (!opcode_is_rep_ins(ref->opcode)) return false; if (!opnd_is_far_base_disp(ref->opnd)) return false; return true; }
bool ref_is_tls(opnd_t opnd) { reg_id_t seg; if (opnd_is_far_base_disp(opnd)) { seg = opnd_get_segment(opnd); if (seg == DR_SEG_FS || seg == DR_SEG_GS) return true; } return false; }
/* returns whether found a syscall * - found_eax: whether the caller has seen "mov imm => %eax" * - found_edx: whether the caller has seen "mov $0x7ffe0300 => %edx", * xref the comment below about "mov $0x7ffe0300 => %edx". */ static bool process_syscall_instr(void *dcontext, instr_t *instr, bool found_eax, bool found_edx) { /* 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) || /* win10 has imm in edx and a near call */ found_edx))) return true; return false; }
/* 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; }