* fix: format with clang-format-20 * fix: fix clang-format linter with rewrite '^#define.*/\*.*\\$' * fix: update clang-format to version 20 in workflows and documentation * fix: add SPDX license information to .git-blame-ignore-revs
2477 lines
69 KiB
C++
2477 lines
69 KiB
C++
// SPDX-FileCopyrightText: 2023 Dhruv Maroo <dhruvmaru007@gmail.com>
|
|
// SPDX-FileCopyrightText: 2024-2025 tushar3q34 <tushar3q34@gmail.com>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
/**
|
|
* \file il_ops.c
|
|
*
|
|
* Contains the IL implementations for x86 instructions.
|
|
*
|
|
* References:
|
|
* - https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "x86_il.h"
|
|
#include <rz_il/rz_il_opbuilder_begin.h>
|
|
|
|
/**
|
|
* ======== INSTRUCTION DOCUMENTATION FORMAT ========
|
|
*
|
|
* Instruction mnemonic
|
|
* Description
|
|
* | Opcode | 64-bit | Compat/Leg mode
|
|
*/
|
|
|
|
/**
|
|
* \brief Invalid instruction
|
|
*/
|
|
IL_LIFTER(invalid) {
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* \brief Unimplemented instruction
|
|
*/
|
|
IL_LIFTER(unimpl) {
|
|
return EMPTY();
|
|
}
|
|
|
|
/* 8086/8088/80186/80286/80386/80486 instructions*/
|
|
|
|
/**
|
|
* AAA
|
|
* ASCII adjust AL after addition
|
|
* 37 | Invalid | Valid
|
|
*/
|
|
IL_LIFTER(aaa) {
|
|
RzILOpPure *low_al = LOGAND(x86_il_get_reg(X86_REG_AL), U8(0x0f));
|
|
RzILOpPure *al_ovf = UGT(low_al, U8(9));
|
|
RzILOpPure *cond = OR(al_ovf, VARG(EFLAGS(AF)));
|
|
|
|
RzILOpEffect *set_ax = x86_il_set_reg(X86_REG_AX, ADD(x86_il_get_reg(X86_REG_AX), U16(0x106)));
|
|
RzILOpEffect *set_af = SETG(EFLAGS(AF), IL_TRUE);
|
|
RzILOpEffect *set_cf = SETG(EFLAGS(CF), IL_TRUE);
|
|
RzILOpEffect *true_cond = SEQ3(set_ax, set_af, set_cf);
|
|
|
|
set_af = SETG(EFLAGS(AF), IL_FALSE);
|
|
set_cf = SETG(EFLAGS(CF), IL_FALSE);
|
|
RzILOpEffect *false_cond = SEQ2(set_af, set_cf);
|
|
|
|
RzILOpEffect *final_cond = BRANCH(cond, true_cond, false_cond);
|
|
RzILOpEffect *set_al = x86_il_set_reg(X86_REG_AL, LOGAND(x86_il_get_reg(X86_REG_AL), U8(0x0f)));
|
|
|
|
return SEQ2(final_cond, set_al);
|
|
}
|
|
|
|
/**
|
|
* AAD imm8
|
|
* Adjust AX before division to number base imm8
|
|
* D5 ib | Invalid | Valid
|
|
*/
|
|
IL_LIFTER(aad) {
|
|
|
|
RzILOpEffect *temp_al = SETL("temp_al", x86_il_get_reg(X86_REG_AL));
|
|
RzILOpEffect *temp_ah = SETL("temp_ah", x86_il_get_reg(X86_REG_AH));
|
|
|
|
RzILOpPure *imm;
|
|
if (ins->structure->operand_count_visible == 0) {
|
|
// Use base 10 if none specified
|
|
imm = SN(8, 0x0a);
|
|
} else {
|
|
imm = x86_il_get_op(0);
|
|
}
|
|
|
|
RzILOpPure *adjusted = ADD(VARL("temp_al"), MUL(VARL("temp_ah"), imm));
|
|
adjusted = LOGAND(adjusted, U8(0xff));
|
|
RzILOpEffect *adjusted_set = SETL("adjusted", adjusted);
|
|
|
|
RzILOpEffect *set_flags = x86_il_set_result_flags(VARL("adjusted"));
|
|
|
|
return SEQ6(temp_al, temp_ah, adjusted_set, x86_il_set_reg(X86_REG_AL, VARL("adjusted")), x86_il_set_reg(X86_REG_AH, U8(0)), set_flags);
|
|
}
|
|
|
|
/**
|
|
* AAM imm8
|
|
* Adjust AX after multiply to number base imm8
|
|
* D4 ib | Invalid | Valid
|
|
*/
|
|
IL_LIFTER(aam) {
|
|
|
|
RzILOpEffect *temp_al = SETL("temp_al", x86_il_get_reg(X86_REG_AL));
|
|
|
|
RzILOpPure *imm;
|
|
if (ins->structure->operand_count_visible == 0) {
|
|
imm = SN(8, 0xa);
|
|
} else {
|
|
imm = x86_il_get_op(0);
|
|
}
|
|
|
|
RzILOpEffect *ah = x86_il_set_reg(X86_REG_AH, DIV(VARL("temp_al"), imm));
|
|
RzILOpEffect *adjusted = SETL("adjusted", MOD(VARL("temp_al"), DUP(imm)));
|
|
RzILOpEffect *al = x86_il_set_reg(X86_REG_AL, VARL("adjusted"));
|
|
RzILOpEffect *set_flags = x86_il_set_result_flags(VARL("adjusted"));
|
|
|
|
return SEQ5(temp_al, ah, adjusted, al, set_flags);
|
|
}
|
|
|
|
/**
|
|
* AAS
|
|
* ASCII adjust AL after subtraction
|
|
* 3F | Invalid | Valid
|
|
*/
|
|
IL_LIFTER(aas) {
|
|
|
|
RzILOpPure *low_al = LOGAND(x86_il_get_reg(X86_REG_AL), U8(0x0f));
|
|
RzILOpPure *al_ovf = UGT(low_al, U8(9));
|
|
RzILOpPure *cond = OR(al_ovf, VARG(EFLAGS(AF)));
|
|
|
|
RzILOpEffect *set_ax = x86_il_set_reg(X86_REG_AX, SUB(x86_il_get_reg(X86_REG_AX), U16(0x6)));
|
|
RzILOpEffect *set_ah = x86_il_set_reg(X86_REG_AH, SUB(x86_il_get_reg(X86_REG_AH), U8(0x1)));
|
|
RzILOpEffect *set_af = SETG(EFLAGS(AF), IL_TRUE);
|
|
RzILOpEffect *set_cf = SETG(EFLAGS(CF), IL_TRUE);
|
|
RzILOpEffect *true_cond = SEQ4(set_ax, set_ah, set_af, set_cf);
|
|
|
|
set_af = SETG(EFLAGS(AF), IL_FALSE);
|
|
set_cf = SETG(EFLAGS(CF), IL_FALSE);
|
|
RzILOpEffect *false_cond = SEQ2(set_af, set_cf);
|
|
|
|
RzILOpEffect *final_cond = BRANCH(cond, true_cond, false_cond);
|
|
RzILOpEffect *set_al = x86_il_set_reg(X86_REG_AL, LOGAND(x86_il_get_reg(X86_REG_AL), U8(0x0f)));
|
|
|
|
return SEQ2(final_cond, set_al);
|
|
}
|
|
|
|
/**
|
|
* ADC dest, src
|
|
* (ADC family of instructions)
|
|
* Add with carry
|
|
* dest = dest + src + CF
|
|
* Possible encodings:
|
|
* - I
|
|
* - MI
|
|
* - MR
|
|
* - RM
|
|
*/
|
|
IL_LIFTER(adc) {
|
|
RzILOpEffect *op1 = SETL("op1", x86_il_get_op(0));
|
|
RzILOpEffect *op2 = SETL("op2", x86_il_get_op(1));
|
|
RzILOpPure *cf = VARG(EFLAGS(CF));
|
|
|
|
RzILOpEffect *sum = SETL("sum", ADD(ADD(VARL("op1"), VARL("op2")), BOOL_TO_BV(cf, ins->operands[0].size * BITS_PER_BYTE)));
|
|
RzILOpEffect *set_dest = x86_il_set_op(0, VARL("sum"));
|
|
RzILOpEffect *set_res_flags = x86_il_set_result_flags(VARL("sum"));
|
|
RzILOpEffect *set_arith_flags = x86_il_set_arithmetic_flags(VARL("sum"), VARL("op1"), VARL("op2"), true);
|
|
|
|
return SEQ6(op1, op2, sum, set_dest, set_res_flags, set_arith_flags);
|
|
}
|
|
|
|
/**
|
|
* ADD dest, src
|
|
* (ADD family of instructions)
|
|
* Add
|
|
* dest = dest + src
|
|
* Possible encodings:
|
|
* - I
|
|
* - MI
|
|
* - MR
|
|
* - RM
|
|
*/
|
|
IL_LIFTER(add) {
|
|
RzILOpEffect *op1 = SETL("op1", x86_il_get_op(0));
|
|
RzILOpEffect *op2 = SETL("op2", x86_il_get_op(1));
|
|
RzILOpEffect *sum = SETL("sum", ADD(VARL("op1"), VARL("op2")));
|
|
|
|
RzILOpEffect *set_dest = x86_il_set_op(0, VARL("sum"));
|
|
RzILOpEffect *set_res_flags = x86_il_set_result_flags(VARL("sum"));
|
|
RzILOpEffect *set_arith_flags = x86_il_set_arithmetic_flags(VARL("sum"), VARL("op1"), VARL("op2"), true);
|
|
|
|
return SEQ6(op1, op2, sum, set_dest, set_res_flags, set_arith_flags);
|
|
}
|
|
|
|
/**
|
|
* AND dest, src
|
|
* (AND family of instructions)
|
|
* Logical and
|
|
* dest = dest & src
|
|
* Possible encodings:
|
|
* - I
|
|
* - MI
|
|
* - MR
|
|
* - RM
|
|
*/
|
|
IL_LIFTER(and) {
|
|
RzILOpPure *op1 = x86_il_get_op(0);
|
|
RzILOpPure *op2 = x86_il_get_op(1);
|
|
RzILOpEffect *and = SETL("and_", LOGAND(op1, op2));
|
|
|
|
RzILOpEffect *set_dest = x86_il_set_op(0, VARL("and_"));
|
|
RzILOpEffect *clear_of = SETG(EFLAGS(OF), IL_FALSE);
|
|
RzILOpEffect *clear_cf = SETG(EFLAGS(CF), IL_FALSE);
|
|
RzILOpEffect *set_res_flags = x86_il_set_result_flags(VARL("and_"));
|
|
|
|
return SEQ5(and, set_dest, clear_of, clear_cf, set_res_flags);
|
|
}
|
|
|
|
/**
|
|
* BSF
|
|
* Bit scan forward
|
|
* Encoding: RM
|
|
*/
|
|
IL_LIFTER(bsf) {
|
|
RzILOpPure *src = x86_il_get_op(1);
|
|
|
|
RzILOpEffect *set_zf = SETG(EFLAGS(ZF), IL_FALSE);
|
|
|
|
uint8_t op_size = ins->operands[0].size * BITS_PER_BYTE;
|
|
RzILOpEffect *tmp_var = SETL("tmpvar", UN(op_size, 0));
|
|
|
|
RzILOpEffect *calc = REPEAT(INV(LSB(SHIFTR0(src, VARL("tmpvar")))), SETL("tmpvar", ADD(VARL("tmpvar"), UN(op_size, 1))));
|
|
|
|
RzILOpEffect *calc_res = SEQ3(set_zf, tmp_var, calc);
|
|
RzILOpPure *res = VARL("tmpvar");
|
|
|
|
return BRANCH(IS_ZERO(DUP(src)), SETG(EFLAGS(ZF), IL_TRUE), SEQ2(calc_res, x86_il_set_op(0, res)));
|
|
}
|
|
|
|
/**
|
|
* CBW
|
|
* Convert byte to word
|
|
* 98 | Valid | Valid
|
|
*/
|
|
IL_LIFTER(cbw) {
|
|
/* The UNSIGNED(16, ...) cast is useless in case of 32 bits,
|
|
but removing it will cause issues for 16-bit */
|
|
return x86_il_set_reg(X86_REG_AX, UNSIGNED(16, x86_il_get_reg(X86_REG_AL)));
|
|
}
|
|
|
|
/**
|
|
* CLC
|
|
* Clear carry flag
|
|
* F8 | Valid | Valid
|
|
*/
|
|
IL_LIFTER(clc) {
|
|
return SETG(EFLAGS(CF), IL_FALSE);
|
|
}
|
|
|
|
/**
|
|
* CLD
|
|
* Clear direction flag
|
|
* FC | Valid | Valid
|
|
*/
|
|
IL_LIFTER(cld) {
|
|
return SETG(EFLAGS(DF), IL_FALSE);
|
|
}
|
|
|
|
/**
|
|
* CLI
|
|
* Clear interrupt flag
|
|
* FA | Valid | Valid
|
|
*/
|
|
IL_LIFTER(cli) {
|
|
return SETG(EFLAGS(IF), IL_FALSE);
|
|
}
|
|
|
|
/**
|
|
* CMC
|
|
* Complement carry flag
|
|
* F5 | Valid | Valid
|
|
*/
|
|
IL_LIFTER(cmc) {
|
|
return SETG(EFLAGS(CF), INV(VARG(EFLAGS(CF))));
|
|
}
|
|
|
|
/**
|
|
* CMP
|
|
* (CMP family of instructions)
|
|
* Compare two operands
|
|
* Possible encodings:
|
|
* - I
|
|
* - MI
|
|
* - MR
|
|
* - RM
|
|
*/
|
|
IL_LIFTER(cmp) {
|
|
RzILOpEffect *op1 = SETL("op1", x86_il_get_op(0));
|
|
|
|
RzILOpPure *second = x86_il_get_op(1);
|
|
RzILOpEffect *op2 = SETL("op2", second);
|
|
|
|
RzILOpEffect *sub = SETL("sub", SUB(VARL("op1"), VARL("op2")));
|
|
RzILOpEffect *arith = x86_il_set_arithmetic_flags(VARL("sub"), VARL("op1"), VARL("op2"), false);
|
|
RzILOpEffect *res = x86_il_set_result_flags(VARL("sub"));
|
|
|
|
return SEQ5(op1, op2, sub, arith, res);
|
|
}
|
|
|
|
RzILOpEffect *x86_il_cmp_helper(const X86ILIns *ins, ut64 pc, RzAnalysis *analysis, ut8 size) {
|
|
if (analysis->bits == 64) {
|
|
X86Reg mem_reg1 = X86_REG_RSI;
|
|
X86Reg mem_reg2 = X86_REG_RDI;
|
|
ut8 mem_size = 64;
|
|
|
|
/* Address override prefix: 67H */
|
|
if (ins->structure->raw.prefixes[3].value) {
|
|
mem_reg1 = X86_REG_ESI;
|
|
mem_reg2 = X86_REG_EDI;
|
|
mem_size = 32;
|
|
}
|
|
|
|
/* Cast to 64 if necessary (needed when address override prefix present) */
|
|
RzILOpEffect *src1 = SETL("_src1", LOADW(size, (mem_size == 64 ? x86_il_get_reg(mem_reg1) : UNSIGNED(64, x86_il_get_reg(mem_reg1)))));
|
|
RzILOpEffect *src2 = SETL("_src2", LOADW(size, (mem_size == 64 ? x86_il_get_reg(mem_reg2) : UNSIGNED(64, x86_il_get_reg(mem_reg2)))));
|
|
RzILOpEffect *temp = SETL("_temp", SUB(VARL("_src1"), VARL("_src2")));
|
|
|
|
RzILOpEffect *arith_flags = x86_il_set_arithmetic_flags(VARL("_temp"), VARL("_src1"), VARL("_src2"), false);
|
|
RzILOpEffect *res_flags = x86_il_set_result_flags(VARL("_temp"));
|
|
|
|
RzILOpEffect *increment = SEQ2(x86_il_set_reg(mem_reg1, ADD(x86_il_get_reg(mem_reg1), UN(mem_size, size / BITS_PER_BYTE))), x86_il_set_reg(mem_reg2, ADD(x86_il_get_reg(mem_reg2), UN(mem_size, size / BITS_PER_BYTE))));
|
|
RzILOpEffect *decrement = SEQ2(x86_il_set_reg(mem_reg1, SUB(x86_il_get_reg(mem_reg1), UN(mem_size, size / BITS_PER_BYTE))), x86_il_set_reg(mem_reg2, SUB(x86_il_get_reg(mem_reg2), UN(mem_size, size / BITS_PER_BYTE))));
|
|
|
|
return SEQ6(src1, src2, temp, arith_flags, res_flags, BRANCH(VARG(EFLAGS(DF)), decrement, increment));
|
|
} else {
|
|
X86Reg mem_reg1 = X86_REG_ESI;
|
|
X86Reg mem_reg2 = X86_REG_EDI;
|
|
ut8 mem_size = 32;
|
|
|
|
/* Address override prefix: 67H */
|
|
if (analysis->bits == 16 || ins->structure->raw.prefixes[3].value) {
|
|
mem_reg1 = X86_REG_SI;
|
|
mem_reg2 = X86_REG_DI;
|
|
mem_size = 16;
|
|
}
|
|
|
|
X86Mem src_mem1 = {
|
|
.base = mem_reg1,
|
|
.disp = { 0 },
|
|
.index = X86_REG_NONE,
|
|
.scale = 1,
|
|
.segment = X86_REG_DS
|
|
};
|
|
X86Mem src_mem2 = {
|
|
.base = mem_reg2,
|
|
.disp = { 0 },
|
|
.index = X86_REG_NONE,
|
|
.scale = 1,
|
|
.segment = X86_REG_ES
|
|
};
|
|
|
|
/* No need for casting memaddr here since the casting will be done while calculating the segmented address */
|
|
RzILOpEffect *src1 = SETL("_src1", LOADW(size, x86_il_get_memaddr(src_mem1)));
|
|
RzILOpEffect *src2 = SETL("_src2", LOADW(size, x86_il_get_memaddr(src_mem2)));
|
|
RzILOpEffect *temp = SETL("_temp", SUB(VARL("_src1"), VARL("_src2")));
|
|
|
|
RzILOpEffect *arith_flags = x86_il_set_arithmetic_flags(VARL("_temp"), VARL("_src1"), VARL("_src2"), false);
|
|
RzILOpEffect *res_flags = x86_il_set_result_flags(VARL("_temp"));
|
|
|
|
RzILOpEffect *increment = SEQ2(x86_il_set_reg(mem_reg1, ADD(x86_il_get_reg(mem_reg1), UN(mem_size, size / BITS_PER_BYTE))), x86_il_set_reg(mem_reg2, ADD(x86_il_get_reg(mem_reg2), UN(mem_size, size / BITS_PER_BYTE))));
|
|
RzILOpEffect *decrement = SEQ2(x86_il_set_reg(mem_reg1, SUB(x86_il_get_reg(mem_reg1), UN(mem_size, size / BITS_PER_BYTE))), x86_il_set_reg(mem_reg2, SUB(x86_il_get_reg(mem_reg2), UN(mem_size, size / BITS_PER_BYTE))));
|
|
|
|
return SEQ6(src1, src2, temp, arith_flags, res_flags, BRANCH(VARG(EFLAGS(DF)), increment, decrement));
|
|
}
|
|
}
|
|
|
|
RzILOpPure *get_cmov_cond(X86InsMnem ins) {
|
|
switch (ins) {
|
|
case X86_INS_CMOVNBE:
|
|
/* Same as CMOVNBE */
|
|
/* CF = 0 and ZF = 0 */
|
|
return AND(INV(VARG(EFLAGS(CF))), INV(VARG(EFLAGS(ZF))));
|
|
case X86_INS_CMOVNB:
|
|
/* Same as CMOVNB, CMOVNC */
|
|
/* CF = 0 */
|
|
return INV(VARG(EFLAGS(CF)));
|
|
case X86_INS_CMOVB:
|
|
/* Same as CMOVC, CMOVNAE */
|
|
/* CF = 1 */
|
|
return VARG(EFLAGS(CF));
|
|
case X86_INS_CMOVBE:
|
|
/* Same as CMOVNA */
|
|
/* CF = 1 or ZF = 1*/
|
|
return OR(VARG(EFLAGS(CF)), VARG(EFLAGS(ZF)));
|
|
case X86_INS_CMOVZ:
|
|
/* Same as CMOVZ */
|
|
/* ZF = 1 */
|
|
return VARG(EFLAGS(ZF));
|
|
case X86_INS_CMOVNLE:
|
|
/* Same as CMOVNLE */
|
|
/* ZF = 0 and SF = OF */
|
|
return AND(INV(VARG(EFLAGS(ZF))), INV(XOR(VARG(EFLAGS(SF)), VARG(EFLAGS(OF)))));
|
|
case X86_INS_CMOVNL:
|
|
/* Same CMOVNL */
|
|
/* SF = OF */
|
|
return INV(XOR(VARG(EFLAGS(SF)), VARG(EFLAGS(OF))));
|
|
case X86_INS_CMOVL:
|
|
/* SF != OF */
|
|
return XOR(VARG(EFLAGS(SF)), VARG(EFLAGS(OF)));
|
|
case X86_INS_CMOVLE:
|
|
/* Same as CMOVNG */
|
|
/* ZF = 1 or SF != OF */
|
|
return OR(VARG(EFLAGS(ZF)), XOR(VARG(EFLAGS(SF)), VARG(EFLAGS(OF))));
|
|
case X86_INS_CMOVNZ:
|
|
/* Same as CMOVNZ */
|
|
/* ZF = 0 */
|
|
return INV(VARG(EFLAGS(ZF)));
|
|
case X86_INS_CMOVNO:
|
|
/* OF = 0 */
|
|
return INV(VARG(EFLAGS(OF)));
|
|
case X86_INS_CMOVNP:
|
|
/* Same as CMOVPO */
|
|
/* PF = 0 */
|
|
return INV(VARG(EFLAGS(PF)));
|
|
case X86_INS_CMOVNS:
|
|
/* SF = 0 */
|
|
return INV(VARG(EFLAGS(SF)));
|
|
case X86_INS_CMOVO:
|
|
/* OF = 1 */
|
|
return VARG(EFLAGS(OF));
|
|
case X86_INS_CMOVP:
|
|
/* Same as CMOVPE */
|
|
/* PF = 1 */
|
|
return VARG(EFLAGS(PF));
|
|
case X86_INS_CMOVS:
|
|
/* SF = 1 */
|
|
return VARG(EFLAGS(SF));
|
|
default:
|
|
rz_warn_if_reached();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* CMOVcc
|
|
* (Conditional move family of instructions)
|
|
* Conditional move (based on flags)
|
|
* Encoding: RM
|
|
*/
|
|
IL_LIFTER(cmov) {
|
|
RzILOpPure *cond = get_cmov_cond(ins->mnem);
|
|
|
|
return BRANCH(cond, x86_il_set_op(0, x86_il_get_op(1)), NOP());
|
|
}
|
|
|
|
/**
|
|
* CMPSB
|
|
* Compare byte
|
|
* A6 | Valid | Valid
|
|
*/
|
|
IL_LIFTER(cmpsb) {
|
|
return x86_il_cmp_helper(ins, pc, analysis, 8);
|
|
}
|
|
|
|
/**
|
|
* CMPSW
|
|
* Compare word
|
|
* A7 | Valid | Valid
|
|
*/
|
|
IL_LIFTER(cmpsw) {
|
|
return x86_il_cmp_helper(ins, pc, analysis, 16);
|
|
}
|
|
|
|
/**
|
|
* CMPSD
|
|
* Compare dword
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(cmpsd) {
|
|
return x86_il_cmp_helper(ins, pc, analysis, 32);
|
|
}
|
|
|
|
/**
|
|
* CMPSQ
|
|
* Compare quadword
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(cmpsq) {
|
|
return x86_il_cmp_helper(ins, pc, analysis, 64);
|
|
}
|
|
|
|
/**
|
|
* DAA
|
|
* Decimal adjust AL after addition
|
|
* 27 | Invalid | Valid
|
|
*/
|
|
IL_LIFTER(daa) {
|
|
|
|
RzILOpEffect *old_al = SETL("old_al", x86_il_get_reg(X86_REG_AL));
|
|
RzILOpEffect *old_cf = SETL("old_cf", VARG(EFLAGS(CF)));
|
|
|
|
RzILOpEffect *set_cf = SETL(EFLAGS(CF), IL_FALSE);
|
|
|
|
RzILOpBool *cond = UGT(LOGAND(x86_il_get_reg(X86_REG_AL), UN(8, 0xf)), UN(8, 9));
|
|
cond = OR(cond, VARG(EFLAGS(AF)));
|
|
|
|
RzILOpEffect *set_al = SETL("_al", x86_il_get_reg(X86_REG_AL));
|
|
RzILOpEffect *sum = SETL("_sum", ADD(VARL("_al"), UN(8, 6)));
|
|
RzILOpEffect *true_cond = SEQ3(set_al, sum, x86_il_set_reg(X86_REG_AL, VARL("_sum")));
|
|
RzILOpPure *new_cf = OR(VARL("old_cf"), x86_il_is_sub_borrow(VARL("_sum"), VARL("_al"), UN(8, 6)));
|
|
|
|
RzILOpEffect *ret = SEQ4(old_al, old_cf, set_cf, BRANCH(cond, SEQ3(true_cond, SETG(EFLAGS(CF), new_cf), SETG(EFLAGS(AF), IL_TRUE)), SETG(EFLAGS(AF), IL_FALSE)));
|
|
|
|
cond = OR(UGT(VARL("old_al"), UN(8, 0x99)), VARL("old_cf"));
|
|
|
|
set_al = SETL("_al", x86_il_get_reg(X86_REG_AL));
|
|
sum = SETL("_sum", ADD(VARL("_al"), UN(8, 0x60)));
|
|
true_cond = SEQ3(set_al, sum, x86_il_set_reg(X86_REG_AL, VARL("_sum")));
|
|
|
|
ret = SEQ3(ret, BRANCH(cond, SEQ2(true_cond, SETG(EFLAGS(CF), IL_TRUE)), SETG(EFLAGS(CF), IL_FALSE)), x86_il_set_result_flags(x86_il_get_reg(X86_REG_AL)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* DAS
|
|
* Decimal adjust AL after subtraction
|
|
* 2F | Invalid | Valid
|
|
*/
|
|
IL_LIFTER(das) {
|
|
|
|
RzILOpEffect *old_al = SETL("old_al", x86_il_get_reg(X86_REG_AL));
|
|
RzILOpEffect *old_cf = SETL("old_cf", VARG(EFLAGS(CF)));
|
|
|
|
RzILOpEffect *set_cf = SETL(EFLAGS(CF), IL_FALSE);
|
|
|
|
RzILOpBool *cond = UGT(LOGAND(x86_il_get_reg(X86_REG_AL), UN(8, 0xf)), UN(8, 9));
|
|
cond = OR(cond, VARG(EFLAGS(AF)));
|
|
|
|
RzILOpEffect *set_al = SETL("_al", x86_il_get_reg(X86_REG_AL));
|
|
RzILOpEffect *sum = SETL("_sum", SUB(VARL("_al"), UN(8, 6)));
|
|
RzILOpEffect *true_cond = SEQ3(set_al, sum, x86_il_set_reg(X86_REG_AL, VARL("_sum")));
|
|
RzILOpPure *new_cf = OR(VARL("old_cf"), x86_il_is_sub_borrow(VARL("_sum"), VARL("_al"), UN(8, 6)));
|
|
|
|
RzILOpEffect *ret = SEQ4(old_al, old_cf, set_cf, BRANCH(cond, SEQ3(true_cond, SETG(EFLAGS(CF), new_cf), SETG(EFLAGS(AF), IL_TRUE)), SETG(EFLAGS(AF), IL_FALSE)));
|
|
|
|
cond = OR(UGT(VARL("old_al"), UN(8, 0x99)), VARL("old_cf"));
|
|
|
|
set_al = SETL("_al", x86_il_get_reg(X86_REG_AL));
|
|
sum = SETL("_sum", SUB(VARL("_al"), UN(8, 0x60)));
|
|
true_cond = SEQ3(set_al, sum, x86_il_set_reg(X86_REG_AL, VARL("_sum")));
|
|
|
|
ret = SEQ3(ret, BRANCH(cond, SEQ2(true_cond, SETG(EFLAGS(CF), IL_TRUE)), NOP()), x86_il_set_result_flags(x86_il_get_reg(X86_REG_AL)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* DEC
|
|
* Decrement by 1
|
|
* Operand can be a memory address or a register
|
|
*/
|
|
IL_LIFTER(dec) {
|
|
RzILOpEffect *op = SETL("_op", x86_il_get_op(0));
|
|
RzILOpEffect *dec = SETL("_dec", SUB(VARL("_op"), UN(ins->operands[0].size * BITS_PER_BYTE, 1)));
|
|
|
|
RzILOpEffect *set_result = x86_il_set_op(0, VARL("_dec"));
|
|
RzILOpEffect *res_flags = x86_il_set_result_flags(VARL("_dec"));
|
|
|
|
RzILOpEffect *arith_flags = x86_il_set_arithmetic_flags_except_cf(VARL("_dec"), VARL("_op"), UN(ins->operands[0].size * BITS_PER_BYTE, 1), false);
|
|
|
|
return SEQ5(op, dec, set_result, res_flags, arith_flags);
|
|
}
|
|
|
|
/**
|
|
* DIV
|
|
* Unsigned division
|
|
* One operand (memory address), used as the divisor
|
|
*/
|
|
IL_LIFTER(div) {
|
|
RzILOpEffect *ret = NULL;
|
|
|
|
switch (ins->operands[0].size) {
|
|
case 1: {
|
|
/* Word/Byte operation */
|
|
RzILOpEffect *ax = SETL("_ax", x86_il_get_reg(X86_REG_AX));
|
|
RzILOpEffect *temp = SETL("_temp", UNSIGNED(8, DIV(VARL("_ax"), VARL("_src"))));
|
|
|
|
RzILOpPure *cond = UGT(VARL("_temp"), UN(8, 0xff));
|
|
RzILOpEffect *else_cond = SEQ2(x86_il_set_reg(X86_REG_AL, VARL("_temp")), x86_il_set_reg(X86_REG_AH, MOD(VARL("_ax"), VARL("_src"))));
|
|
|
|
ret = SEQ3(ax, temp, BRANCH(cond, NULL, else_cond));
|
|
break;
|
|
}
|
|
case 2: {
|
|
/* Doubleword/Word operation */
|
|
RzILOpEffect *dxax = SETL("_dxax", LOGOR(SHIFTL0(UNSIGNED(32, x86_il_get_reg(X86_REG_DX)), UN(8, 16)), UNSIGNED(32, x86_il_get_reg(X86_REG_AX))));
|
|
RzILOpEffect *temp = SETL("_temp", UNSIGNED(16, DIV(VARL("_dxax"), VARL("_src"))));
|
|
|
|
RzILOpPure *cond = UGT(VARL("_temp"), UN(16, 0xffff));
|
|
RzILOpEffect *else_cond = SEQ2(x86_il_set_reg(X86_REG_AX, VARL("_temp")), x86_il_set_reg(X86_REG_DX, MOD(VARL("_dxax"), VARL("_src"))));
|
|
|
|
ret = SEQ3(dxax, temp, BRANCH(cond, NULL, else_cond));
|
|
break;
|
|
}
|
|
case 4: {
|
|
/* Quadword/Doubleword operation */
|
|
RzILOpEffect *edxeax = SETL("_edxeax", LOGOR(SHIFTL0(UNSIGNED(64, x86_il_get_reg(X86_REG_EDX)), UN(8, 32)), UNSIGNED(64, x86_il_get_reg(X86_REG_EAX))));
|
|
RzILOpEffect *temp = SETL("_temp", UNSIGNED(32, DIV(VARL("_edxeax"), VARL("_src"))));
|
|
|
|
RzILOpPure *cond = UGT(VARL("_temp"), UN(32, 0xffffffffULL));
|
|
RzILOpEffect *else_cond = SEQ2(x86_il_set_reg(X86_REG_AX, VARL("_temp")), x86_il_set_reg(X86_REG_DX, MOD(VARL("_edxeax"), VARL("_src"))));
|
|
|
|
ret = SEQ3(edxeax, temp, BRANCH(cond, NULL, else_cond));
|
|
break;
|
|
}
|
|
case 8: {
|
|
/* Doublequadword/Quadword operation */
|
|
RzILOpEffect *rdxrax = SETL("_rdxrax", LOGOR(SHIFTL0(UNSIGNED(128, x86_il_get_reg(X86_REG_RDX)), UN(8, 64)), UNSIGNED(128, x86_il_get_reg(X86_REG_RAX))));
|
|
RzILOpEffect *temp = SETL("_temp", UNSIGNED(64, DIV(VARL("_rdxrax"), VARL("_src"))));
|
|
|
|
RzILOpPure *cond = UGT(VARL("_temp"), UN(64, 0xffffffffffffffffULL));
|
|
RzILOpEffect *else_cond = SEQ2(x86_il_set_reg(X86_REG_AX, VARL("_temp")), x86_il_set_reg(X86_REG_DX, MOD(VARL("_rdxrax"), VARL("_src"))));
|
|
|
|
ret = SEQ3(rdxrax, temp, BRANCH(cond, NULL, else_cond));
|
|
break;
|
|
}
|
|
default:
|
|
RZ_LOG_ERROR("RzIL: x86: DIV: Invalid operand size\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* We need the divisor to be as wide as the operand, since the sizes of the dividend and the divisor need to match */
|
|
RzILOpEffect *op = SETL("_src", UNSIGNED(ins->operands[0].size * 2 * BITS_PER_BYTE, x86_il_get_op(0)));
|
|
|
|
/* Check if the operand is zero, return NULL if it is (to avoid divide by zero) */
|
|
return SEQ2(op, BRANCH(IS_ZERO(VARL("_src")), NULL, ret));
|
|
}
|
|
|
|
/**
|
|
* ESC
|
|
* Escape to coprocessor instruction set
|
|
* To be used with floating-point unit
|
|
* Not necessary to implement for binary analysis
|
|
*/
|
|
|
|
/**
|
|
* HLT
|
|
* Enter HALT state
|
|
*/
|
|
IL_LIFTER(hlt) {
|
|
/* It just jumps to an empty goto label "halt" */
|
|
return GOTO("halt");
|
|
}
|
|
|
|
void label_halt(RzILVM *vm, RzILOpEffect *op) {
|
|
// empty "halt" label
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* IDIV
|
|
* Signed division
|
|
* One operand (memory address), used as the divisor
|
|
*/
|
|
IL_LIFTER(idiv) {
|
|
RzILOpEffect *ret = NULL;
|
|
|
|
switch (ins->operands[0].size) {
|
|
case 1: {
|
|
/* Word/Byte operation */
|
|
RzILOpEffect *ax = SETL("_ax", x86_il_get_reg(X86_REG_AX));
|
|
RzILOpEffect *temp = SETL("_temp", UNSIGNED(8, SDIV(VARL("_ax"), VARL("_src"))));
|
|
|
|
RzILOpPure *cond = OR(SGT(VARL("_temp"), UN(8, 0x7f)), SLT(VARL("_temp"), UN(8, 0x80)));
|
|
RzILOpEffect *else_cond = SEQ2(x86_il_set_reg(X86_REG_AL, VARL("_temp")), x86_il_set_reg(X86_REG_AH, SMOD(VARL("_ax"), VARL("_src"))));
|
|
|
|
ret = SEQ3(ax, temp, BRANCH(cond, NULL, else_cond));
|
|
break;
|
|
}
|
|
case 2: {
|
|
/* Doubleword/Word operation */
|
|
RzILOpEffect *dxax = SETL("_dxax", LOGOR(SHIFTL0(UNSIGNED(32, x86_il_get_reg(X86_REG_DX)), UN(8, 16)), UNSIGNED(32, x86_il_get_reg(X86_REG_AX))));
|
|
RzILOpEffect *temp = SETL("_temp", UNSIGNED(16, SDIV(VARL("_dxax"), VARL("_src"))));
|
|
|
|
RzILOpPure *cond = OR(SGT(VARL("_temp"), UN(16, 0x7fff)), SLT(VARL("_temp"), UN(16, 0x8000)));
|
|
RzILOpEffect *else_cond = SEQ2(x86_il_set_reg(X86_REG_AX, VARL("_temp")), x86_il_set_reg(X86_REG_DX, SMOD(VARL("_dxax"), VARL("_src"))));
|
|
|
|
ret = SEQ3(dxax, temp, BRANCH(cond, NULL, else_cond));
|
|
break;
|
|
}
|
|
case 4: {
|
|
/* Quadword/Doubleword operation */
|
|
RzILOpEffect *edxeax = SETL("_edxeax", LOGOR(SHIFTL0(UNSIGNED(64, x86_il_get_reg(X86_REG_EDX)), UN(8, 32)), UNSIGNED(64, x86_il_get_reg(X86_REG_EAX))));
|
|
RzILOpEffect *temp = SETL("_temp", UNSIGNED(32, SDIV(VARL("_edxeax"), VARL("_src"))));
|
|
|
|
RzILOpPure *cond = OR(SGT(VARL("_temp"), UN(32, 0x7fffffffULL)), SLT(VARL("_temp"), UN(32, 0x80000000ULL)));
|
|
RzILOpEffect *else_cond = SEQ2(x86_il_set_reg(X86_REG_AX, VARL("_temp")), x86_il_set_reg(X86_REG_DX, SMOD(VARL("_edxeax"), VARL("_src"))));
|
|
|
|
ret = SEQ3(edxeax, temp, BRANCH(cond, NULL, else_cond));
|
|
break;
|
|
}
|
|
case 8: {
|
|
/* Doublequadword/Quadword operation */
|
|
RzILOpEffect *rdxrax = SETL("_rdxrax", LOGOR(SHIFTL0(UNSIGNED(128, x86_il_get_reg(X86_REG_EDX)), UN(8, 64)), UNSIGNED(128, x86_il_get_reg(X86_REG_EAX))));
|
|
RzILOpEffect *temp = SETL("_temp", UNSIGNED(64, SDIV(VARL("_rdxrax"), VARL("_src"))));
|
|
|
|
RzILOpPure *cond = OR(SGT(VARL("_temp"), UN(64, 0x7fffffffffffffffULL)), SLT(VARL("_temp"), UN(64, 0x8000000000000000ULL)));
|
|
RzILOpEffect *else_cond = SEQ2(x86_il_set_reg(X86_REG_AX, VARL("_temp")), x86_il_set_reg(X86_REG_DX, SMOD(VARL("_rdxrax"), VARL("_src"))));
|
|
|
|
ret = SEQ3(rdxrax, temp, BRANCH(cond, NULL, else_cond));
|
|
break;
|
|
}
|
|
default:
|
|
RZ_LOG_ERROR("RzIL: x86: IDIV: Invalid operand size\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* We need the divisor to be as wide as the operand, since the sizes of the dividend and the divisor need to match */
|
|
RzILOpEffect *op = SETL("_src", UNSIGNED(ins->operands[0].size * 2 * BITS_PER_BYTE, x86_il_get_op(0)));
|
|
|
|
/* Check if the operand is zero, return NULL if it is (to avoid divide by zero) */
|
|
return SEQ2(op, BRANCH(IS_ZERO(VARL("_src")), NULL, ret));
|
|
}
|
|
|
|
/**
|
|
* IMUL
|
|
* Signed multiply
|
|
* Three different operand number:
|
|
* - One operand (Encoding: M)
|
|
* - Two operands (Encoding: RM)
|
|
* - Three operands (Encoding: RMI)
|
|
*/
|
|
IL_LIFTER(imul) {
|
|
switch (ins->structure->operand_count_visible) {
|
|
case 1: {
|
|
switch (ins->operands[0].size) {
|
|
case 1: {
|
|
RzILOpEffect *tmp_xp = SETL("_tmp_xp", MUL(SIGNED(16, x86_il_get_reg(X86_REG_AL)), SIGNED(16, x86_il_get_op(0))));
|
|
RzILOpEffect *set_ax = x86_il_set_reg(X86_REG_AX, VARL("_tmp_xp"));
|
|
|
|
/* Check if the result fits in a byte */
|
|
RzILOpPure *cond = EQ(SIGNED(16, UNSIGNED(8, VARL("_tmp_xp"))), VARL("_tmp_xp"));
|
|
RzILOpEffect *true_branch = SEQ2(SETG(EFLAGS(CF), IL_FALSE), SETG(EFLAGS(OF), IL_FALSE));
|
|
RzILOpEffect *false_branch = SEQ2(SETG(EFLAGS(CF), IL_TRUE), SETG(EFLAGS(OF), IL_TRUE));
|
|
|
|
return SEQ3(tmp_xp, set_ax, BRANCH(cond, true_branch, false_branch));
|
|
}
|
|
case 2: {
|
|
RzILOpEffect *tmp_xp = SETL("_tmp_xp", MUL(SIGNED(32, x86_il_get_reg(X86_REG_AX)), SIGNED(32, x86_il_get_op(0))));
|
|
RzILOpEffect *set_ax = x86_il_set_reg(X86_REG_AX, UNSIGNED(16, VARL("_tmp_xp")));
|
|
RzILOpEffect *set_dx = x86_il_set_reg(X86_REG_DX, UNSIGNED(32, SHIFTR0(VARL("_tmp_xp"), UN(8, 16))));
|
|
|
|
/* Check if the result fits in a word */
|
|
RzILOpPure *cond = EQ(SIGNED(32, UNSIGNED(16, VARL("_tmp_xp"))), VARL("_tmp_xp"));
|
|
RzILOpEffect *true_branch = SEQ2(SETG(EFLAGS(CF), IL_FALSE), SETG(EFLAGS(OF), IL_FALSE));
|
|
RzILOpEffect *false_branch = SEQ2(SETG(EFLAGS(CF), IL_TRUE), SETG(EFLAGS(OF), IL_TRUE));
|
|
|
|
return SEQ4(tmp_xp, set_ax, set_dx, BRANCH(cond, true_branch, false_branch));
|
|
}
|
|
case 4: {
|
|
RzILOpEffect *tmp_xp = SETL("_tmp_xp", MUL(SIGNED(64, x86_il_get_reg(X86_REG_EAX)), SIGNED(64, x86_il_get_op(0))));
|
|
RzILOpEffect *set_eax = x86_il_set_reg(X86_REG_EAX, UNSIGNED(32, VARL("_tmp_xp")));
|
|
RzILOpEffect *set_edx = x86_il_set_reg(X86_REG_EDX, UNSIGNED(32, SHIFTR0(VARL("_tmp_xp"), UN(8, 32))));
|
|
|
|
/* Check if the result fits in a doubleword */
|
|
RzILOpPure *cond = EQ(SIGNED(64, UNSIGNED(32, VARL("_tmp_xp"))), VARL("_tmp_xp"));
|
|
RzILOpEffect *true_branch = SEQ2(SETG(EFLAGS(CF), IL_FALSE), SETG(EFLAGS(OF), IL_FALSE));
|
|
RzILOpEffect *false_branch = SEQ2(SETG(EFLAGS(CF), IL_TRUE), SETG(EFLAGS(OF), IL_TRUE));
|
|
|
|
return SEQ4(tmp_xp, set_eax, set_edx, BRANCH(cond, true_branch, false_branch));
|
|
}
|
|
case 8: {
|
|
RzILOpEffect *tmp_xp = SETL("_tmp_xp", MUL(SIGNED(128, x86_il_get_reg(X86_REG_RAX)), SIGNED(128, x86_il_get_op(0))));
|
|
RzILOpEffect *set_rax = x86_il_set_reg(X86_REG_RAX, UNSIGNED(64, VARL("_tmp_xp")));
|
|
RzILOpEffect *set_rdx = x86_il_set_reg(X86_REG_RDX, UNSIGNED(64, SHIFTR0(VARL("_tmp_xp"), UN(8, 64))));
|
|
|
|
/* Check if the result fits in a quadword */
|
|
RzILOpPure *cond = EQ(SIGNED(128, UNSIGNED(64, VARL("_tmp_xp"))), VARL("_tmp_xp"));
|
|
RzILOpEffect *true_branch = SEQ2(SETG(EFLAGS(CF), IL_FALSE), SETG(EFLAGS(OF), IL_FALSE));
|
|
RzILOpEffect *false_branch = SEQ2(SETG(EFLAGS(CF), IL_TRUE), SETG(EFLAGS(OF), IL_TRUE));
|
|
|
|
return SEQ4(tmp_xp, set_rax, set_rdx, BRANCH(cond, true_branch, false_branch));
|
|
}
|
|
default:
|
|
RZ_LOG_ERROR("RzIL: x86: IMUL: Invalid operand size\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
case 2: {
|
|
RzILOpEffect *dest = SETL("_dest", x86_il_get_op(0));
|
|
RzILOpEffect *tmp_xp = SETL("_tmp_xp", MUL(SIGNED(ins->operands[0].size * 2 * BITS_PER_BYTE, VARL("_dest")), SIGNED(ins->operands[0].size * 2 * BITS_PER_BYTE, x86_il_get_op(1))));
|
|
RzILOpEffect *set_dest = SETL("_dest", UNSIGNED(ins->operands[0].size * BITS_PER_BYTE, VARL("_tmp_xp")));
|
|
RzILOpEffect *set_operand = x86_il_set_op(0, VARL("_dest"));
|
|
|
|
/* Check if the result fits in the destination */
|
|
RzILOpPure *cond = EQ(SIGNED(ins->operands[0].size * 2 * BITS_PER_BYTE, VARL("_dest")), VARL("_tmp_xp"));
|
|
RzILOpEffect *true_branch = SEQ2(SETG(EFLAGS(CF), IL_FALSE), SETG(EFLAGS(OF), IL_FALSE));
|
|
RzILOpEffect *false_branch = SEQ2(SETG(EFLAGS(CF), IL_TRUE), SETG(EFLAGS(OF), IL_TRUE));
|
|
|
|
return SEQ5(dest, tmp_xp, set_dest, set_operand, BRANCH(cond, true_branch, false_branch));
|
|
}
|
|
case 3: {
|
|
RzILOpEffect *tmp_xp = SETL("_tmp_xp", MUL(SIGNED(ins->operands[1].size * 2 * BITS_PER_BYTE, x86_il_get_op(1)), SIGNED(ins->operands[1].size * 2 * BITS_PER_BYTE, x86_il_get_op(2))));
|
|
RzILOpEffect *set_dest = SETL("_dest", UNSIGNED(ins->operands[0].size * BITS_PER_BYTE, VARL("_tmp_xp")));
|
|
RzILOpEffect *set_operand = x86_il_set_op(0, VARL("_dest"));
|
|
|
|
/* Check if the result fits in the destination */
|
|
RzILOpPure *cond = EQ(SIGNED(ins->operands[0].size * 2 * BITS_PER_BYTE, VARL("_dest")), VARL("_tmp_xp"));
|
|
RzILOpEffect *true_branch = SEQ2(SETG(EFLAGS(CF), IL_FALSE), SETG(EFLAGS(OF), IL_FALSE));
|
|
RzILOpEffect *false_branch = SEQ2(SETG(EFLAGS(CF), IL_TRUE), SETG(EFLAGS(OF), IL_TRUE));
|
|
|
|
return SEQ4(tmp_xp, set_dest, set_operand, BRANCH(cond, true_branch, false_branch));
|
|
}
|
|
default:
|
|
RZ_LOG_ERROR("RzIL: x86: IMUL: Invalid operand count\n");
|
|
return NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* IN
|
|
* Input from port
|
|
* Encodings: I, ZO
|
|
*/
|
|
IL_LIFTER(in) {
|
|
/* It just jumps to an empty goto label "port" */
|
|
return GOTO("port");
|
|
}
|
|
|
|
void label_port(RzILVM *vm, RzILOpEffect *op) {
|
|
// empty "port" label
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* INC
|
|
* Increment by 1
|
|
* Encodings: M, O
|
|
*/
|
|
IL_LIFTER(inc) {
|
|
RzILOpEffect *op = SETL("_op", x86_il_get_op(0));
|
|
RzILOpEffect *result = SETL("_result", ADD(VARL("_op"), UN(ins->operands[0].size * BITS_PER_BYTE, 1)));
|
|
RzILOpEffect *set_result = x86_il_set_op(0, VARL("_result"));
|
|
|
|
RzILOpEffect *arith_flags = x86_il_set_arithmetic_flags_except_cf(VARL("_result"), VARL("_op"), UN(ins->operands[0].size * BITS_PER_BYTE, 1), true);
|
|
RzILOpEffect *res_flags = x86_il_set_result_flags(VARL("_result"));
|
|
|
|
return SEQ5(op, result, set_result, arith_flags, res_flags);
|
|
}
|
|
|
|
/**
|
|
* INT
|
|
* Call to interrupt procedure
|
|
* Encodings: I, ZO
|
|
*/
|
|
IL_LIFTER(int) {
|
|
/* For now, it just jumps to an empty goto label "int" */
|
|
return GOTO("int");
|
|
}
|
|
|
|
void label_int(RzILVM *vm, RzILOpEffect *op) {
|
|
// empty "int" label
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* INTO
|
|
* Call to interrupt if overflow flag set
|
|
*/
|
|
IL_LIFTER(into) {
|
|
return BRANCH(VARG(EFLAGS(OF)), GOTO("int"), NOP());
|
|
}
|
|
|
|
// TODO: Implement IRET
|
|
/**
|
|
* IRET
|
|
* Return from interrupt
|
|
*/
|
|
|
|
#define JUMP_IL() \
|
|
do { \
|
|
RzILOpPure *jmp_addr = UN(analysis->bits, imm_value(ins->operands[0], pc)); \
|
|
if (ins->operands[0].size == 16 && analysis->bits != 64) { \
|
|
jmp_addr = LOGAND(jmp_addr, UN(analysis->bits, 0x0000ffff)); \
|
|
} \
|
|
return BRANCH(cond, JMP(jmp_addr), NOP()); \
|
|
} while (0)
|
|
|
|
/**
|
|
* JA
|
|
* Jump if above (CF = 0 and ZF = 0)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(ja) {
|
|
RzILOpBool *cond = AND(INV(VARG(EFLAGS(CF))), INV(VARG(EFLAGS(ZF))));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JAE
|
|
* Jump if above or equal (CF = 0)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jae) {
|
|
RzILOpBool *cond = INV(VARG(EFLAGS(CF)));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JB
|
|
* Jump if below (CF = 1)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jb) {
|
|
RzILOpBool *cond = VARG(EFLAGS(CF));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JBE
|
|
* Jump if below or equal (CF = 1 or ZF = 1)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jbe) {
|
|
RzILOpBool *cond = OR(VARG(EFLAGS(CF)), VARG(EFLAGS(ZF)));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JCXZ
|
|
* Jump if CX register is zero (CX = 0)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jcxz) {
|
|
RzILOpBool *cond = IS_ZERO(x86_il_get_reg(X86_REG_CX));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JECXZ
|
|
* Jump if ECX register is zero (ECX = 0)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jecxz) {
|
|
RzILOpBool *cond = IS_ZERO(x86_il_get_reg(X86_REG_ECX));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JRCXZ
|
|
* Jump if RCX register is zero (RCX = 0)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jrcxz) {
|
|
RzILOpBool *cond = IS_ZERO(x86_il_get_reg(X86_REG_RCX));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JE
|
|
* Jump if equal (ZF = 1)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(je) {
|
|
RzILOpBool *cond = VARG(EFLAGS(ZF));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JG
|
|
* Jump if greater (ZF = 0 and SF = OF)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jg) {
|
|
RzILOpBool *cond = AND(INV(VARG(EFLAGS(ZF))), INV(XOR(VARG(EFLAGS(SF)), VARG(EFLAGS(OF)))));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JGE
|
|
* Jump if greater or equal (SF = OF)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jge) {
|
|
RzILOpBool *cond = INV(XOR(VARG(EFLAGS(SF)), VARG(EFLAGS(OF))));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JL
|
|
* Jump if less or equal (SF != OF)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jl) {
|
|
RzILOpBool *cond = XOR(VARG(EFLAGS(SF)), VARG(EFLAGS(OF)));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JLE
|
|
* Jump if less or equal (ZF = 1 or SF != OF)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jle) {
|
|
RzILOpBool *cond = OR(VARG(EFLAGS(ZF)), XOR(VARG(EFLAGS(SF)), VARG(EFLAGS(OF))));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JNE
|
|
* Jump if not equal (ZF = 0)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jne) {
|
|
RzILOpBool *cond = INV(VARG(EFLAGS(ZF)));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JNO
|
|
* Jump if not overflow (OF = 0)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jno) {
|
|
RzILOpBool *cond = INV(VARG(EFLAGS(OF)));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JNP
|
|
* Jump if not parity (PF = 0)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jnp) {
|
|
RzILOpBool *cond = INV(VARG(EFLAGS(PF)));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JNS
|
|
* Jump if not sign (SF = 0)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jns) {
|
|
RzILOpBool *cond = INV(VARG(EFLAGS(SF)));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JO
|
|
* Jump if overflow (OF = 1)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jo) {
|
|
RzILOpBool *cond = VARG(EFLAGS(OF));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JP
|
|
* Jump if parity (PF = 1)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(jp) {
|
|
RzILOpBool *cond = VARG(EFLAGS(PF));
|
|
JUMP_IL();
|
|
}
|
|
|
|
/**
|
|
* JS
|
|
* Jump if sign (SF = 1)
|
|
* Encoding: D
|
|
*/
|
|
IL_LIFTER(js) {
|
|
RzILOpBool *cond = VARG(EFLAGS(SF));
|
|
JUMP_IL();
|
|
}
|
|
|
|
#undef JUMP_IL
|
|
|
|
/**
|
|
* JMP
|
|
* Jump
|
|
* Relative jump or absolute jump decided by the encoding of the operands
|
|
* Possible encodings:
|
|
* - S (Segment + absolute address)
|
|
* - D (Offset/Displacement)
|
|
* - M
|
|
*/
|
|
IL_LIFTER(jmp) {
|
|
return JMP(UNSIGNED(analysis->bits, x86_il_get_op(0)));
|
|
}
|
|
|
|
/**
|
|
* LAHF
|
|
* Load status flags in the AH register
|
|
* No operands
|
|
*/
|
|
IL_LIFTER(lahf) {
|
|
return x86_il_set_reg(X86_REG_AH, x86_il_get_flags(8));
|
|
}
|
|
|
|
/**
|
|
* LDS
|
|
* Load pointer using DS
|
|
* Encoding: RM
|
|
*/
|
|
IL_LIFTER(lds) {
|
|
return x86_il_set_op(0, x86_il_get_memaddr_segment(ins->operands[1].mem, X86_REG_DS));
|
|
}
|
|
|
|
/**
|
|
* LEA
|
|
* Load effective address
|
|
* Encoding: RM
|
|
* Cast the M to R in an unsigned cast
|
|
*/
|
|
IL_LIFTER(lea) {
|
|
return x86_il_set_op(0, UNSIGNED(ins->operands[1].size * BITS_PER_BYTE, x86_il_get_memaddr(ins->operands[1].mem)));
|
|
}
|
|
|
|
/**
|
|
* LES
|
|
* Load pointer using ES
|
|
* Encoding: RM
|
|
*/
|
|
IL_LIFTER(les) {
|
|
return x86_il_set_op(0, x86_il_get_memaddr_segment(ins->operands[1].mem, X86_REG_ES));
|
|
}
|
|
|
|
RzILOpEffect *x86_il_lods_helper(const X86ILIns *ins, ut64 pc, RzAnalysis *analysis, ut8 size) {
|
|
X86Reg reg;
|
|
switch (size) {
|
|
case 8:
|
|
reg = X86_REG_AL;
|
|
break;
|
|
case 16:
|
|
reg = X86_REG_AX;
|
|
break;
|
|
case 32:
|
|
reg = X86_REG_EAX;
|
|
break;
|
|
case 64:
|
|
reg = X86_REG_RAX;
|
|
break;
|
|
default:
|
|
rz_warn_if_reached();
|
|
return NULL;
|
|
}
|
|
|
|
if (analysis->bits == 64) {
|
|
X86Reg mem_reg = X86_REG_RSI;
|
|
ut8 mem_size = 64;
|
|
/* Address override prefix: 67H */
|
|
if (ins->structure->raw.prefixes[3].value) {
|
|
mem_reg = X86_REG_ESI;
|
|
mem_size = 32;
|
|
}
|
|
|
|
/* Cast to 64 if necessary (needed when address override prefix present) */
|
|
RzILOpPure *val = LOADW(size, (mem_size == 64 ? x86_il_get_reg(mem_reg) : UNSIGNED(64, x86_il_get_reg(mem_reg))));
|
|
RzILOpEffect *inc = x86_il_set_reg(mem_reg, ADD(x86_il_get_reg(mem_reg), UN(mem_size, size / BITS_PER_BYTE)));
|
|
RzILOpEffect *dec = x86_il_set_reg(mem_reg, SUB(x86_il_get_reg(mem_reg), UN(mem_size, size / BITS_PER_BYTE)));
|
|
RzILOpEffect *update_rsi = BRANCH(VARG(EFLAGS(DF)), dec, inc);
|
|
|
|
return SEQ2(x86_il_set_reg(reg, val), update_rsi);
|
|
|
|
} else {
|
|
X86Reg mem_reg = X86_REG_ESI;
|
|
ut8 mem_size = 32;
|
|
/* Address override prefix: 67H */
|
|
if (analysis->bits == 16 || ins->structure->raw.prefixes[3].value) {
|
|
mem_reg = X86_REG_SI;
|
|
mem_size = 16;
|
|
}
|
|
|
|
X86Mem src_mem;
|
|
src_mem.base = mem_reg;
|
|
src_mem.disp.value = 0;
|
|
src_mem.index = X86_REG_NONE;
|
|
src_mem.scale = 1;
|
|
src_mem.segment = X86_REG_DS;
|
|
|
|
/* No need for casting memaddr here since the casting will be done while calculating the segmented address */
|
|
RzILOpPure *val = LOADW(size, x86_il_get_memaddr(src_mem));
|
|
|
|
RzILOpEffect *inc = x86_il_set_reg(mem_reg, ADD(x86_il_get_reg(mem_reg), UN(mem_size, size / BITS_PER_BYTE)));
|
|
RzILOpEffect *dec = x86_il_set_reg(mem_reg, SUB(x86_il_get_reg(mem_reg), UN(mem_size, size / BITS_PER_BYTE)));
|
|
RzILOpEffect *update_si = BRANCH(VARG(EFLAGS(DF)), dec, inc);
|
|
|
|
return SEQ2(x86_il_set_reg(reg, val), update_si);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* LODSB
|
|
* Load string byte
|
|
* No operands
|
|
*/
|
|
IL_LIFTER(lodsb) {
|
|
return x86_il_lods_helper(ins, pc, analysis, 8);
|
|
}
|
|
|
|
/**
|
|
* LODSW
|
|
* Load string word
|
|
* No operands
|
|
*/
|
|
IL_LIFTER(lodsw) {
|
|
return x86_il_lods_helper(ins, pc, analysis, 16);
|
|
}
|
|
|
|
/**
|
|
* LODSD
|
|
* Load string dword
|
|
* No operands
|
|
*/
|
|
IL_LIFTER(lodsd) {
|
|
return x86_il_lods_helper(ins, pc, analysis, 32);
|
|
}
|
|
|
|
/**
|
|
* LODSQ
|
|
* Load string quadword
|
|
* No operands
|
|
*/
|
|
IL_LIFTER(lodsq) {
|
|
return x86_il_lods_helper(ins, pc, analysis, 64);
|
|
}
|
|
|
|
#define LOOP_HELPER(cond) \
|
|
do { \
|
|
/* Will automatically be resolved to the widest CX register */ \
|
|
X86Reg count_reg = X86_REG_RCX; \
|
|
\
|
|
uint8_t addr_size = analysis->bits; \
|
|
/* Check address override prefix (67H) */ \
|
|
if (analysis->bits == 64 && ins->structure->raw.prefixes[3].value) { \
|
|
addr_size >>= 1; \
|
|
count_reg = X86_REG_ECX; \
|
|
} \
|
|
\
|
|
RzILOpEffect *dec_counter = x86_il_set_reg(count_reg, SUB(x86_il_get_reg(count_reg), UN(addr_size, 1))); \
|
|
RzILOpEffect *true_cond = JMP(UN(analysis->bits, pc + imm_value(ins->operands[0], pc))); \
|
|
RzILOpEffect *branch = BRANCH(cond, true_cond, NOP()); \
|
|
\
|
|
return SEQ2(dec_counter, branch); \
|
|
} while (0)
|
|
|
|
/**
|
|
* LOOP
|
|
* Loop the following instruction
|
|
* Encoding: D
|
|
* Decrement count ; jump if count != 0
|
|
*/
|
|
IL_LIFTER(loop) {
|
|
LOOP_HELPER(NON_ZERO(x86_il_get_reg(count_reg)));
|
|
}
|
|
|
|
/**
|
|
* LOOPE
|
|
* Loop the following instruction
|
|
* Encoding: D
|
|
* Decrement count ; jump if count != 0 and ZF = 1
|
|
*/
|
|
IL_LIFTER(loope) {
|
|
LOOP_HELPER(AND(NON_ZERO(x86_il_get_reg(count_reg)), VARG(EFLAGS(ZF))));
|
|
}
|
|
|
|
/**
|
|
* LOOPNE
|
|
* Loop the following instruction
|
|
* Encoding: D
|
|
* Decrement count ; jump if count != 0 and ZF = 0
|
|
*/
|
|
IL_LIFTER(loopne) {
|
|
LOOP_HELPER(AND(NON_ZERO(x86_il_get_reg(count_reg)), INV(VARG(EFLAGS(ZF)))));
|
|
}
|
|
|
|
/**
|
|
* MOV
|
|
* Move
|
|
* Encodings:
|
|
* - MR
|
|
* - RM
|
|
* - FD
|
|
* - TD
|
|
* - OI
|
|
* - MI
|
|
*/
|
|
IL_LIFTER(mov) {
|
|
return x86_il_set_op(0, x86_il_get_op(1));
|
|
}
|
|
|
|
RzILOpEffect *x86_il_movs_helper(const X86ILIns *ins, ut64 pc, RzAnalysis *analysis, ut8 size) {
|
|
if (analysis->bits == 64) {
|
|
X86Reg mem_reg1 = X86_REG_RSI;
|
|
X86Reg mem_reg2 = X86_REG_RDI;
|
|
ut8 mem_size = 64;
|
|
|
|
/* Address override prefix: 67H */
|
|
if (ins->structure->raw.prefixes[3].value) {
|
|
mem_reg1 = X86_REG_ESI;
|
|
mem_reg2 = X86_REG_EDI;
|
|
mem_size = 32;
|
|
}
|
|
|
|
/* Cast to 64 if necessary (needed when address override prefix present) */
|
|
RzILOpPure *val = LOADW(size, (mem_size == 64 ? x86_il_get_reg(mem_reg1) : UNSIGNED(64, x86_il_get_reg(mem_reg1))));
|
|
RzILOpEffect *inc = SEQ2(x86_il_set_reg(mem_reg1, ADD(x86_il_get_reg(mem_reg1), UN(mem_size, size / BITS_PER_BYTE))), x86_il_set_reg(mem_reg2, ADD(x86_il_get_reg(mem_reg2), UN(mem_size, size / BITS_PER_BYTE))));
|
|
RzILOpEffect *dec = SEQ2(x86_il_set_reg(mem_reg1, SUB(x86_il_get_reg(mem_reg1), UN(mem_size, size / BITS_PER_BYTE))), x86_il_set_reg(mem_reg2, SUB(x86_il_get_reg(mem_reg2), UN(mem_size, size / BITS_PER_BYTE))));
|
|
RzILOpEffect *update = BRANCH(VARG(EFLAGS(DF)), dec, inc);
|
|
|
|
return SEQ2(STOREW((mem_size == 64 ? x86_il_get_reg(mem_reg2) : UNSIGNED(64, x86_il_get_reg(mem_reg2))), val), update);
|
|
|
|
} else {
|
|
X86Reg src_reg = X86_REG_ESI;
|
|
X86Reg dst_reg = X86_REG_EDI;
|
|
ut8 mem_size = 32;
|
|
|
|
/* Address override prefix: 67H */
|
|
if (analysis->bits == 16 || ins->structure->raw.prefixes[3].value) {
|
|
src_reg = X86_REG_SI;
|
|
dst_reg = X86_REG_DI;
|
|
mem_size = 16;
|
|
}
|
|
|
|
X86Mem src_mem = {
|
|
.base = src_reg,
|
|
.disp = { 0 },
|
|
.index = X86_REG_NONE,
|
|
.scale = 1,
|
|
.segment = X86_REG_DS
|
|
};
|
|
X86Mem dst_mem = {
|
|
.base = dst_reg,
|
|
.disp = { 0 },
|
|
.index = X86_REG_NONE,
|
|
.scale = 1,
|
|
.segment = X86_REG_ES
|
|
};
|
|
|
|
RzILOpEffect *inc = SEQ2(x86_il_set_reg(src_reg, ADD(x86_il_get_reg(src_reg), UN(mem_size, size / BITS_PER_BYTE))), x86_il_set_reg(dst_reg, ADD(x86_il_get_reg(dst_reg), UN(mem_size, size / BITS_PER_BYTE))));
|
|
RzILOpEffect *dec = SEQ2(x86_il_set_reg(src_reg, SUB(x86_il_get_reg(src_reg), UN(mem_size, size / BITS_PER_BYTE))), x86_il_set_reg(dst_reg, SUB(x86_il_get_reg(dst_reg), UN(mem_size, size / BITS_PER_BYTE))));
|
|
RzILOpEffect *update = BRANCH(VARG(EFLAGS(DF)), dec, inc);
|
|
|
|
/* No need for casting memaddr here since the casting will be done while calculating the segmented address */
|
|
return SEQ2(x86_il_set_mem(dst_mem, LOADW(size, x86_il_get_memaddr(src_mem))), update);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* MOVSB
|
|
* Move string byte
|
|
* No operands
|
|
*/
|
|
IL_LIFTER(movsb) {
|
|
return x86_il_movs_helper(ins, pc, analysis, 8);
|
|
}
|
|
|
|
/**
|
|
* MOVSW
|
|
* Move string word
|
|
* No operands
|
|
*/
|
|
IL_LIFTER(movsw) {
|
|
return x86_il_movs_helper(ins, pc, analysis, 16);
|
|
}
|
|
|
|
/**
|
|
* MOVSD
|
|
* Move string dword
|
|
* No operands
|
|
*/
|
|
IL_LIFTER(movsd) {
|
|
return x86_il_movs_helper(ins, pc, analysis, 32);
|
|
}
|
|
|
|
/**
|
|
* MOVSQ
|
|
* Move string quadword
|
|
* No operands
|
|
*/
|
|
IL_LIFTER(movsq) {
|
|
return x86_il_movs_helper(ins, pc, analysis, 64);
|
|
}
|
|
|
|
/**
|
|
* MOVSX, MOVSXD
|
|
* Move with sign extension
|
|
*
|
|
* Encoding: RM
|
|
*/
|
|
IL_LIFTER(movsx) {
|
|
return x86_il_set_op(0, SIGNED(ins->operands[0].size * BITS_PER_BYTE, x86_il_get_op(1)));
|
|
}
|
|
|
|
/**
|
|
* MOVZX
|
|
* Move with zero extension
|
|
* Encoding: RM
|
|
*/
|
|
IL_LIFTER(movzx) {
|
|
return x86_il_set_op(0, UNSIGNED(ins->operands[0].size * BITS_PER_BYTE, x86_il_get_op(1)));
|
|
}
|
|
|
|
/**
|
|
* MUL
|
|
* Unsigned multiply
|
|
* Encoding: M
|
|
*/
|
|
IL_LIFTER(mul) {
|
|
RzILOpPure *op = UNSIGNED(ins->operands[0].size * BITS_PER_BYTE * 2, x86_il_get_op(0));
|
|
RzILOpEffect *true_cond = SEQ2(SETG(EFLAGS(OF), IL_FALSE), SETG(EFLAGS(CF), IL_FALSE));
|
|
RzILOpEffect *false_cond = SEQ2(SETG(EFLAGS(OF), IL_TRUE), SETG(EFLAGS(CF), IL_TRUE));
|
|
|
|
switch (ins->operands[0].size) {
|
|
case 1: {
|
|
RzILOpEffect *set = SETL("_mul", MUL(UNSIGNED(16, x86_il_get_reg(X86_REG_AL)), op));
|
|
RzILOpEffect *ax = x86_il_set_reg(X86_REG_AX, VARL("_mul"));
|
|
RzILOpEffect *flags = BRANCH(IS_ZERO(SHIFTR0(VARL("_mul"), U8(8))), true_cond, false_cond);
|
|
|
|
return SEQ3(set, ax, flags);
|
|
}
|
|
case 2: {
|
|
RzILOpEffect *set = SETL("_mul", MUL(UNSIGNED(32, x86_il_get_reg(X86_REG_AX)), op));
|
|
RzILOpEffect *dx = x86_il_set_reg(X86_REG_DX, UNSIGNED(16, SHIFTR0(VARL("_mul"), U8(16))));
|
|
RzILOpEffect *ax = x86_il_set_reg(X86_REG_AX, UNSIGNED(16, VARL("_mul")));
|
|
RzILOpEffect *flags = BRANCH(IS_ZERO(SHIFTR0(VARL("_mul"), U8(16))), true_cond, false_cond);
|
|
|
|
return SEQ4(set, dx, ax, flags);
|
|
}
|
|
case 4: {
|
|
RzILOpEffect *set = SETL("_mul", MUL(UNSIGNED(64, x86_il_get_reg(X86_REG_EAX)), op));
|
|
RzILOpEffect *edx = x86_il_set_reg(X86_REG_EDX, UNSIGNED(32, SHIFTR0(VARL("_mul"), U8(32))));
|
|
RzILOpEffect *eax = x86_il_set_reg(X86_REG_EAX, UNSIGNED(32, VARL("_mul")));
|
|
RzILOpEffect *flags = BRANCH(IS_ZERO(SHIFTR0(VARL("_mul"), U8(32))), true_cond, false_cond);
|
|
|
|
return SEQ4(set, edx, eax, flags);
|
|
}
|
|
case 8: {
|
|
RzILOpEffect *set = SETL("_mul", MUL(UNSIGNED(128, x86_il_get_reg(X86_REG_RAX)), op));
|
|
RzILOpEffect *rdx = x86_il_set_reg(X86_REG_RDX, UNSIGNED(64, SHIFTR0(VARL("_mul"), U8(64))));
|
|
RzILOpEffect *rax = x86_il_set_reg(X86_REG_RAX, UNSIGNED(64, VARL("_mul")));
|
|
RzILOpEffect *flags = BRANCH(IS_ZERO(SHIFTR0(VARL("_mul"), U8(64))), true_cond, false_cond);
|
|
|
|
return SEQ4(set, rdx, rax, flags);
|
|
}
|
|
}
|
|
|
|
rz_warn_if_reached();
|
|
rz_il_op_pure_free(op);
|
|
rz_il_op_effect_free(true_cond);
|
|
rz_il_op_effect_free(false_cond);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* NEG
|
|
* Two's complement negation
|
|
* Encoding: M
|
|
*/
|
|
IL_LIFTER(neg) {
|
|
RzILOpEffect *op = SETL("_op", x86_il_get_op(0));
|
|
RzILOpEffect *cf = BRANCH(IS_ZERO(VARL("_op")), SETG(EFLAGS(CF), IL_FALSE), SETG(EFLAGS(CF), IL_TRUE));
|
|
RzILOpEffect *neg = x86_il_set_op(0, NEG(VARL("_op")));
|
|
|
|
return SEQ3(op, cf, neg);
|
|
}
|
|
|
|
/**
|
|
* NOP
|
|
* No operation
|
|
* Encoding:
|
|
* - ZO (zero operands)
|
|
* - M (multi-byte nop)
|
|
*/
|
|
IL_LIFTER(nop) {
|
|
return NOP();
|
|
}
|
|
|
|
/**
|
|
* NOT
|
|
* One's complement negation
|
|
* Encoding: M
|
|
*/
|
|
IL_LIFTER(not) {
|
|
return x86_il_set_op(0, LOGNOT(x86_il_get_op(0)));
|
|
}
|
|
|
|
/**
|
|
* OR
|
|
* Logical inclusive or
|
|
* Encoding:
|
|
* - I
|
|
* - MI
|
|
* - MR
|
|
* - RM
|
|
*/
|
|
IL_LIFTER(or) {
|
|
RzILOpPure *op1 = x86_il_get_op(0);
|
|
RzILOpPure *op2 = x86_il_get_op(1);
|
|
RzILOpEffect * or = SETL("_or", LOGOR(op1, op2));
|
|
|
|
RzILOpEffect *set_dest = x86_il_set_op(0, VARL("_or"));
|
|
RzILOpEffect *clear_of = SETG(EFLAGS(OF), IL_FALSE);
|
|
RzILOpEffect *clear_cf = SETG(EFLAGS(CF), IL_FALSE);
|
|
RzILOpEffect *set_res_flags = x86_il_set_result_flags(VARL("_or"));
|
|
|
|
return SEQ5(or, set_dest, clear_of, clear_cf, set_res_flags);
|
|
}
|
|
|
|
/**
|
|
* OUT
|
|
* Output to port
|
|
* Encodings: I, ZO
|
|
*/
|
|
IL_LIFTER(out) {
|
|
/* It just jumps to an empty goto label "port" */
|
|
return GOTO("port");
|
|
}
|
|
|
|
typedef struct pop_helper_t {
|
|
RzILOpPure *val;
|
|
RzILOpEffect *eff;
|
|
} PopHelper;
|
|
|
|
PopHelper x86_pop_helper_bits(unsigned int op_size, unsigned int bitness, ut64 pc) {
|
|
X86Mem stack_mem;
|
|
/* The correct register will automatically be chosen if we use RSP */
|
|
stack_mem.base = X86_REG_RSP;
|
|
stack_mem.disp.value = 0;
|
|
stack_mem.index = X86_REG_NONE;
|
|
stack_mem.scale = 1;
|
|
stack_mem.segment = X86_REG_SS;
|
|
|
|
PopHelper ret;
|
|
|
|
ret.val = LOADW(op_size * BITS_PER_BYTE, x86_il_get_memaddr_bits(stack_mem, bitness, pc));
|
|
ret.eff = x86_il_set_reg_bits(X86_REG_RSP, ADD(x86_il_get_reg_bits(X86_REG_RSP, bitness, pc), UN(bitness, op_size)), bitness);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define x86_pop_helper(op_size) x86_pop_helper_bits(op_size, analysis->bits, pc)
|
|
|
|
/**
|
|
* POP
|
|
* Pop a value from the stack
|
|
* Encoding:
|
|
* - M
|
|
* - O
|
|
* - ZO
|
|
*/
|
|
IL_LIFTER(pop) {
|
|
/* Ideally, we should use the stack size instead of the address size (analysis->bits),
|
|
but there seems to be no way to do that using Capstone */
|
|
/* Also, it is very rare to use have a different stack size and address size */
|
|
PopHelper pop = x86_pop_helper(ins->operands[0].size);
|
|
RzILOpEffect *copy = x86_il_set_op(0, pop.val);
|
|
|
|
return SEQ2(copy, pop.eff);
|
|
}
|
|
|
|
/**
|
|
* POPF
|
|
* Pop stack into FLAGS register (16 bits)
|
|
* Encoding: ZO
|
|
*/
|
|
IL_LIFTER(popf) {
|
|
/* This is not _completely_ accurate, but it is good enough for our purposes */
|
|
PopHelper pop = x86_pop_helper(2 /* BYTES */);
|
|
return SEQ2(x86_il_set_flags(pop.val, 16), pop.eff);
|
|
}
|
|
|
|
/**
|
|
* POPFD
|
|
* Pop stack into EFLAGS register (32 bits)
|
|
* Encoding: ZO
|
|
*/
|
|
IL_LIFTER(popfd) {
|
|
/* Functionally the same as POPF IL */
|
|
PopHelper pop = x86_pop_helper(4 /* BYTES */);
|
|
return SEQ2(x86_il_set_flags(pop.val, 32), pop.eff);
|
|
}
|
|
|
|
/**
|
|
* POPFQ
|
|
* Pop stack into RFLAGS register (64 bits)
|
|
* Encoding: ZO
|
|
*/
|
|
IL_LIFTER(popfq) {
|
|
/* Functionally the same as POPF IL */
|
|
PopHelper pop = x86_pop_helper(8 /* BYTES */);
|
|
return SEQ2(x86_il_set_flags(pop.val, 64), pop.eff);
|
|
}
|
|
|
|
RzILOpEffect *x86_push_helper_impl(RzILOpPure *val, unsigned int user_op_size, unsigned int bitness, const X86ILIns *ins, ut64 pc) {
|
|
unsigned int dflag = user_op_size;
|
|
unsigned int op_size;
|
|
unsigned int stack_size = bitness / BITS_PER_BYTE;
|
|
|
|
if (ins) {
|
|
if (bitness == 64) {
|
|
dflag = ins->structure->raw.rex.W ? 8 : ins->structure->raw.prefixes[2].value ? 2
|
|
: 4;
|
|
stack_size = 8; /* in bytes */
|
|
} else {
|
|
/* We use the other operand and address size if the prefix is set */
|
|
if ((bitness == 32) ^ ins->structure->raw.prefixes[2].value) {
|
|
dflag = 4;
|
|
} else {
|
|
dflag = 2;
|
|
}
|
|
|
|
stack_size = 4;
|
|
}
|
|
}
|
|
|
|
if (bitness == 64) {
|
|
op_size = (dflag == 2) ? 2 : 8;
|
|
} else {
|
|
op_size = dflag;
|
|
}
|
|
|
|
RzILOpEffect *final_stack = SETL("final", SUB(x86_il_get_reg_bits(X86_REG_RSP, bitness, pc), UN(bitness, stack_size)));
|
|
|
|
RzILOpEffect *ret = STOREW(VARL("final"), UNSIGNED(op_size * BITS_PER_BYTE, val));
|
|
ret = SEQ3(final_stack, ret, x86_il_set_reg_bits(X86_REG_RSP, VARL("final"), bitness));
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define x86_push_helper(val, op_size) x86_push_helper_impl(val, op_size, analysis->bits, NULL, pc)
|
|
|
|
/**
|
|
* CALL
|
|
* Perform a function call
|
|
* Encoding: D, M
|
|
*/
|
|
IL_LIFTER(call) {
|
|
/*
|
|
* The following implementation is not accurate, since there are many nitty-gritties involved.
|
|
* Like whether the call is a near or far call, absolute or relative call, etc.
|
|
* Implementing it accurately will require exceptions, task switching,
|
|
* shadow stack, CPU internal flags, segmentation support
|
|
* Just pushing the current program counter is a good approximation for now
|
|
* shadow stack, CPU internal flags, segmentation support
|
|
* Just pushing the current program counter is a good approcimation for now
|
|
* We also need to push the code segment register in case 16 and 32 bit modes.
|
|
*/
|
|
|
|
RzILOpEffect *push_pc = x86_push_helper(UN(analysis->bits, pc), analysis->bits / BITS_PER_BYTE);
|
|
return SEQ2(push_pc, JMP(x86_il_get_op(0)));
|
|
}
|
|
|
|
/**
|
|
* PUSH
|
|
* Push value on the stack
|
|
* Encoding:
|
|
* - M
|
|
* - O
|
|
* - I
|
|
* - ZO
|
|
*/
|
|
IL_LIFTER(push) {
|
|
return x86_push_helper_impl(x86_il_get_op(0), ins->operands->size, analysis->bits, ins, pc);
|
|
}
|
|
|
|
/**
|
|
* PUSHF
|
|
* Push FLAGS register onto the stack (16 bits)
|
|
* Encoding: ZO
|
|
*/
|
|
IL_LIFTER(pushf) {
|
|
return x86_push_helper(x86_il_get_flags(16), 2);
|
|
}
|
|
|
|
/**
|
|
* PUSHFD
|
|
* Push EFLAGS register onto the stack (32 bits)
|
|
* Encoding: ZO
|
|
*/
|
|
IL_LIFTER(pushfd) {
|
|
return x86_push_helper(x86_il_get_flags(32), 4);
|
|
}
|
|
|
|
/**
|
|
* PUSHFQ
|
|
* Push RFLAGS register onto the stack (64 bits)
|
|
* Encoding: ZO
|
|
*/
|
|
IL_LIFTER(pushfq) {
|
|
return x86_push_helper(x86_il_get_flags(64), 8);
|
|
}
|
|
|
|
/**
|
|
* PUSHA
|
|
* Push all general-purpose registers (16-bits)
|
|
* Encoding: ZO
|
|
*/
|
|
IL_LIFTER(pushaw) {
|
|
if (analysis->bits != 16) {
|
|
return NULL;
|
|
}
|
|
|
|
RzILOpEffect *temp = SETL("_sp", x86_il_get_reg(X86_REG_SP));
|
|
RzILOpEffect *push = x86_push_helper(x86_il_get_reg(X86_REG_AX), 2);
|
|
push = SEQ2(push, x86_push_helper(x86_il_get_reg(X86_REG_CX), 2));
|
|
push = SEQ2(push, x86_push_helper(x86_il_get_reg(X86_REG_DX), 2));
|
|
push = SEQ2(push, x86_push_helper(x86_il_get_reg(X86_REG_BX), 2));
|
|
push = SEQ2(push, x86_push_helper(VARL("_sp"), 2));
|
|
push = SEQ2(push, x86_push_helper(x86_il_get_reg(X86_REG_BP), 2));
|
|
push = SEQ2(push, x86_push_helper(x86_il_get_reg(X86_REG_SI), 2));
|
|
push = SEQ2(push, x86_push_helper(x86_il_get_reg(X86_REG_DI), 2));
|
|
|
|
return SEQ2(temp, push);
|
|
}
|
|
|
|
#define RCX_MACRO() \
|
|
ut8 size_in_bytes = ins->operands[0].size; \
|
|
ut8 size = size_in_bytes * BITS_PER_BYTE; \
|
|
ut8 shift_size = (size_in_bytes == 8 || (analysis->bits == 64 && ins->structure->raw.rex.W)) ? 6 : 5; \
|
|
ut8 shift_max = size + 1; \
|
|
RzILOpEffect *pre_dest = SETL("_pre_dest", x86_il_get_op(0)); \
|
|
RzILOpEffect *shift = NULL; \
|
|
switch (size_in_bytes) { \
|
|
case 1: \
|
|
/* fall-thru */ \
|
|
case 2: \
|
|
shift = SETL("_shift", MOD(UNSIGNED(shift_size, x86_il_get_op_implicit(1, size_in_bytes)), UN(shift_size, shift_max))); \
|
|
break; \
|
|
case 4: \
|
|
/* fall-thru */ \
|
|
case 8: \
|
|
shift = SETL("_shift", UNSIGNED(shift_size, x86_il_get_op_implicit(1, size_in_bytes))); \
|
|
break; \
|
|
default: \
|
|
rz_warn_if_reached(); \
|
|
}
|
|
|
|
/**
|
|
* RCL
|
|
* Rotate left, with carry
|
|
* Encoding: MI, M1, MC
|
|
*/
|
|
IL_LIFTER(rcl) {
|
|
RCX_MACRO();
|
|
|
|
RzILOpEffect *rotated = SETL("_rotated",
|
|
LET("_cf_dest", APPEND(BOOL_TO_BV(VARG(EFLAGS(CF)), 1), VARL("_pre_dest")),
|
|
LOGOR(
|
|
SHIFTL0(VARLP("_cf_dest"), VARL("_shift")),
|
|
SHIFTR0(VARLP("_cf_dest"), SUB(UN(shift_size, shift_max), VARL("_shift"))))));
|
|
|
|
RzILOpEffect *result = SETL("_dest", CAST(size, IL_FALSE, VARL("_rotated")));
|
|
RzILOpEffect *set_dest = x86_il_set_op(0, VARL("_dest"));
|
|
|
|
// if _shift == 0, CF is unaffected.
|
|
RzILOpEffect *set_cf = SETG(EFLAGS(CF), MSB(VARL("_rotated")));
|
|
|
|
// if _shift == 1, OF is set to 'CF xor MSB (after rotation)'.
|
|
// otherwise, OF is undefined.
|
|
RzILOpEffect *set_of = SETG(EFLAGS(OF), XOR(VARG(EFLAGS(CF)), MSB(VARL("_dest"))));
|
|
|
|
return SEQ7(pre_dest, shift, rotated, result, set_cf, set_of, set_dest);
|
|
}
|
|
|
|
/**
|
|
* RCR
|
|
* Rotate right, with carry
|
|
* Encoding: MI, M1, MC
|
|
*/
|
|
IL_LIFTER(rcr) {
|
|
RCX_MACRO();
|
|
RzILOpEffect *rotated = SETL("_rotated",
|
|
LET("_cf_dest", APPEND(BOOL_TO_BV(VARG(EFLAGS(CF)), 1), VARL("_pre_dest")),
|
|
LOGOR(
|
|
SHIFTR0(VARLP("_cf_dest"), VARL("_shift")),
|
|
SHIFTL0(VARLP("_cf_dest"), SUB(UN(shift_size, shift_max), VARL("_shift"))))));
|
|
|
|
RzILOpEffect *result = SETL("_dest", CAST(size, IL_FALSE, VARL("_rotated")));
|
|
RzILOpEffect *set_dest = x86_il_set_op(0, VARL("_dest"));
|
|
|
|
// if _shift == 0, CF is unaffected.
|
|
RzILOpEffect *set_cf = SETG(EFLAGS(CF), MSB(VARL("_rotated")));
|
|
|
|
// if _shift == 1, OF is set to 'CF xor MSB (before rotation)'.
|
|
// otherwise, OF is undefined.
|
|
RzILOpEffect *set_of = SETG(EFLAGS(OF), XOR(VARG(EFLAGS(CF)), MSB(VARL("_pre_dest"))));
|
|
|
|
return SEQ7(pre_dest, shift, rotated, result, set_of, set_cf, set_dest);
|
|
}
|
|
|
|
#undef RCX_MACRO
|
|
|
|
#define ROX_MACRO() \
|
|
ut8 size_in_bytes = ins->operands[0].size; \
|
|
ut8 size = size_in_bytes * BITS_PER_BYTE; \
|
|
ut8 shift_size = (size_in_bytes == 8 || (analysis->bits == 64 && ins->structure->raw.rex.W)) ? 6 : 5; \
|
|
ut8 shift_max = size; \
|
|
RzILOpEffect *pre_dest = SETL("_pre_dest", x86_il_get_op(0)); \
|
|
RzILOpEffect *shift = SETL("_shift", MOD(UNSIGNED(shift_size, x86_il_get_op_implicit(1, size_in_bytes)), UN(shift_size, shift_max)));
|
|
|
|
/**
|
|
* ROL
|
|
* Rotate left
|
|
* Encoding: MI, M1, MC
|
|
*/
|
|
IL_LIFTER(rol) {
|
|
ROX_MACRO();
|
|
|
|
RzILOpEffect *rotated = SETL("_rotated",
|
|
LOGOR(
|
|
SHIFTL0(VARL("_pre_dest"), VARL("_shift")),
|
|
SHIFTR0(VARL("_pre_dest"), SUB(UN(shift_size, shift_max), VARL("_shift")))));
|
|
|
|
RzILOpEffect *result = SETL("_dest", CAST(size, IL_FALSE, VARL("_rotated")));
|
|
RzILOpEffect *set_dest = x86_il_set_op(0, VARL("_dest"));
|
|
|
|
// if _shift == 0, CF is unaffected.
|
|
RzILOpEffect *set_cf = SETG(EFLAGS(CF), ITE(IS_ZERO(VARL("_shift")), VARG(EFLAGS(CF)), LSB(VARL("_rotated"))));
|
|
|
|
// if _shift == 1, OF is set to 'CF xor MSB (after rotation)'.
|
|
// otherwise, OF is undefined.
|
|
RzILOpEffect *set_of = SETG(EFLAGS(OF), XOR(VARG(EFLAGS(CF)), MSB(VARL("_dest"))));
|
|
|
|
return SEQ7(pre_dest, shift, rotated, result, set_cf, set_of, set_dest);
|
|
}
|
|
|
|
/**
|
|
* ROR
|
|
* Rotate right
|
|
* Encoding: MI, M1, MC
|
|
*/
|
|
IL_LIFTER(ror) {
|
|
ROX_MACRO();
|
|
|
|
RzILOpEffect *rotated = SETL("_rotated",
|
|
LOGOR(
|
|
SHIFTR0(VARL("_pre_dest"), VARL("_shift")),
|
|
SHIFTL0(VARL("_pre_dest"), SUB(UN(shift_size, shift_max), VARL("_shift")))));
|
|
|
|
RzILOpEffect *result = SETL("_dest", CAST(size, IL_FALSE, VARL("_rotated")));
|
|
RzILOpEffect *set_dest = x86_il_set_op(0, VARL("_dest"));
|
|
|
|
// if _shift == 0, CF is unaffected.
|
|
RzILOpEffect *set_cf = SETG(EFLAGS(CF), ITE(IS_ZERO(VARL("_shift")), VARG(EFLAGS(CF)), MSB(VARL("_rotated"))));
|
|
|
|
// if _shift == 1, OF is set to 'CF xor MSB (before rotation)'.
|
|
// otherwise, OF is undefined.
|
|
RzILOpEffect *set_of = SETG(EFLAGS(OF), XOR(VARG(EFLAGS(CF)), MSB(VARL("_pre_dest"))));
|
|
|
|
return SEQ7(pre_dest, shift, rotated, result, set_of, set_cf, set_dest);
|
|
}
|
|
|
|
#undef ROX_MACRO
|
|
|
|
/**
|
|
* RET
|
|
* Return (near pointer)
|
|
* Encoding: ZO, I
|
|
* Most modern x86-32 and x86-64 programs use this return instruction
|
|
*/
|
|
IL_LIFTER(ret) {
|
|
PopHelper ph = x86_pop_helper(analysis->bits / BITS_PER_BYTE /* BYTES */);
|
|
|
|
RzILOpEffect *ret = SEQ2(SETL("tgt", ph.val), ph.eff);
|
|
|
|
if (ins->structure->operand_count_visible == 1) {
|
|
/* Immediate operand (Encoding: I)
|
|
* Reduce RSP by that many bytes. */
|
|
ret = SEQ2(ret, x86_il_set_reg(X86_REG_RSP, ADD(x86_il_get_reg(X86_REG_RSP), UN(analysis->bits, imm_value(ins->operands[0], pc)))));
|
|
}
|
|
|
|
return SEQ2(ret, JMP(VARL("tgt")));
|
|
}
|
|
|
|
/**
|
|
* RETF
|
|
* Return far pointer
|
|
* Encoding: ZO, I
|
|
* Rarely found in modern programs
|
|
*/
|
|
|
|
/**
|
|
* RETFQ
|
|
* Return far pointer (size: qword)
|
|
* Encoding: ZO, I
|
|
* Rarely found in modern programs
|
|
*/
|
|
|
|
/**
|
|
* SAHF
|
|
* Store AH into FLAGS
|
|
* Encoding: ZO
|
|
*/
|
|
IL_LIFTER(sahf) {
|
|
return x86_il_set_flags(x86_il_get_reg(X86_REG_AH), 8);
|
|
}
|
|
|
|
#define SHIFT_MACRO() \
|
|
ut8 size = ins->operands[0].size * BITS_PER_BYTE; \
|
|
ut8 shift_size = ins->operands[1].size * BITS_PER_BYTE; \
|
|
ut8 shift_mask = (analysis->bits == 64 && ins->structure->raw.rex.W) ? 0x3f : 0x1f; \
|
|
RzILOpEffect *pre_dest = SETL("_pre_dest", x86_il_get_op(0)); \
|
|
RzILOpEffect *shift = SETL("_shift", LOGAND(x86_il_get_op(1), UN(shift_size, shift_mask))); \
|
|
RzILOpEffect *set_dest = x86_il_set_op(0, VARL("_dest")); \
|
|
RzILOpEffect *set_flags = BRANCH(IS_ZERO(VARL("_shift")), NULL, x86_il_set_result_flags(VARL("_dest")));
|
|
|
|
/**
|
|
* SAR
|
|
* Shift arithmetically right (signed shift right)
|
|
* Encoding: M1, MC, MI
|
|
*/
|
|
IL_LIFTER(sar) {
|
|
SHIFT_MACRO();
|
|
|
|
RzILOpEffect *shifted = SETL("_shifted",
|
|
SHIFTR(MSB(VARL("_pre_dest")), APPEND(VARL("_pre_dest"), BOOL_TO_BV(VARG(EFLAGS(CF)), 1)), VARL("_shift")));
|
|
|
|
RzILOpEffect *result = SETL("_dest", UNSIGNED(size, SHIFTR0(VARL("_shifted"), U8(1))));
|
|
|
|
// if _shift == 0, CF is unaffected.
|
|
RzILOpEffect *set_cf = SETG(EFLAGS(CF), LSB(VARL("_shifted")));
|
|
|
|
// if _shift == 1, OF is set to false.
|
|
// otherwise, OF is undefined.
|
|
RzILOpEffect *set_of = SETG(EFLAGS(OF), IL_FALSE);
|
|
|
|
return SEQ8(pre_dest, shift, shifted, result, set_cf, set_of, set_flags, set_dest);
|
|
}
|
|
|
|
/**
|
|
* SHL
|
|
* Shift left (unsigned shift left)
|
|
* Encoding: M1, MC, MI
|
|
* (Functionally the same as SAL)
|
|
*/
|
|
IL_LIFTER(shl) {
|
|
SHIFT_MACRO();
|
|
|
|
RzILOpEffect *shifted = SETL("_shifted", SHIFTL0(UNSIGNED(size + 1, VARL("_pre_dest")), VARL("_shift")));
|
|
|
|
RzILOpEffect *result = SETL("_dest", UNSIGNED(size, VARL("_shifted")));
|
|
|
|
// if _shift == 0, CF is unaffected.
|
|
RzILOpEffect *set_cf = SETG(EFLAGS(CF), MSB(VARL("_shifted")));
|
|
|
|
// if _shift == 1, OF is set to 'CF xor MSB (after shift)'.
|
|
// otherwise, OF is undefined.
|
|
RzILOpEffect *set_of = SETG(EFLAGS(OF), XOR(VARG(EFLAGS(CF)), MSB(VARL("_dest"))));
|
|
|
|
return SEQ8(pre_dest, shift, shifted, result, set_cf, set_of, set_flags, set_dest);
|
|
}
|
|
|
|
/**
|
|
* SHR
|
|
* Shift right (unsigned shift left)
|
|
* Encoding: M1, MC, MI
|
|
*/
|
|
IL_LIFTER(shr) {
|
|
SHIFT_MACRO();
|
|
|
|
RzILOpEffect *shifted = SETL("_shifted",
|
|
SHIFTR0(APPEND(VARL("_pre_dest"), BOOL_TO_BV(VARG(EFLAGS(CF)), 1)), VARL("_shift")));
|
|
|
|
RzILOpEffect *result = SETL("_dest", UNSIGNED(size, SHIFTR0(VARL("_shifted"), U8(1))));
|
|
|
|
// if _shift == 0, CF is unaffected.
|
|
RzILOpEffect *set_cf = SETG(EFLAGS(CF), LSB(VARL("_shifted")));
|
|
|
|
// if _shift == 1, OF is set to MSB (before shift).
|
|
// otherwise, OF is undefined.
|
|
RzILOpEffect *set_of = SETG(EFLAGS(OF), MSB(VARL("_pre_dest")));
|
|
|
|
return SEQ8(pre_dest, shift, shifted, result, set_cf, set_of, set_flags, set_dest);
|
|
}
|
|
|
|
/**
|
|
* SBB
|
|
* Subtraction with borrow
|
|
* DEST = DEST - (SRC + CF)
|
|
* Encoding: I, MI, MR, RM
|
|
*/
|
|
IL_LIFTER(sbb) {
|
|
RzILOpEffect *op1 = SETL("_op1", x86_il_get_op(0));
|
|
RzILOpEffect *op2 = SETL("_op2", x86_il_get_op(1));
|
|
RzILOpPure *cf = VARG(EFLAGS(CF));
|
|
|
|
RzILOpEffect *diff = SETL("_diff", SUB(SUB(VARL("_op1"), VARL("_op2")), BOOL_TO_BV(cf, ins->operands[0].size * BITS_PER_BYTE)));
|
|
RzILOpEffect *set_dest = x86_il_set_op(0, VARL("_diff"));
|
|
RzILOpEffect *set_res_flags = x86_il_set_result_flags(VARL("_diff"));
|
|
RzILOpEffect *set_arith_flags = x86_il_set_arithmetic_flags(VARL("_diff"), VARL("_op1"), VARL("_op2"), false);
|
|
|
|
return SEQ6(op1, op2, diff, set_dest, set_res_flags, set_arith_flags);
|
|
}
|
|
|
|
RzILOpEffect *x86_il_scas_helper(const X86ILIns *ins, ut64 pc, RzAnalysis *analysis, ut8 size) {
|
|
X86Reg sub_reg;
|
|
|
|
switch (size) {
|
|
case 8:
|
|
sub_reg = X86_REG_AL;
|
|
break;
|
|
case 16:
|
|
sub_reg = X86_REG_AX;
|
|
break;
|
|
case 32:
|
|
sub_reg = X86_REG_EAX;
|
|
break;
|
|
case 64:
|
|
sub_reg = X86_REG_RAX;
|
|
break;
|
|
default:
|
|
rz_warn_if_reached();
|
|
return NULL;
|
|
}
|
|
|
|
if (analysis->bits == 64) {
|
|
X86Reg mem_reg = X86_REG_RDI;
|
|
ut8 mem_size = 64;
|
|
/* Address override prefix: 67H */
|
|
if (ins->structure->raw.prefixes[3].value) {
|
|
mem_reg = X86_REG_EDI;
|
|
mem_size = 32;
|
|
}
|
|
|
|
/* Cast to 64 if necessary (needed when address override prefix present) */
|
|
RzILOpEffect *src = SETL("_src", LOADW(size, (mem_size == 64 ? x86_il_get_reg(mem_reg) : UNSIGNED(64, x86_il_get_reg(mem_reg)))));
|
|
RzILOpEffect *reg = SETL("_reg", x86_il_get_reg(sub_reg));
|
|
RzILOpEffect *temp = SETL("_temp", SUB(VARL("_reg"), VARL("_src")));
|
|
RzILOpEffect *arith_flags = x86_il_set_arithmetic_flags(VARL("_temp"), VARL("_reg"), VARL("_src"), false);
|
|
RzILOpEffect *res_flags = x86_il_set_result_flags(VARL("_temp"));
|
|
|
|
RzILOpEffect *increment = x86_il_set_reg(mem_reg, ADD(x86_il_get_reg(mem_reg), UN(mem_size, size / BITS_PER_BYTE)));
|
|
RzILOpEffect *decrement = x86_il_set_reg(mem_reg, SUB(x86_il_get_reg(mem_reg), UN(mem_size, size / BITS_PER_BYTE)));
|
|
|
|
return SEQ6(reg, src, temp, arith_flags, res_flags, BRANCH(VARG(EFLAGS(DF)), decrement, increment));
|
|
} else {
|
|
RzILOpEffect *reg = SETL("_reg", x86_il_get_reg(sub_reg));
|
|
|
|
X86Reg mem_reg = X86_REG_EDI;
|
|
ut8 mem_size = 32;
|
|
/* Check bitness and address override prefix: 67H */
|
|
if (analysis->bits == 16 || ins->structure->raw.prefixes[3].value) {
|
|
mem_reg = X86_REG_DI;
|
|
mem_size = 16;
|
|
}
|
|
|
|
X86Mem src_mem;
|
|
src_mem.base = mem_reg;
|
|
src_mem.disp.value = 0;
|
|
src_mem.index = X86_REG_NONE;
|
|
src_mem.scale = 1;
|
|
src_mem.segment = X86_REG_ES;
|
|
|
|
/* No need for casting memaddr here since the casting will be done while calculating the segmented address */
|
|
RzILOpEffect *src = SETL("_src", LOADW(size, x86_il_get_memaddr(src_mem)));
|
|
RzILOpEffect *temp = SETL("_temp", SUB(VARL("_reg"), VARL("_src")));
|
|
RzILOpEffect *arith_flags = x86_il_set_arithmetic_flags(VARL("_temp"), VARL("_reg"), VARL("_src"), false);
|
|
RzILOpEffect *res_flags = x86_il_set_result_flags(VARL("_temp"));
|
|
|
|
RzILOpEffect *increment = x86_il_set_reg(mem_reg, ADD(x86_il_get_reg(mem_reg), UN(mem_size, size / BITS_PER_BYTE)));
|
|
RzILOpEffect *decrement = x86_il_set_reg(mem_reg, SUB(x86_il_get_reg(mem_reg), UN(mem_size, size / BITS_PER_BYTE)));
|
|
|
|
return SEQ6(reg, src, temp, arith_flags, res_flags, BRANCH(VARG(EFLAGS(DF)), decrement, increment));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SCASB
|
|
* Compare byte string
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(scasb) {
|
|
return x86_il_scas_helper(ins, pc, analysis, 8);
|
|
}
|
|
|
|
/**
|
|
* SCASW
|
|
* Compare word string
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(scasw) {
|
|
return x86_il_scas_helper(ins, pc, analysis, 16);
|
|
}
|
|
|
|
/**
|
|
* SCASD
|
|
* Compare dword string
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(scasd) {
|
|
return x86_il_scas_helper(ins, pc, analysis, 32);
|
|
}
|
|
|
|
/**
|
|
* SCASQ
|
|
* Compare quadword string (only for x86-64)
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(scasq) {
|
|
return x86_il_scas_helper(ins, pc, analysis, 64);
|
|
}
|
|
|
|
/**
|
|
* STAC
|
|
* Set AC flag
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(stac) {
|
|
return SETG(EFLAGS(AC), IL_TRUE);
|
|
}
|
|
|
|
/**
|
|
* STC
|
|
* Set carry flag (CF)
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(stc) {
|
|
return SETG(EFLAGS(CF), IL_TRUE);
|
|
}
|
|
|
|
/**
|
|
* STD
|
|
* Set direction flag (DF)
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(std) {
|
|
return SETG(EFLAGS(DF), IL_TRUE);
|
|
}
|
|
|
|
/**
|
|
* STI
|
|
* Set interrupt flag (IF)
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(sti) {
|
|
return SETG(EFLAGS(IF), IL_TRUE);
|
|
}
|
|
|
|
RzILOpEffect *x86_il_stos_helper(const X86ILIns *ins, ut64 pc, RzAnalysis *analysis, ut8 size) {
|
|
X86Reg store_reg;
|
|
|
|
switch (size) {
|
|
case 8:
|
|
store_reg = X86_REG_AL;
|
|
break;
|
|
case 16:
|
|
store_reg = X86_REG_AX;
|
|
break;
|
|
case 32:
|
|
store_reg = X86_REG_EAX;
|
|
break;
|
|
case 64:
|
|
store_reg = X86_REG_RAX;
|
|
break;
|
|
default:
|
|
rz_warn_if_reached();
|
|
return NULL;
|
|
}
|
|
|
|
if (analysis->bits == 64) {
|
|
X86Reg mem_reg = X86_REG_RDI;
|
|
ut8 mem_size = 64;
|
|
/* Address override prefix: 67H */
|
|
if (ins->structure->raw.prefixes[3].value) {
|
|
mem_reg = X86_REG_EDI;
|
|
mem_size = 32;
|
|
}
|
|
|
|
/* Cast to 64 if necessary (needed when address override prefix present) */
|
|
RzILOpEffect *store = STOREW((mem_size == 64 ? x86_il_get_reg(mem_reg) : UNSIGNED(64, x86_il_get_reg(mem_reg))), x86_il_get_reg(store_reg));
|
|
|
|
RzILOpEffect *increment = x86_il_set_reg(mem_reg, ADD(x86_il_get_reg(mem_reg), UN(mem_size, size / BITS_PER_BYTE)));
|
|
RzILOpEffect *decrement = x86_il_set_reg(mem_reg, SUB(x86_il_get_reg(mem_reg), UN(mem_size, size / BITS_PER_BYTE)));
|
|
|
|
return SEQ2(store, BRANCH(VARG(EFLAGS(DF)), decrement, increment));
|
|
} else {
|
|
X86Reg mem_reg = X86_REG_EDI;
|
|
ut8 mem_size = 32;
|
|
/* Check bitness and address override prefix: 67H */
|
|
if (analysis->bits == 16 || ins->structure->raw.prefixes[3].value) {
|
|
mem_reg = X86_REG_DI;
|
|
mem_size = 16;
|
|
}
|
|
|
|
X86Mem src_mem;
|
|
src_mem.base = mem_reg;
|
|
src_mem.disp.value = 0;
|
|
src_mem.index = X86_REG_NONE;
|
|
src_mem.scale = 1;
|
|
src_mem.segment = X86_REG_ES;
|
|
|
|
/* No need for casting memaddr here since the casting will be done while calculating the segmented address */
|
|
RzILOpEffect *store = x86_il_set_mem(src_mem, x86_il_get_reg(store_reg));
|
|
RzILOpEffect *increment = x86_il_set_reg(mem_reg, ADD(x86_il_get_reg(mem_reg), UN(mem_size, size / BITS_PER_BYTE)));
|
|
RzILOpEffect *decrement = x86_il_set_reg(mem_reg, SUB(x86_il_get_reg(mem_reg), UN(mem_size, size / BITS_PER_BYTE)));
|
|
|
|
return SEQ2(store, BRANCH(VARG(EFLAGS(DF)), decrement, increment));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* STOSB
|
|
* Store byte in a string
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(stosb) {
|
|
return x86_il_stos_helper(ins, pc, analysis, 8);
|
|
}
|
|
|
|
/**
|
|
* STOSW
|
|
* Store word in a string
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(stosw) {
|
|
|
|
return x86_il_stos_helper(ins, pc, analysis, 16);
|
|
}
|
|
|
|
/**
|
|
* STOSD
|
|
* Store dword in a string
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(stosd) {
|
|
return x86_il_stos_helper(ins, pc, analysis, 32);
|
|
}
|
|
|
|
/**
|
|
* STOSQ
|
|
* Store quadword in a string
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(stosq) {
|
|
return x86_il_stos_helper(ins, pc, analysis, 64);
|
|
}
|
|
|
|
/**
|
|
* SUB
|
|
* (SUB family of instructions)
|
|
* Possible encodings:
|
|
* - I
|
|
* - MI
|
|
* - MR
|
|
* - RM
|
|
*/
|
|
IL_LIFTER(sub) {
|
|
RzILOpEffect *op1 = SETL("op1", x86_il_get_op(0));
|
|
RzILOpEffect *op2 = SETL("op2", x86_il_get_op(1));
|
|
RzILOpEffect *sub = SETL("sub", SUB(VARL("op1"), VARL("op2")));
|
|
|
|
RzILOpEffect *set_dest = x86_il_set_op(0, VARL("sub"));
|
|
RzILOpEffect *set_res_flags = x86_il_set_result_flags(VARL("sub"));
|
|
RzILOpEffect *set_arith_flags = x86_il_set_arithmetic_flags(VARL("sub"), VARL("op1"), VARL("op2"), false);
|
|
|
|
return SEQ6(op1, op2, sub, set_dest, set_res_flags, set_arith_flags);
|
|
}
|
|
|
|
/**
|
|
* TEST
|
|
* Logical compare (AND)
|
|
* Encoding: I, MI, MR
|
|
*/
|
|
IL_LIFTER(test) {
|
|
RzILOpEffect *res_flags = x86_il_set_result_flags(LOGAND(x86_il_get_op(0), x86_il_get_op(1)));
|
|
RzILOpEffect *arith_flags = SEQ2(SETG(EFLAGS(CF), IL_FALSE), SETG(EFLAGS(OF), IL_FALSE));
|
|
|
|
return SEQ2(res_flags, arith_flags);
|
|
}
|
|
|
|
/**
|
|
* WAIT
|
|
* Wait until not busy
|
|
* ZO
|
|
*/
|
|
IL_LIFTER(fwait) {
|
|
/* NOP seems to be a reasonable implementation */
|
|
return NOP();
|
|
}
|
|
|
|
/**
|
|
* XCHG
|
|
* Exchange data
|
|
* Encoding: O, MR, RM
|
|
*/
|
|
IL_LIFTER(xchg) {
|
|
RzILOpEffect *temp = SETL("_temp", x86_il_get_op(0));
|
|
RzILOpEffect *xchg = x86_il_set_op(0, x86_il_get_op(1));
|
|
RzILOpEffect *set_src = x86_il_set_op(1, VARL("_temp"));
|
|
|
|
return SEQ3(temp, xchg, set_src);
|
|
}
|
|
|
|
/**
|
|
* XLATB
|
|
* Table look-up translation
|
|
* Encoding: ZO
|
|
*/
|
|
IL_LIFTER(xlat) {
|
|
X86Mem mem;
|
|
mem.disp.value = 0;
|
|
mem.index = X86_REG_NONE;
|
|
mem.scale = 1;
|
|
mem.segment = X86_REG_DS;
|
|
mem.base = X86_REG_EBX;
|
|
|
|
if (analysis->bits == 64) {
|
|
mem.segment = X86_REG_NONE;
|
|
mem.base = X86_REG_RBX;
|
|
} else if (analysis->bits == 16) {
|
|
mem.base = X86_REG_BX;
|
|
}
|
|
|
|
return x86_il_set_reg(X86_REG_AL, LOADW(8, ADD(x86_il_get_memaddr(mem), UNSIGNED(analysis->bits, x86_il_get_reg(X86_REG_AL)))));
|
|
}
|
|
|
|
/**
|
|
* XOR
|
|
* Logical exclusive OR
|
|
* Encodings: I, MI, MR, RM
|
|
*/
|
|
IL_LIFTER(xor) {
|
|
RzILOpPure *op1 = x86_il_get_op(0);
|
|
RzILOpPure *op2 = x86_il_get_op(1);
|
|
RzILOpEffect * xor = SETL("_xor", LOGXOR(op1, op2));
|
|
|
|
RzILOpEffect *set_dest = x86_il_set_op(0, VARL("_xor"));
|
|
RzILOpEffect *clear_of = SETG(EFLAGS(OF), IL_FALSE);
|
|
RzILOpEffect *clear_cf = SETG(EFLAGS(CF), IL_FALSE);
|
|
RzILOpEffect *set_res_flags = x86_il_set_result_flags(VARL("_xor"));
|
|
|
|
return SEQ5(xor, set_dest, clear_of, clear_cf, set_res_flags);
|
|
}
|
|
|
|
/**
|
|
* BOUND
|
|
* Check array index against bounds
|
|
* Encoding: RM
|
|
*/
|
|
|
|
IL_LIFTER(bound) {
|
|
RzILOpEffect *index = SETL("_index", x86_il_get_op(0));
|
|
|
|
X86Mem mem = ins->operands[1].mem;
|
|
RzILOpEffect *lower = SETL("_lower", LOADW(ins->operands[0].size * BITS_PER_BYTE, x86_il_get_memaddr(mem)));
|
|
if (mem.scale != 0) {
|
|
mem.disp.value += ins->operands[1].size / mem.scale;
|
|
} else {
|
|
mem.disp.value += ins->operands[1].size;
|
|
}
|
|
RzILOpEffect *upper = SETL("_upper", LOADW(ins->operands[0].size * BITS_PER_BYTE, x86_il_get_memaddr(mem)));
|
|
|
|
RzILOpBool *cond = OR(ULT(VARL("_index"), VARL("_lower")), UGT(VARL("_index"), VARL("_upper")));
|
|
|
|
/* Interrupt if out of bounds, NOP otherwise */
|
|
return SEQ4(index, lower, upper, BRANCH(cond, GOTO("int"), NOP()));
|
|
}
|
|
|
|
/**
|
|
* ENTER
|
|
* Make a stack frame for procedure parameters
|
|
* Encoding: II
|
|
*/
|
|
IL_LIFTER(enter) {
|
|
RzILOpEffect *alloc_size = SETL("_alloc_sz", UNSIGNED(16, x86_il_get_op(0)));
|
|
RzILOpEffect *nesting_level = SETL("_nest_lvl", MOD(UNSIGNED(8, x86_il_get_op(1)), U8(32)));
|
|
|
|
/* Will get resolved correctly to the largest SP reg */
|
|
X86Reg sp_reg = X86_REG_RSP;
|
|
|
|
/* Default value initialization (useless, but need to avoid warnings) */
|
|
X86Reg bp_reg = X86_REG_RBP;
|
|
unsigned short bp_size = analysis->bits / BITS_PER_BYTE;
|
|
|
|
switch (analysis->bits) {
|
|
case 64:
|
|
/* Operand-size override (66H) */
|
|
if (ins->structure->raw.prefixes[2].value) {
|
|
bp_reg = X86_REG_EBP;
|
|
bp_size = 4;
|
|
} else {
|
|
bp_reg = X86_REG_RBP;
|
|
bp_size = 8;
|
|
}
|
|
break;
|
|
case 32:
|
|
/* Operand-size override (66H) */
|
|
if (ins->structure->raw.prefixes[2].value) {
|
|
bp_reg = X86_REG_BP;
|
|
bp_size = 2;
|
|
} else {
|
|
bp_reg = X86_REG_EBP;
|
|
bp_size = 4;
|
|
}
|
|
break;
|
|
case 16:
|
|
bp_reg = X86_REG_BP;
|
|
bp_size = 2;
|
|
break;
|
|
default:
|
|
rz_warn_if_reached();
|
|
}
|
|
|
|
RzILOpEffect *push = x86_push_helper(x86_il_get_reg(bp_reg), bp_size);
|
|
RzILOpEffect *frame_temp = SETL("_frame_tmp", x86_il_get_reg(sp_reg));
|
|
|
|
RzILOpEffect *itr = SETL("_itr", U8(1));
|
|
|
|
/* RBP will be dynamically resolved to the correct BP register */
|
|
RzILOpEffect *loop_body = SEQ3(x86_il_set_reg(X86_REG_RBP, SUB(x86_il_get_reg(X86_REG_RBP), UN(analysis->bits, bp_size))), x86_push_helper(LOADW(bp_size * BITS_PER_BYTE, x86_il_get_reg(X86_REG_RBP)), bp_size), SETL("_itr", ADD(VARL("_itr"), U8(1))));
|
|
RzILOpEffect *loop = REPEAT(ULT(VARL("_itr"), VARL("_nest_lvl")), loop_body);
|
|
|
|
RzILOpEffect *nesting_lvl1 = x86_push_helper(VARL("_frame_tmp"), bp_size);
|
|
|
|
RzILOpEffect *continue_eff = x86_il_set_reg(sp_reg, SUB(x86_il_get_reg(sp_reg), UNSIGNED(analysis->bits, VARL("_alloc_sz"))));
|
|
if (bp_size == 2) {
|
|
continue_eff = SEQ2(continue_eff, x86_il_set_reg(bp_reg, UNSIGNED(16, UNSIGNED(15, VARL("_frame_tmp")))));
|
|
} else {
|
|
continue_eff = SEQ2(continue_eff, x86_il_set_reg(bp_reg, VARL("_frame_tmp")));
|
|
}
|
|
|
|
return SEQ6(alloc_size, nesting_level, push, frame_temp, BRANCH(IS_ZERO(VARL("_nest_lvl")), NOP(), SEQ2(BRANCH(UGT(VARL("_nest_lvl"), U8(1)), SEQ2(itr, loop), NOP()), nesting_lvl1)), continue_eff);
|
|
}
|
|
|
|
/**
|
|
* LEAVE
|
|
* High level procedure exit
|
|
* Encoding: ZO
|
|
*/
|
|
IL_LIFTER(leave) {
|
|
RzILOpEffect *set_sp = x86_il_set_reg(X86_REG_RSP, x86_il_get_reg(X86_REG_RBP));
|
|
|
|
/* Default value initialization (useless, but need to avoid warnings) */
|
|
X86Reg bp_reg = X86_REG_RBP;
|
|
unsigned short bp_size = analysis->bits / BITS_PER_BYTE;
|
|
|
|
switch (analysis->bits) {
|
|
case 64:
|
|
/* Operand-size override (66H) */
|
|
if (ins->structure->raw.prefixes[2].value) {
|
|
bp_reg = X86_REG_EBP;
|
|
bp_size = 4;
|
|
} else {
|
|
bp_reg = X86_REG_RBP;
|
|
bp_size = 8;
|
|
}
|
|
break;
|
|
case 32:
|
|
/* Operand-size override (66H) */
|
|
if (ins->structure->raw.prefixes[2].value) {
|
|
bp_reg = X86_REG_BP;
|
|
bp_size = 2;
|
|
} else {
|
|
bp_reg = X86_REG_EBP;
|
|
bp_size = 4;
|
|
}
|
|
break;
|
|
case 16:
|
|
bp_reg = X86_REG_BP;
|
|
bp_size = 2;
|
|
break;
|
|
default:
|
|
rz_warn_if_reached();
|
|
}
|
|
|
|
PopHelper pop = x86_pop_helper(bp_size /* BYTES */);
|
|
RzILOpEffect *set_bp = x86_il_set_reg(bp_reg, pop.val);
|
|
|
|
return SEQ3(set_sp, pop.eff, set_bp);
|
|
}
|
|
|
|
#include <rz_il/rz_il_opbuilder_end.h>
|