| Index: src/processor/disassembler_x86.cc
|
| diff --git a/src/processor/disassembler_x86.cc b/src/processor/disassembler_x86.cc
|
| index 559022404f6ee0ac1cac44ab67b4eb5d8c4ffb7b..b5c8838d837880e587fe10211027c1febf959f50 100644
|
| --- a/src/processor/disassembler_x86.cc
|
| +++ b/src/processor/disassembler_x86.cc
|
| @@ -36,132 +36,139 @@ DisassemblerX86::DisassemblerX86(const uint8_t *bytecode,
|
| bytecode_(bytecode),
|
| size_(size),
|
| virtual_address_(virtual_address),
|
| - current_byte_offset_(0),
|
| - current_inst_offset_(0),
|
| + handle_(0),
|
| instr_valid_(false),
|
| + current_instr_(nullptr),
|
| register_valid_(false),
|
| + bad_register_(X86_REG_INVALID),
|
| pushed_bad_value_(false),
|
| end_of_block_(false),
|
| flags_(0) {
|
| - libdis::x86_init(libdis::opt_none, NULL, NULL);
|
| + //TODO: allow selecting 32 or 64-bit.
|
| + if (cs_open(CS_ARCH_X86, CS_MODE_32, &handle_) == CS_ERR_OK) {
|
| + // Enable detailed instruction information.
|
| + cs_option(handle_, CS_OPT_DETAIL, CS_OPT_ON);
|
| + current_instr_ = cs_malloc(handle_);
|
| + }
|
| }
|
|
|
| DisassemblerX86::~DisassemblerX86() {
|
| - if (instr_valid_)
|
| - libdis::x86_oplist_free(¤t_instr_);
|
| + if (current_instr_) {
|
| + cs_free(current_instr_, 1);
|
| + }
|
|
|
| - libdis::x86_cleanup();
|
| + cs_close(&handle_);
|
| }
|
|
|
| -uint32_t DisassemblerX86::NextInstruction() {
|
| - if (instr_valid_)
|
| - libdis::x86_oplist_free(¤t_instr_);
|
| -
|
| - if (current_byte_offset_ >= size_) {
|
| - instr_valid_ = false;
|
| - return 0;
|
| +// Return a pointer to the `nth` (zero-based) operand in `detail`, or NULL if
|
| +// no such operand exists.
|
| +static cs_x86_op* get_operand(cs_x86& detail, size_t nth) {
|
| + if (nth > detail.op_count) {
|
| + return nullptr;
|
| }
|
| - uint32_t instr_size = 0;
|
| - instr_size = libdis::x86_disasm((unsigned char *)bytecode_, size_,
|
| - virtual_address_, current_byte_offset_,
|
| - ¤t_instr_);
|
| - if (instr_size == 0) {
|
| - instr_valid_ = false;
|
| + return &detail.operands[nth];
|
| +}
|
| +
|
| +size_t DisassemblerX86::NextInstruction() {
|
| + instr_valid_ = false;
|
| + if (size_ == 0) {
|
| return 0;
|
| }
|
|
|
| - current_byte_offset_ += instr_size;
|
| - current_inst_offset_++;
|
| - instr_valid_ = libdis::x86_insn_is_valid(¤t_instr_);
|
| - if (!instr_valid_)
|
| + if (!cs_disasm_iter(handle_, &bytecode_, &size_, &virtual_address_,
|
| + current_instr_)) {
|
| return 0;
|
| + }
|
|
|
| - if (current_instr_.type == libdis::insn_return)
|
| + instr_valid_ = true;
|
| + if (cs_insn_group(handle_, current_instr_, CS_GRP_RET))
|
| end_of_block_ = true;
|
| - libdis::x86_op_t *src = libdis::x86_get_src_operand(¤t_instr_);
|
| - libdis::x86_op_t *dest = libdis::x86_get_dest_operand(¤t_instr_);
|
| + cs_x86_op* src = get_operand(current_instr_->detail->x86, 1);
|
| + cs_x86_op* dest = get_operand(current_instr_->detail->x86, 0);
|
|
|
| if (register_valid_) {
|
| - switch (current_instr_.group) {
|
| + if (cs_insn_group(handle_, current_instr_, CS_GRP_JUMP) ||
|
| + cs_insn_group(handle_, current_instr_, CS_GRP_CALL)) {
|
| // Flag branches based off of bad registers and calls that occur
|
| // after pushing bad values.
|
| - case libdis::insn_controlflow:
|
| - switch (current_instr_.type) {
|
| - case libdis::insn_jmp:
|
| - case libdis::insn_jcc:
|
| - case libdis::insn_call:
|
| - case libdis::insn_callcc:
|
| - if (dest) {
|
| - switch (dest->type) {
|
| - case libdis::op_expression:
|
| - if (dest->data.expression.base.id == bad_register_.id)
|
| - flags_ |= DISX86_BAD_BRANCH_TARGET;
|
| - break;
|
| - case libdis::op_register:
|
| - if (dest->data.reg.id == bad_register_.id)
|
| - flags_ |= DISX86_BAD_BRANCH_TARGET;
|
| - break;
|
| - default:
|
| - if (pushed_bad_value_ &&
|
| - (current_instr_.type == libdis::insn_call ||
|
| - current_instr_.type == libdis::insn_callcc))
|
| - flags_ |= DISX86_BAD_ARGUMENT_PASSED;
|
| - break;
|
| - }
|
| - }
|
| - break;
|
| - default:
|
| - break;
|
| + if (dest) {
|
| + switch (dest->type) {
|
| + case X86_OP_MEM:
|
| + if (dest->mem.base == bad_register_)
|
| + flags_ |= DISX86_BAD_BRANCH_TARGET;
|
| + break;
|
| + case X86_OP_REG:
|
| + if (dest->reg == bad_register_)
|
| + flags_ |= DISX86_BAD_BRANCH_TARGET;
|
| + break;
|
| + default:
|
| + if (pushed_bad_value_ &&
|
| + cs_insn_group(handle_, current_instr_, CS_GRP_CALL)) {
|
| + flags_ |= DISX86_BAD_ARGUMENT_PASSED;
|
| + }
|
| + break;
|
| }
|
| - break;
|
| -
|
| - // Flag block data operations that use bad registers for src or dest.
|
| - case libdis::insn_string:
|
| - if (dest && dest->type == libdis::op_expression &&
|
| - dest->data.expression.base.id == bad_register_.id)
|
| + }
|
| + } else if(currentInstructionIsBlockData()) {
|
| + // Flag block data operations that use bad registers for src or dest.
|
| + if (dest && dest->type == X86_OP_MEM &&
|
| + dest->mem.base == bad_register_) {
|
| flags_ |= DISX86_BAD_BLOCK_WRITE;
|
| - if (src && src->type == libdis::op_expression &&
|
| - src->data.expression.base.id == bad_register_.id)
|
| + }
|
| + if (src && src->type == X86_OP_MEM &&
|
| + src->mem.base == bad_register_) {
|
| flags_ |= DISX86_BAD_BLOCK_READ;
|
| - break;
|
| -
|
| - // Flag comparisons based on bad data.
|
| - case libdis::insn_comparison:
|
| - if ((dest && dest->type == libdis::op_expression &&
|
| - dest->data.expression.base.id == bad_register_.id) ||
|
| - (src && src->type == libdis::op_expression &&
|
| - src->data.expression.base.id == bad_register_.id) ||
|
| - (dest && dest->type == libdis::op_register &&
|
| - dest->data.reg.id == bad_register_.id) ||
|
| - (src && src->type == libdis::op_register &&
|
| - src->data.reg.id == bad_register_.id))
|
| + }
|
| + } else {
|
| + switch (current_instr_->id) {
|
| + // Flag comparisons based on bad data.
|
| + case X86_INS_CMP:
|
| + case X86_INS_CMPPS:
|
| + case X86_INS_PCMPEQD:
|
| + case X86_INS_PCMPEQW:
|
| + case X86_INS_PFCMPEQ:
|
| + case X86_INS_PFCMPGE:
|
| + case X86_INS_PFCMPGT:
|
| + case X86_INS_TEST:
|
| + if ((dest && dest->type == X86_OP_MEM &&
|
| + dest->mem.base == bad_register_) ||
|
| + (src && src->type == X86_OP_MEM &&
|
| + src->mem.base == bad_register_) ||
|
| + (dest && dest->type == X86_OP_REG &&
|
| + dest->reg == bad_register_) ||
|
| + (src && src->type == X86_OP_REG &&
|
| + src->reg == bad_register_)) {
|
| flags_ |= DISX86_BAD_COMPARISON;
|
| + }
|
| break;
|
|
|
| - // Flag any other instruction which derefs a bad register for
|
| - // src or dest.
|
| + // Flag any other instruction which derefs a bad register for
|
| + // src or dest.
|
| default:
|
| - if (dest && dest->type == libdis::op_expression &&
|
| - dest->data.expression.base.id == bad_register_.id)
|
| + if (dest && dest->type == X86_OP_MEM &&
|
| + dest->mem.base == bad_register_) {
|
| flags_ |= DISX86_BAD_WRITE;
|
| - if (src && src->type == libdis::op_expression &&
|
| - src->data.expression.base.id == bad_register_.id)
|
| + }
|
| + if (src && src->type == X86_OP_MEM &&
|
| + src->mem.base == bad_register_) {
|
| flags_ |= DISX86_BAD_READ;
|
| + }
|
| break;
|
| + }
|
| }
|
| }
|
|
|
| // When a register is marked as tainted check if it is pushed.
|
| // TODO(cdn): may also want to check for MOVs into EBP offsets.
|
| - if (register_valid_ && dest && current_instr_.type == libdis::insn_push) {
|
| + if (register_valid_ && dest && current_instr_->id == X86_INS_PUSH) {
|
| switch (dest->type) {
|
| - case libdis::op_expression:
|
| - if (dest->data.expression.base.id == bad_register_.id ||
|
| - dest->data.expression.index.id == bad_register_.id)
|
| + case X86_OP_MEM:
|
| + if (dest->mem.base == bad_register_ ||
|
| + dest->mem.index == bad_register_)
|
| pushed_bad_value_ = true;
|
| break;
|
| - case libdis::op_register:
|
| - if (dest->data.reg.id == bad_register_.id)
|
| + case X86_OP_REG:
|
| + if (dest->reg == bad_register_)
|
| pushed_bad_value_ = true;
|
| break;
|
| default:
|
| @@ -173,32 +180,93 @@ uint32_t DisassemblerX86::NextInstruction() {
|
| // For conditional MOVs and XCHGs assume that
|
| // there is a hit.
|
| if (register_valid_) {
|
| - switch (current_instr_.type) {
|
| - case libdis::insn_xor:
|
| - if (src && src->type == libdis::op_register &&
|
| - dest && dest->type == libdis::op_register &&
|
| - src->data.reg.id == bad_register_.id &&
|
| - src->data.reg.id == dest->data.reg.id)
|
| + switch (current_instr_->id) {
|
| + case X86_INS_PXOR:
|
| + case X86_INS_XOR:
|
| + case X86_INS_XORPD:
|
| + case X86_INS_XORPS:
|
| + if (src && src->type == X86_OP_REG &&
|
| + dest && dest->type == X86_OP_REG &&
|
| + src->reg == bad_register_ &&
|
| + src->reg == dest->reg) {
|
| register_valid_ = false;
|
| + }
|
| break;
|
| - case libdis::insn_pop:
|
| - case libdis::insn_mov:
|
| - case libdis::insn_movcc:
|
| - if (dest && dest->type == libdis::op_register &&
|
| - dest->data.reg.id == bad_register_.id)
|
| + case X86_INS_POP:
|
| + case X86_INS_CVTDQ2PS:
|
| + case X86_INS_CVTPI2PS:
|
| + case X86_INS_CVTPS2PD:
|
| + case X86_INS_CVTPS2PI:
|
| + case X86_INS_CVTTPS2PI:
|
| + case X86_INS_LAHF:
|
| + case X86_INS_LDS:
|
| + case X86_INS_LEA:
|
| + case X86_INS_LES:
|
| + case X86_INS_LFS:
|
| + case X86_INS_LGS:
|
| + case X86_INS_LSS:
|
| + case X86_INS_MASKMOVQ:
|
| + case X86_INS_MOVD:
|
| + case X86_INS_MOVQ:
|
| + case X86_INS_MOV:
|
| + case X86_INS_MOVAPD:
|
| + case X86_INS_MOVAPS:
|
| + case X86_INS_MOVHLPS:
|
| + case X86_INS_MOVHPD:
|
| + case X86_INS_MOVLHPS:
|
| + case X86_INS_MOVLPD:
|
| + case X86_INS_MOVLPS:
|
| + case X86_INS_MOVMSKPS:
|
| + case X86_INS_MOVNTI:
|
| + case X86_INS_MOVNTPS:
|
| + case X86_INS_MOVNTQ:
|
| + case X86_INS_MOVSX:
|
| + case X86_INS_MOVSXD:
|
| + case X86_INS_MOVUPD:
|
| + case X86_INS_MOVUPS:
|
| + case X86_INS_MOVZX:
|
| + case X86_INS_PSHUFW:
|
| + case X86_INS_SAHF:
|
| + case X86_INS_CMOVA:
|
| + case X86_INS_CMOVBE:
|
| + case X86_INS_CMOVG:
|
| + case X86_INS_CMOVGE:
|
| + case X86_INS_CMOVL:
|
| + case X86_INS_CMOVLE:
|
| + case X86_INS_CMOVNO:
|
| + case X86_INS_CMOVNP:
|
| + case X86_INS_CMOVNS:
|
| + case X86_INS_CMOVO:
|
| + case X86_INS_CMOVP:
|
| + case X86_INS_CMOVS:
|
| + case X86_INS_SETA:
|
| + case X86_INS_SETBE:
|
| + case X86_INS_SETG:
|
| + case X86_INS_SETGE:
|
| + case X86_INS_SETL:
|
| + case X86_INS_SETLE:
|
| + case X86_INS_SETNO:
|
| + case X86_INS_SETNS:
|
| + case X86_INS_SETO:
|
| + case X86_INS_SETS:
|
| + if (dest && dest->type == X86_OP_REG &&
|
| + dest->reg == bad_register_) {
|
| register_valid_ = false;
|
| + }
|
| break;
|
| - case libdis::insn_popregs:
|
| + case X86_INS_POPAW:
|
| register_valid_ = false;
|
| break;
|
| - case libdis::insn_xchg:
|
| - case libdis::insn_xchgcc:
|
| - if (dest && dest->type == libdis::op_register &&
|
| - src && src->type == libdis::op_register) {
|
| - if (dest->data.reg.id == bad_register_.id)
|
| - memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t));
|
| - else if (src->data.reg.id == bad_register_.id)
|
| - memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t));
|
| + case X86_INS_BSWAP:
|
| + case X86_INS_XCHG:
|
| + case X86_INS_CMPXCHG:
|
| + case X86_INS_CMPXCHG8B:
|
| + if (dest && dest->type == X86_OP_REG &&
|
| + src && src->type == X86_OP_REG) {
|
| + if (dest->reg == bad_register_)
|
| + bad_register_ = src->reg;
|
| + else if (src->reg == bad_register_)
|
| + bad_register_ = dest->reg;
|
| }
|
| break;
|
| default:
|
| @@ -206,34 +274,67 @@ uint32_t DisassemblerX86::NextInstruction() {
|
| }
|
| }
|
|
|
| - return instr_size;
|
| + return current_instr_->size;
|
| +}
|
| +
|
| +bool DisassemblerX86::currentInstructionIsBlockData() {
|
| + if (!instr_valid_)
|
| + return false;
|
| +
|
| + switch (current_instr_->id) {
|
| + case X86_INS_CMPSB:
|
| + case X86_INS_CMPSD:
|
| + case X86_INS_CMPSQ:
|
| + case X86_INS_CMPSW:
|
| + case X86_INS_LODSB:
|
| + case X86_INS_LODSD:
|
| + case X86_INS_LODSQ:
|
| + case X86_INS_LODSW:
|
| + case X86_INS_MOVSB:
|
| + case X86_INS_MOVSD:
|
| + case X86_INS_MOVSQ:
|
| + case X86_INS_MOVSW:
|
| + case X86_INS_SCASB:
|
| + case X86_INS_SCASD:
|
| + case X86_INS_SCASQ:
|
| + case X86_INS_SCASW:
|
| + case X86_INS_STOSB:
|
| + case X86_INS_STOSD:
|
| + case X86_INS_STOSQ:
|
| + case X86_INS_STOSW:
|
| + case X86_INS_XLATB:
|
| + return true;
|
| + default:
|
| + return false;
|
| + }
|
| }
|
|
|
| bool DisassemblerX86::setBadRead() {
|
| if (!instr_valid_)
|
| return false;
|
|
|
| - libdis::x86_op_t *operand = libdis::x86_get_src_operand(¤t_instr_);
|
| - if (!operand || operand->type != libdis::op_expression)
|
| + cs_x86_op* operand = get_operand(current_instr_->detail->x86, 1);
|
| + if (!operand || operand->type != X86_OP_MEM)
|
| return false;
|
|
|
| - memcpy(&bad_register_, &operand->data.expression.base,
|
| - sizeof(libdis::x86_reg_t));
|
| + bad_register_ = static_cast<x86_reg>(operand->mem.base);
|
| register_valid_ = true;
|
| +
|
| return true;
|
| }
|
|
|
| bool DisassemblerX86::setBadWrite() {
|
| +
|
| if (!instr_valid_)
|
| return false;
|
|
|
| - libdis::x86_op_t *operand = libdis::x86_get_dest_operand(¤t_instr_);
|
| - if (!operand || operand->type != libdis::op_expression)
|
| + cs_x86_op* operand = get_operand(current_instr_->detail->x86, 0);
|
| + if (!operand || operand->type != X86_OP_MEM)
|
| return false;
|
|
|
| - memcpy(&bad_register_, &operand->data.expression.base,
|
| - sizeof(libdis::x86_reg_t));
|
| + bad_register_ = static_cast<x86_reg>(operand->mem.base);
|
| register_valid_ = true;
|
| +
|
| return true;
|
| }
|
|
|
|
|