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; |
} |