Initial commit

This commit is contained in:
Boris Batteux
2020-10-29 11:09:07 +01:00
commit 33f8d22f28
73 changed files with 14194 additions and 0 deletions

0
d810/__init__.py Normal file
View File

605
d810/ast.py Normal file
View File

@@ -0,0 +1,605 @@
from __future__ import annotations
import logging
from typing import List, Union, Dict, Tuple
from ida_hexrays import *
from d810.utils import unsigned_to_signed, signed_to_unsigned, \
get_add_cf, get_add_of, get_sub_of, get_parity_flag
from d810.hexrays_helpers import OPCODES_INFO, MBA_RELATED_OPCODES, Z3_SPECIAL_OPERANDS, MINSN_TO_AST_FORBIDDEN_OPCODES, \
equal_mops_ignore_size, AND_TABLE
from d810.hexrays_formatters import format_minsn_t, format_mop_t
from d810.errors import AstEvaluationException
logger = logging.getLogger('D810')
def check_and_add_to_list(new_ast: Union[AstNode, AstLeaf], known_ast_list: List[Union[AstNode, AstLeaf]]):
is_new_ast_known = False
for existing_elt in known_ast_list:
if equal_mops_ignore_size(new_ast.mop, existing_elt.mop):
new_ast.ast_index = existing_elt.ast_index
is_new_ast_known = True
break
if not is_new_ast_known:
ast_index = len(known_ast_list)
new_ast.ast_index = ast_index
known_ast_list.append(new_ast)
def mop_to_ast_internal(mop: mop_t, ast_list: List[Union[AstNode, AstLeaf]]) -> Union[None, AstNode, AstLeaf]:
if mop is None:
return None
if mop.t != mop_d or (mop.d.opcode not in MBA_RELATED_OPCODES):
tree = AstLeaf(format_mop_t(mop))
tree.mop = mop
dest_size = mop.size if mop.t != mop_d else mop.d.d.size
tree.dest_size = dest_size
else:
left_ast = mop_to_ast_internal(mop.d.l, ast_list)
right_ast = mop_to_ast_internal(mop.d.r, ast_list)
dst_ast = mop_to_ast_internal(mop.d.d, ast_list)
tree = AstNode(mop.d.opcode, left_ast, right_ast, dst_ast)
tree.mop = mop
tree.dest_size = mop.d.d.size
tree.ea = mop.d.ea
check_and_add_to_list(tree, ast_list)
return tree
def mop_to_ast(mop: mop_t) -> Union[None, AstNode, AstLeaf]:
mop_ast = mop_to_ast_internal(mop, [])
mop_ast.compute_sub_ast()
return mop_ast
def minsn_to_ast(instruction: minsn_t) -> Union[None, AstNode, AstLeaf]:
try:
if instruction.opcode in MINSN_TO_AST_FORBIDDEN_OPCODES:
# To avoid error 50278
return None
ins_mop = mop_t()
ins_mop.create_from_insn(instruction)
if instruction.opcode == m_mov:
tmp = AstNode(m_mov, mop_to_ast(ins_mop))
tmp.mop = ins_mop
tmp.dest_size = instruction.d.size
tmp.ea = instruction.ea
tmp.dst_mop = instruction.d
return tmp
tmp = mop_to_ast(ins_mop)
tmp.dst_mop = instruction.d
return tmp
except RuntimeError as e:
logger.error("Error while transforming instruction {0}: {1}".format(format_minsn_t(instruction), e))
return None
class AstInfo(object):
def __init__(self, ast: Union[AstNode, AstLeaf], number_of_use: int):
self.ast = ast
self.number_of_use = number_of_use
def __str__(self):
return "{0} used {1} times: {2}".format(self.ast, self.number_of_use, format_mop_t(self.ast.mop))
class AstNode(dict):
def __init__(self, opcode, left=None, right=None, dst=None):
super(dict, self).__init__()
self.opcode = opcode
self.left = left
self.right = right
self.dst = dst
self.dst_mop = None
self.opcodes = []
self.mop = None
self.is_candidate_ok = False
self.leafs = []
self.leafs_by_name = {}
self.ast_index = 0
self.sub_ast_info_by_index = {}
self.dest_size = None
self.ea = None
@property
def size(self):
return self.mop.d.d.size
def compute_sub_ast(self):
self.sub_ast_info_by_index = {}
self.sub_ast_info_by_index[self.ast_index] = AstInfo(self, 1)
if self.left is not None:
self.left.compute_sub_ast()
for ast_index, ast_info in self.left.sub_ast_info_by_index.items():
if ast_index not in self.sub_ast_info_by_index.keys():
self.sub_ast_info_by_index[ast_index] = AstInfo(ast_info.ast, 0)
self.sub_ast_info_by_index[ast_index].number_of_use += ast_info.number_of_use
if self.right is not None:
self.right.compute_sub_ast()
for ast_index, ast_info in self.right.sub_ast_info_by_index.items():
if ast_index not in self.sub_ast_info_by_index.keys():
self.sub_ast_info_by_index[ast_index] = AstInfo(ast_info.ast, 0)
self.sub_ast_info_by_index[ast_index].number_of_use += ast_info.number_of_use
def get_information(self):
leaf_info_list = []
cst_list = []
opcode_list = []
self.compute_sub_ast()
for _, ast_info in self.sub_ast_info_by_index.items():
if (ast_info.ast.mop is not None) and (ast_info.ast.mop.t != mop_z):
if ast_info.ast.is_leaf():
if ast_info.ast.is_constant():
cst_list.append(ast_info.ast.mop.nnn.value)
else:
leaf_info_list.append(ast_info)
else:
opcode_list += [ast_info.ast.opcode] * ast_info.number_of_use
return leaf_info_list, cst_list, opcode_list
def __getitem__(self, k) -> AstLeaf:
return self.leafs_by_name[k]
def get_leaf_list(self) -> List[AstLeaf]:
leafs = []
if self.left is not None:
leafs += self.left.get_leaf_list()
if self.right is not None:
leafs += self.right.get_leaf_list()
return leafs
def is_leaf(self) -> bool:
# An AstNode is not a leaf, so returns False
return False
def add_leaf(self, leaf_name: str, leaf_mop: mop_t):
leaf = AstLeaf(leaf_name)
leaf.mop = leaf_mop
self.leafs.append(leaf)
self.leafs_by_name[leaf_name] = leaf
def add_constant_leaf(self, leaf_name: str, cst_value: int, cst_size: int):
cst_mop = mop_t()
cst_mop.make_number(cst_value & AND_TABLE[cst_size], cst_size)
self.add_leaf(leaf_name, cst_mop)
def check_pattern_and_copy_mops(self, ast: Union[AstNode, AstLeaf]) -> bool:
self.reset_mops()
is_matching_shape = self._copy_mops_from_ast(ast)
if not is_matching_shape:
return False
return self._check_implicit_equalities()
def reset_mops(self):
self.mop = None
if self.left is not None:
self.left.reset_mops()
if self.right is not None:
self.right.reset_mops()
def _copy_mops_from_ast(self, other: Union[AstNode, AstLeaf]) -> bool:
self.mop = other.mop
self.dst_mop = other.dst_mop
self.dest_size = other.dest_size
self.ea = other.ea
if not isinstance(other, AstNode):
return False
if self.opcode != other.opcode:
return False
if self.left is not None:
if not self.left._copy_mops_from_ast(other.left):
return False
if self.right is not None:
if not self.right._copy_mops_from_ast(other.right):
return False
return True
def _check_implicit_equalities(self) -> bool:
self.leafs = self.get_leaf_list()
self.leafs_by_name = {}
self.is_candidate_ok = True
for leaf in self.leafs:
ref_leaf = self.leafs_by_name.get(leaf.name)
if ref_leaf is not None:
if not equal_mops_ignore_size(ref_leaf.mop, leaf.mop):
self.is_candidate_ok = False
self.leafs_by_name[leaf.name] = leaf
return self.is_candidate_ok
def update_leafs_mop(self, other: Union[AstNode, AstLeaf], other2: Union[None, AstNode, AstLeaf] = None) -> bool:
self.leafs = self.get_leaf_list()
all_leafs_found = True
for leaf in self.leafs:
if leaf.name in other.leafs_by_name.keys():
leaf.mop = other.leafs_by_name[leaf.name].mop
elif (other2 is not None) and (leaf.name in other2.leafs_by_name.keys()):
leaf.mop = other2.leafs_by_name[leaf.name].mop
else:
all_leafs_found = False
return all_leafs_found
def create_mop(self, ea: int) -> mop_t:
new_ins = self.create_minsn(ea)
new_ins_mop = mop_t()
new_ins_mop.create_from_insn(new_ins)
return new_ins_mop
def create_minsn(self, ea: int, dest=None) -> minsn_t:
new_ins = minsn_t(ea)
new_ins.opcode = self.opcode
if self.left is not None:
new_ins.l = self.left.create_mop(ea)
if self.right is not None:
new_ins.r = self.right.create_mop(ea)
new_ins.d = mop_t()
if self.left is not None:
new_ins.d.size = new_ins.l.size
if dest is not None:
new_ins.d = dest
return new_ins
def get_pattern(self) -> str:
nb_operands = OPCODES_INFO[self.opcode]["nb_operands"]
if nb_operands == 0:
return "AstNode({0})".format(OPCODES_INFO[self.opcode]["name"])
elif nb_operands == 1:
return "AstNode(m_{0}, {1})".format(OPCODES_INFO[self.opcode]["name"], self.left.get_pattern())
elif nb_operands == 2:
return "AstNode(m_{0}, {1}, {2})" \
.format(OPCODES_INFO[self.opcode]["name"], self.left.get_pattern(), self.right.get_pattern())
def evaluate_with_leaf_info(self, leafs_info, leafs_value):
dict_index_to_value = {leaf_info.ast.ast_index: leaf_value for leaf_info, leaf_value in
zip(leafs_info, leafs_value)}
res = self.evaluate(dict_index_to_value)
return res
def evaluate(self, dict_index_to_value):
if self.ast_index in dict_index_to_value:
return dict_index_to_value[self.ast_index]
res_mask = AND_TABLE[self.dest_size]
if self.opcode == m_mov:
return (self.left.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_neg:
return (- self.left.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_lnot:
return self.left.evaluate(dict_index_to_value) != 0
elif self.opcode == m_bnot:
return (self.left.evaluate(dict_index_to_value) ^ res_mask) & res_mask
elif self.opcode == m_xds:
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
return signed_to_unsigned(left_value_signed, self.dest_size) & res_mask
elif self.opcode == m_xdu:
return (self.left.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_low:
return (self.left.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_add:
return (self.left.evaluate(dict_index_to_value) + self.right.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_sub:
return (self.left.evaluate(dict_index_to_value) - self.right.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_mul:
return (self.left.evaluate(dict_index_to_value) * self.right.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_udiv:
return (self.left.evaluate(dict_index_to_value) // self.right.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_sdiv:
return (self.left.evaluate(dict_index_to_value) // self.right.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_umod:
return (self.left.evaluate(dict_index_to_value) % self.right.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_smod:
return (self.left.evaluate(dict_index_to_value) % self.right.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_or:
return (self.left.evaluate(dict_index_to_value) | self.right.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_and:
return (self.left.evaluate(dict_index_to_value) & self.right.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_xor:
return (self.left.evaluate(dict_index_to_value) ^ self.right.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_shl:
return (self.left.evaluate(dict_index_to_value) << self.right.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_shr:
return (self.left.evaluate(dict_index_to_value) >> self.right.evaluate(dict_index_to_value)) & res_mask
elif self.opcode == m_sar:
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
res_signed = left_value_signed >> self.right.evaluate(dict_index_to_value)
return signed_to_unsigned(res_signed, self.dest_size) & res_mask
elif self.opcode == m_cfadd:
tmp = get_add_cf(self.left.evaluate(dict_index_to_value), self.right.evaluate(dict_index_to_value),
self.left.dest_size)
return tmp & res_mask
elif self.opcode == m_ofadd:
tmp = get_add_of(self.left.evaluate(dict_index_to_value), self.right.evaluate(dict_index_to_value),
self.left.dest_size)
return tmp & res_mask
elif self.opcode == m_sets:
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
res = 1 if left_value_signed < 0 else 0
return res & res_mask
elif self.opcode == m_seto:
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
right_value_signed = unsigned_to_signed(self.right.evaluate(dict_index_to_value), self.right.dest_size)
sub_overflow = get_sub_of(left_value_signed, right_value_signed, self.left.dest_size)
return sub_overflow & res_mask
elif self.opcode == m_setnz:
res = 1 if self.left.evaluate(dict_index_to_value) != self.right.evaluate(dict_index_to_value) else 0
return res & res_mask
elif self.opcode == m_setz:
res = 1 if self.left.evaluate(dict_index_to_value) == self.right.evaluate(dict_index_to_value) else 0
return res & res_mask
elif self.opcode == m_setae:
res = 1 if self.left.evaluate(dict_index_to_value) >= self.right.evaluate(dict_index_to_value) else 0
return res & res_mask
elif self.opcode == m_setb:
res = 1 if self.left.evaluate(dict_index_to_value) < self.right.evaluate(dict_index_to_value) else 0
return res & res_mask
elif self.opcode == m_seta:
res = 1 if self.left.evaluate(dict_index_to_value) > self.right.evaluate(dict_index_to_value) else 0
return res & res_mask
elif self.opcode == m_setbe:
res = 1 if self.left.evaluate(dict_index_to_value) <= self.right.evaluate(dict_index_to_value) else 0
return res & res_mask
elif self.opcode == m_setg:
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
right_value_signed = unsigned_to_signed(self.right.evaluate(dict_index_to_value), self.right.dest_size)
res = 1 if left_value_signed > right_value_signed else 0
return res & res_mask
elif self.opcode == m_setge:
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
right_value_signed = unsigned_to_signed(self.right.evaluate(dict_index_to_value), self.right.dest_size)
res = 1 if left_value_signed >= right_value_signed else 0
return res & res_mask
elif self.opcode == m_setl:
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
right_value_signed = unsigned_to_signed(self.right.evaluate(dict_index_to_value), self.right.dest_size)
res = 1 if left_value_signed < right_value_signed else 0
return res & res_mask
elif self.opcode == m_setle:
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
right_value_signed = unsigned_to_signed(self.right.evaluate(dict_index_to_value), self.right.dest_size)
res = 1 if left_value_signed <= right_value_signed else 0
return res & res_mask
elif self.opcode == m_setp:
res = get_parity_flag(self.left.evaluate(dict_index_to_value), self.right.evaluate(dict_index_to_value),
self.left.dest_size)
return res & res_mask
else:
raise AstEvaluationException("Can't evaluate opcode: {0}".format(self.opcode))
def get_depth_signature(self, depth):
if depth == 1:
return ["{0}".format(self.opcode)]
tmp = []
nb_operands = OPCODES_INFO[self.opcode]["nb_operands"]
if (nb_operands >= 1) and self.left is not None:
tmp += self.left.get_depth_signature(depth - 1)
else:
tmp += ["N"] * (2 ** (depth - 2))
if (nb_operands >= 2) and self.right is not None:
tmp += self.right.get_depth_signature(depth - 1)
else:
tmp += ["N"] * (2 ** (depth - 2))
return tmp
def __str__(self):
try:
nb_operands = OPCODES_INFO[self.opcode]["nb_operands"]
if "symbol" in OPCODES_INFO[self.opcode].keys():
if nb_operands == 0:
return "{0}()".format(OPCODES_INFO[self.opcode]["symbol"])
elif nb_operands == 1:
return "{0}({1})".format(OPCODES_INFO[self.opcode]["symbol"], self.left)
elif nb_operands == 2:
if OPCODES_INFO[self.opcode]["symbol"] not in Z3_SPECIAL_OPERANDS:
return "({1} {0} {2})".format(OPCODES_INFO[self.opcode]["symbol"], self.left, self.right)
else:
return "{0}({1}, {2})".format(OPCODES_INFO[self.opcode]["symbol"], self.left, self.right)
else:
if nb_operands == 0:
return "{0}()".format(OPCODES_INFO[self.opcode]["name"])
elif nb_operands == 1:
return "{0}({1})".format(OPCODES_INFO[self.opcode]["name"], self.left)
elif nb_operands == 2:
return "{0}({1}, {2})".format(OPCODES_INFO[self.opcode]["name"], self.left, self.right)
return "Error_AstNode"
except RuntimeError as e:
logger.info("Error while calling __str__ on AstNode: {0}".format(e))
return "Error_AstNode"
class AstLeaf(object):
def __init__(self, name):
self.name = name
self.ast_index = None
self.mop = None
self.z3_var = None
self.z3_var_name = None
self.dest_size = None
self.ea = None
self.sub_ast_info_by_index = {}
def __getitem__(self, name):
if name == self.name:
return self
raise KeyError
@property
def size(self):
return self.mop.size
@property
def dst_mop(self):
return self.mop
@dst_mop.setter
def dst_mop(self, mop):
self.mop = mop
@property
def value(self):
if self.is_constant():
return self.mop.nnn.value
else:
return None
def compute_sub_ast(self):
self.sub_ast_info_by_index = {}
self.sub_ast_info_by_index[self.ast_index] = AstInfo(self, 1)
def get_information(self):
# Just here to allow calling get_information on either a AstNode or AstLeaf
return [], [], []
def get_leaf_list(self):
return [self]
def is_leaf(self):
return True
def is_constant(self):
if self.mop is None:
return False
return self.mop.t == mop_n
def create_mop(self, ea):
# Currently, we are not creating a new mop but returning the one defined
return self.mop
def update_leafs_mop(self, other, other2=None):
if self.name in other.leafs_by_name.keys():
self.mop = other.leafs_by_name[self.name].mop
return True
elif (other2 is not None) and (self.name in other2.leafs_by_name.keys()):
self.mop = other2.leafs_by_name[self.name].mop
return True
return False
def check_pattern_and_copy_mops(self, ast):
self.reset_mops()
is_matching_shape = self._copy_mops_from_ast(ast)
if not is_matching_shape:
return False
return self._check_implicit_equalities()
def reset_mops(self):
self.z3_var = None
self.z3_var_name = None
self.mop = None
def _copy_mops_from_ast(self, other):
self.mop = other.mop
return True
@staticmethod
def _check_implicit_equalities():
# An AstLeaf does not have any implicit equalities to be checked, so we always returns True
return True
def get_pattern(self):
if self.is_constant():
return "AstConstant('{0}', {0})".format(self.mop.nnn.value)
if self.ast_index is not None:
return "AstLeaf('x_{0}')".format(self.ast_index)
if self.name is not None:
return "AstLeaf('{0}')".format(self.name)
def evaluate_with_leaf_info(self, leafs_info, leafs_value):
dict_index_to_value = {leaf_info.ast.ast_index: leaf_value for leaf_info, leaf_value in
zip(leafs_info, leafs_value)}
res = self.evaluate(dict_index_to_value)
return res
def evaluate(self, dict_index_to_value):
if self.is_constant():
return self.mop.nnn.value
return dict_index_to_value.get(self.ast_index)
def get_depth_signature(self, depth):
if depth == 1:
if self.is_constant():
return ["C"]
return ["L"]
else:
return ["N"] * (2 ** (depth - 1))
def __str__(self):
try:
if self.is_constant():
return "{0}".format(self.mop.nnn.value)
if self.z3_var_name is not None:
return self.z3_var_name
if self.ast_index is not None:
return "x_{0}".format(self.ast_index)
if self.mop is not None:
return format_mop_t(self.mop)
return self.name
except RuntimeError as e:
logger.info("Error while calling __str__ on AstLeaf: {0}".format(e))
return "Error_AstLeaf"
class AstConstant(AstLeaf):
def __init__(self, name, expected_value=None, expected_size=None):
super().__init__(name)
self.expected_value = expected_value
self.expected_size = expected_size
@property
def value(self):
return self.mop.nnn.value
def is_constant(self):
# An AstConstant is always constant, so return True
return True
def _copy_mops_from_ast(self, other):
if other.mop is not None and other.mop.t != mop_n:
return False
self.mop = other.mop
if self.expected_value is None:
return True
return self.expected_value == other.mop.nnn.value
def evaluate(self, dict_index_to_value=None):
if self.mop is not None and self.mop.t == mop_n:
return self.mop.nnn.value
return self.expected_value
def get_depth_signature(self, depth):
if depth == 1:
return ["C"]
else:
return ["N"] * (2 ** (depth - 1))
def __str__(self):
try:
if self.mop is not None and self.mop.t == mop_n:
return "0x{0:x}".format(self.mop.nnn.value)
if self.expected_value is not None:
return "0x{0:x}".format(self.expected_value)
return self.name
except RuntimeError as e:
logger.info("Error while calling __str__ on AstConstant: {0}".format(e))
return "Error_AstConstant"

473
d810/cfg_utils.py Normal file
View File

@@ -0,0 +1,473 @@
import logging
from ida_hexrays import *
from typing import List, Union, Dict, Tuple
from d810.errors import ControlFlowException
from d810.hexrays_helpers import CONDITIONAL_JUMP_OPCODES
from d810.hexrays_formatters import block_printer
helper_logger = logging.getLogger('D810.helper')
def log_block_info(blk: mblock_t, logger_func=helper_logger.info):
if blk is None:
logger_func("Block is None")
return
vp = block_printer()
blk._print(vp)
logger_func("Block {0} with successors {1} and predecessors {2}:\n{3}"
.format(blk.serial, [x for x in blk.succset], [x for x in blk.predset], vp.get_block_mc()))
def insert_goto_instruction(blk: mblock_t, goto_blk_serial: int, nop_previous_instruction=False):
if blk.tail is not None:
goto_ins = minsn_t(blk.tail)
else:
goto_ins = minsn_t(blk.start)
if nop_previous_instruction:
blk.make_nop(blk.tail)
blk.insert_into_block(goto_ins, blk.tail)
# We nop instruction before setting it to goto to avoid error 52123
blk.make_nop(blk.tail)
goto_ins.opcode = m_goto
goto_ins.l = mop_t()
goto_ins.l.make_blkref(goto_blk_serial)
def change_1way_call_block_successor(call_blk: mblock_t, call_blk_successor_serial: int) -> bool:
if call_blk.nsucc() != 1:
return False
mba = call_blk.mba
previous_call_blk_successor_serial = call_blk.succset[0]
previous_call_blk_successor = mba.get_mblock(previous_call_blk_successor_serial)
nop_blk = insert_nop_blk(call_blk)
insert_goto_instruction(nop_blk, call_blk_successor_serial, nop_previous_instruction=True)
is_ok = change_1way_block_successor(nop_blk, call_blk_successor_serial)
if not is_ok:
return False
# Bookkeeping
call_blk.succset._del(previous_call_blk_successor_serial)
call_blk.succset.push_back(nop_blk.serial)
call_blk.mark_lists_dirty()
previous_call_blk_successor.predset._del(call_blk.serial)
if previous_call_blk_successor.serial != mba.qty - 1:
previous_call_blk_successor.mark_lists_dirty()
mba.mark_chains_dirty()
try:
mba.verify(True)
return True
except RuntimeError as e:
helper_logger.error("Error in change_1way_block_successor: {0}".format(e))
log_block_info(call_blk, helper_logger.error)
log_block_info(nop_blk, helper_logger.error)
raise e
def change_1way_block_successor(blk: mblock_t, blk_successor_serial: int) -> bool:
if blk.nsucc() != 1:
return False
mba: mbl_array_t = blk.mba
previous_blk_successor_serial = blk.succset[0]
previous_blk_successor = mba.get_mblock(previous_blk_successor_serial)
if blk.tail is None:
# We add a goto instruction
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=False)
elif blk.tail.opcode == m_goto:
# We change goto target directly
blk.tail.l.make_blkref(blk_successor_serial)
elif blk.tail.opcode == m_ijmp:
# We replace ijmp instruction with goto instruction
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=True)
elif blk.tail.opcode == m_call:
# Before maturity MMAT_CALLS, we can't add a goto after a call instruction
if mba.maturity < MMAT_CALLS:
return change_1way_call_block_successor(blk, blk_successor_serial)
else:
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=False)
else:
# We add a goto instruction
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=False)
# Update block properties
blk.type = BLT_1WAY
blk.flags |= MBL_GOTO
# Bookkeeping
blk.succset._del(previous_blk_successor_serial)
blk.succset.push_back(blk_successor_serial)
blk.mark_lists_dirty()
previous_blk_successor.predset._del(blk.serial)
if previous_blk_successor.serial != mba.qty - 1:
previous_blk_successor.mark_lists_dirty()
new_blk_successor = blk.mba.get_mblock(blk_successor_serial)
new_blk_successor.predset.push_back(blk.serial)
if new_blk_successor.serial != mba.qty - 1:
new_blk_successor.mark_lists_dirty()
mba.mark_chains_dirty()
try:
mba.verify(True)
return True
except RuntimeError as e:
helper_logger.error("Error in change_1way_block_successor: {0}".format(e))
log_block_info(blk, helper_logger.error)
log_block_info(new_blk_successor, helper_logger.error)
log_block_info(previous_blk_successor, helper_logger.error)
raise e
def change_0way_block_successor(blk: mblock_t, blk_successor_serial: int) -> bool:
if blk.nsucc() != 0:
return False
mba = blk.mba
if blk.tail.opcode == m_ijmp:
# We replace ijmp instruction with goto instruction
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=True)
else:
# We add a goto instruction
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=False)
# Update block properties
blk.type = BLT_1WAY
blk.flags |= MBL_GOTO
# Bookkeeping
blk.succset.push_back(blk_successor_serial)
blk.mark_lists_dirty()
new_blk_successor = blk.mba.get_mblock(blk_successor_serial)
new_blk_successor.predset.push_back(blk.serial)
if new_blk_successor.serial != mba.qty - 1:
new_blk_successor.mark_lists_dirty()
mba.mark_chains_dirty()
try:
mba.verify(True)
return True
except RuntimeError as e:
helper_logger.error("Error in change_0way_block_successor: {0}".format(e))
log_block_info(blk, helper_logger.error)
log_block_info(new_blk_successor, helper_logger.error)
raise e
def change_2way_block_conditional_successor(blk: mblock_t, blk_successor_serial: int) -> bool:
if blk.nsucc() != 2:
return False
mba = blk.mba
previous_blk_conditional_successor_serial = blk.tail.d.b
previous_blk_conditional_successor = mba.get_mblock(previous_blk_conditional_successor_serial)
blk.tail.d = mop_t()
blk.tail.d.make_blkref(blk_successor_serial)
# Bookkeeping
blk.succset._del(previous_blk_conditional_successor_serial)
blk.succset.push_back(blk_successor_serial)
blk.mark_lists_dirty()
previous_blk_conditional_successor.predset._del(blk.serial)
if previous_blk_conditional_successor.serial != mba.qty - 1:
previous_blk_conditional_successor.mark_lists_dirty()
new_blk_conditional_successor = blk.mba.get_mblock(blk_successor_serial)
new_blk_conditional_successor.predset.push_back(blk.serial)
if new_blk_conditional_successor.serial != mba.qty - 1:
new_blk_conditional_successor.mark_lists_dirty()
# Step4: Final stuff and checks
mba.mark_chains_dirty()
try:
mba.verify(True)
except RuntimeError as e:
helper_logger.error("Error in change_2way_block_conditional_successor: {0}".format(e))
log_block_info(blk, helper_logger.error)
log_block_info(new_blk_conditional_successor, helper_logger.error)
raise e
def update_blk_successor(blk: mblock_t, old_successor_serial: int, new_successor_serial: int) -> int:
if blk.nsucc() == 1:
change_1way_block_successor(blk, new_successor_serial)
elif blk.nsucc() == 2:
if old_successor_serial == blk.serial + 1:
helper_logger.info("Can't update direct block successor: {0} - {1} - {2}"
.format(blk.serial, old_successor_serial, new_successor_serial))
return 0
else:
change_2way_block_conditional_successor(blk, new_successor_serial)
else:
helper_logger.info("Can't update block successor: {0} ".format(blk.serial))
return 0
return 1
def make_2way_block_goto(blk: mblock_t, blk_successor_serial: int) -> bool:
if blk.nsucc() != 2:
return False
mba = blk.mba
previous_blk_successor_serials = [x for x in blk.succset]
previous_blk_successors = [mba.get_mblock(x) for x in previous_blk_successor_serials]
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=True)
# Update block properties
blk.type = BLT_1WAY
blk.flags |= MBL_GOTO
# Bookkeeping
for prev_serial in previous_blk_successor_serials:
blk.succset._del(prev_serial)
blk.succset.push_back(blk_successor_serial)
blk.mark_lists_dirty()
for prev_blk in previous_blk_successors:
prev_blk.predset._del(blk.serial)
if prev_blk.serial != mba.qty - 1:
prev_blk.mark_lists_dirty()
new_blk_successor = blk.mba.get_mblock(blk_successor_serial)
new_blk_successor.predset.push_back(blk.serial)
if new_blk_successor.serial != mba.qty - 1:
new_blk_successor.mark_lists_dirty()
mba.mark_chains_dirty()
try:
mba.verify(True)
return True
except RuntimeError as e:
helper_logger.error("Error in make_2way_block_goto: {0}".format(e))
log_block_info(blk, helper_logger.error)
log_block_info(new_blk_successor, helper_logger.error)
raise e
def create_block(blk: mblock_t, blk_ins: List[minsn_t], is_0_way: bool = False) -> mblock_t:
mba = blk.mba
new_blk = insert_nop_blk(blk)
for ins in blk_ins:
tmp_ins = minsn_t(ins)
new_blk.insert_into_block(tmp_ins, new_blk.tail)
if is_0_way:
new_blk.type = BLT_0WAY
# Bookkeeping
prev_successor_serial = new_blk.succset[0]
new_blk.succset._del(prev_successor_serial)
prev_succ = mba.get_mblock(prev_successor_serial)
prev_succ.predset._del(new_blk.serial)
if prev_succ.serial != mba.qty - 1:
prev_succ.mark_lists_dirty()
new_blk.mark_lists_dirty()
mba.mark_chains_dirty()
try:
mba.verify(True)
return new_blk
except RuntimeError as e:
helper_logger.error("Error in create_block: {0}".format(e))
log_block_info(new_blk, helper_logger.error)
raise e
def update_block_successors(blk: mblock_t, blk_succ_serial_list: List[int]):
mba = blk.mba
if len(blk_succ_serial_list) == 0:
blk.type = BLT_0WAY
elif len(blk_succ_serial_list) == 1:
blk.type = BLT_1WAY
elif len(blk_succ_serial_list) == 2:
blk.type = BLT_2WAY
else:
raise
# Remove old successors
prev_successor_serials = [x for x in blk.succset]
for prev_successor_serial in prev_successor_serials:
blk.succset._del(prev_successor_serial)
prev_succ = mba.get_mblock(prev_successor_serial)
prev_succ.predset._del(blk.serial)
if prev_succ.serial != mba.qty - 1:
prev_succ.mark_lists_dirty()
# Add new successors
for blk_succ_serial in blk_succ_serial_list:
blk.succset.push_back(blk_succ_serial)
new_blk_successor = mba.get_mblock(blk_succ_serial)
new_blk_successor.predset.push_back(blk.serial)
if new_blk_successor.serial != mba.qty - 1:
new_blk_successor.mark_lists_dirty()
blk.mark_lists_dirty()
def insert_nop_blk(blk: mblock_t) -> mblock_t:
mba = blk.mba
nop_block = mba.copy_block(blk, blk.serial + 1)
cur_ins = nop_block.head
while cur_ins is not None:
nop_block.make_nop(cur_ins)
cur_ins = cur_ins.next
nop_block.type = BLT_1WAY
# We might have clone a block with multiple or no successor, thus we need to clean all
prev_successor_serials = [x for x in nop_block.succset]
# Bookkeeping
for prev_successor_serial in prev_successor_serials:
nop_block.succset._del(prev_successor_serial)
prev_succ = mba.get_mblock(prev_successor_serial)
prev_succ.predset._del(nop_block.serial)
if prev_succ.serial != mba.qty - 1:
prev_succ.mark_lists_dirty()
nop_block.succset.push_back(nop_block.serial + 1)
nop_block.mark_lists_dirty()
new_blk_successor = mba.get_mblock(nop_block.serial + 1)
new_blk_successor.predset.push_back(nop_block.serial)
if new_blk_successor.serial != mba.qty - 1:
new_blk_successor.mark_lists_dirty()
mba.mark_chains_dirty()
try:
mba.verify(True)
return nop_block
except RuntimeError as e:
helper_logger.error("Error in insert_nop_blk: {0}".format(e))
log_block_info(nop_block, helper_logger.error)
raise e
def ensure_last_block_is_goto(mba: mbl_array_t) -> int:
last_blk = mba.get_mblock(mba.qty - 2)
if last_blk.nsucc() == 1:
change_1way_block_successor(last_blk, last_blk.succset[0])
return 1
elif last_blk.nsucc() == 0:
return 0
else:
raise ControlFlowException("Last block {0} is not one way (not supported yet)".format(last_blk.serial))
def duplicate_block(block_to_duplicate: mblock_t) -> Tuple[mblock_t, mblock_t]:
mba = block_to_duplicate.mba
duplicated_blk = mba.copy_block(block_to_duplicate, mba.qty - 1)
helper_logger.debug(" Duplicated {0} -> {1}".format(block_to_duplicate.serial, duplicated_blk.serial))
duplicated_blk_default = None
if (block_to_duplicate.tail is not None) and is_mcode_jcond(block_to_duplicate.tail.opcode):
block_to_duplicate_default_successor = mba.get_mblock(block_to_duplicate.serial + 1)
duplicated_blk_default = insert_nop_blk(duplicated_blk)
change_1way_block_successor(duplicated_blk_default, block_to_duplicate.serial + 1)
helper_logger.debug(" {0} is conditional, so created a default child {1} for {2} which goto {3}"
.format(block_to_duplicate.serial, duplicated_blk_default.serial, duplicated_blk.serial,
block_to_duplicate_default_successor.serial))
elif duplicated_blk.nsucc() == 1:
helper_logger.debug(" Making {0} goto {1}".format(duplicated_blk.serial, block_to_duplicate.succset[0]))
change_1way_block_successor(duplicated_blk, block_to_duplicate.succset[0])
elif duplicated_blk.nsucc() == 0:
helper_logger.debug(" Duplicated block {0} has no successor => Nothing to do".format(duplicated_blk.serial))
return duplicated_blk, duplicated_blk_default
def change_block_address(block: mblock_t, new_ea: int):
# Can be used to fix error 50357
mb_curr = block.head
while mb_curr:
mb_curr.ea = new_ea
mb_curr = mb_curr.next
def is_conditional_jump(blk: mblock_t) -> bool:
if (blk is not None) and (blk.tail is not None):
return blk.tail.opcode in CONDITIONAL_JUMP_OPCODES
return False
def is_indirect_jump(blk: mblock_t) -> bool:
if (blk is not None) and (blk.tail is not None):
return blk.tail.opcode == m_ijmp
return False
def get_block_serials_by_address(mba: mbl_array_t, address: int) -> List[int]:
blk_serial_list = []
for i in range(mba.qty):
blk = mba.get_mblock(i)
if blk.start == address:
blk_serial_list.append(i)
return blk_serial_list
def get_block_serials_by_address_range(mba: mbl_array_t, address: int) -> List[int]:
blk_serial_list = []
for i in range(mba.qty):
blk = mba.get_mblock(i)
if blk.start <= address <= blk.end:
blk_serial_list.append(i)
return blk_serial_list
def mba_remove_simple_goto_blocks(mba: mbl_array_t) -> int:
last_block_index = mba.qty - 1
nb_change = 0
for goto_blk_serial in range(last_block_index):
goto_blk: mblock_t = mba.get_mblock(goto_blk_serial)
if goto_blk.is_simple_goto_block():
goto_blk_dst_serial = goto_blk.tail.l.b
goto_blk_preset = [x for x in goto_blk.predset]
for father_serial in goto_blk_preset:
father_blk: mblock_t = mba.get_mblock(father_serial)
nb_change += update_blk_successor(father_blk, goto_blk_serial, goto_blk_dst_serial)
return nb_change
def mba_deep_cleaning(mba: mbl_array_t) -> int:
if mba.maturity < MMAT_CALLS:
# Doing this optimization before MMAT_CALLS may create blocks with call instruction (not last instruction)
# IDA does like that and will raise a 50864 error
return 0
mba.remove_empty_blocks()
mba.combine_blocks()
nb_change = mba_remove_simple_goto_blocks(mba)
if nb_change > 0:
mba.remove_empty_blocks()
mba.combine_blocks()
return nb_change
def ensure_child_has_an_unconditional_father(father_block: mblock_t, child_block: mblock_t) -> int:
if father_block is None:
return 0
mba = father_block.mba
if father_block.nsucc() == 1:
return 0
if father_block.tail.d.b == child_block.serial:
helper_logger.debug("Father {0} is a conditional jump to child {1}, creating a new father"
.format(father_block.serial, child_block.serial))
new_father_block = insert_nop_blk(mba.get_mblock(mba.qty - 2))
change_1way_block_successor(new_father_block, child_block.serial)
change_2way_block_conditional_successor(father_block, new_father_block.serial)
else:
helper_logger.info("Father {0} is a conditional jump to child {1} (default child), creating a new father"
.format(father_block.serial, child_block.serial))
new_father_block = insert_nop_blk(father_block)
change_1way_block_successor(new_father_block, child_block.serial)
return 1

73
d810/conf/__init__.py Normal file
View File

@@ -0,0 +1,73 @@
import os
import json
class D810Configuration(object):
def __init__(self):
self.config_dir = os.path.join(os.getenv("HOME"), ".idapro", "plugins", "d810", "conf")
self.config_file = os.path.join(self.config_dir, "options.json")
with open(self.config_file, "r") as fp:
self._options = json.load(fp)
def get(self, name):
if (name == "log_dir") and (self._options[name] is None):
return os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", ".."))
return self._options[name]
def set(self, name, value):
self._options[name] = value
def save(self):
with open(self.config_file, "w") as fp:
json.dump(self._options, fp, indent=2)
class RuleConfiguration(object):
def __init__(self, name=None, is_activated=False, config=None):
self.name = name
self.is_activated = is_activated
self.config = config if config is not None else {}
def to_dict(self):
return {
"name": self.name,
"is_activated": self.is_activated,
"config": self.config
}
@staticmethod
def from_dict(kwargs):
return RuleConfiguration(**kwargs)
class ProjectConfiguration(object):
def __init__(self, path=None, description=None, ins_rules=None, blk_rules=None, conf_dir=None):
self.path = path
self.description = description
self.conf_dir = conf_dir
self.ins_rules = [] if ins_rules is None else ins_rules
self.blk_rules = [] if blk_rules is None else blk_rules
self.additional_configuration = {}
def load(self):
try:
with open(self.path, "r") as fp:
project_conf = json.load(fp)
except FileNotFoundError as e:
if self.conf_dir is not None:
self.path = os.path.join(self.conf_dir, self.path)
with open(self.path, "r") as fp:
project_conf = json.load(fp)
self.description = project_conf["description"]
self.ins_rules = [RuleConfiguration.from_dict(x) for x in project_conf["ins_rules"]]
self.blk_rules = [RuleConfiguration.from_dict(x) for x in project_conf["blk_rules"]]
def save(self):
project_conf = {
"description": self.description,
"ins_rules": [x.to_dict() for x in self.ins_rules],
"blk_rules": [x.to_dict() for x in self.blk_rules],
}
with open(self.path, "w") as fp:
json.dump(project_conf, fp, indent=2)

View File

@@ -0,0 +1,921 @@
{
"description": "Default configuration of D-810 with only instruction level optimization",
"ins_rules": [
{
"name": "AddXor_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "AddXor_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_5",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Add_SpecialConstantRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Add_SpecialConstantRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_SpecialConstantRule_3",
"is_activated": true,
"config": {}
},
{
"name": "And1_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "AndGetUpperBits_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndOr_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndXor_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "And_OllvmRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_OllvmRule_2",
"is_activated": true,
"config": {}
},
{
"name": "And_OllvmRule_3",
"is_activated": true,
"config": {}
},
{
"name": "BnotAdd_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "BnotOr_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_Rule_3",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_XorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule1",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule10",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule11",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule12",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule13",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule14",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule15",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule16",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule17",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule18",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule19",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule2",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule20",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule21",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule22",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule3",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule4",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule5",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule6",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule7",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule8",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule9",
"is_activated": true,
"config": {}
},
{
"name": "GetIdentRule1",
"is_activated": true,
"config": {}
},
{
"name": "GetIdentRule2",
"is_activated": true,
"config": {}
},
{
"name": "GetIdentRule3",
"is_activated": true,
"config": {}
},
{
"name": "Mul_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Mul_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_4",
"is_activated": true,
"config": {}
},
{
"name": "NegAdd_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "NegAdd_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "NegOr_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "NegXor_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "NegXor_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Neg_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Neg_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Or_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Or_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_HackersDelightRule_2_variant_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_MbaRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_MbaRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Or_OllvmRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_3",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_4",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule1",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule2",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule3",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule4",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule5",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule2",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule3",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule4",
"is_activated": true,
"config": {}
},
{
"name": "PredOdd1",
"is_activated": true,
"config": {}
},
{
"name": "PredOdd2",
"is_activated": true,
"config": {}
},
{
"name": "PredOr1_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "PredOr2_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetbRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule2",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule3",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule4",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule5",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule6",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule8",
"is_activated": true,
"config": {}
},
{
"name": "PredSetzRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetzRule2",
"is_activated": true,
"config": {}
},
{
"name": "PredSetzRule3",
"is_activated": true,
"config": {}
},
{
"name": "Sub1Add_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1And1_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1And_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1Or_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule1",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule2",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule3",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule4",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule5",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule6",
"is_activated": true,
"config": {}
},
{
"name": "Xor1_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "XorAlmost_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_5",
"is_activated": true,
"config": {}
},
{
"name": "Xor_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_MbaRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_MbaRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_NestedStuff",
"is_activated": true,
"config": {}
},
{
"name": "Xor_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_Rule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_SpecialConstantRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_SpecialConstantRule_2",
"is_activated": true,
"config": {}
},
{
"name": "AndChain",
"is_activated": true,
"config": {}
},
{
"name": "ArithmeticChain",
"is_activated": true,
"config": {}
},
{
"name": "OrChain",
"is_activated": true,
"config": {}
},
{
"name": "XorChain",
"is_activated": true,
"config": {}
},
{
"name": "Z3ConstantOptimization",
"is_activated": true,
"config": {
"min_nb_opcode": 4,
"min_nb_constant": 3
}
},
{
"name": "Z3SmodRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "Z3lnotRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "Z3setnzRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "Z3setzRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "ExampleGuessingRule",
"is_activated": true,
"config": {
"min_nb_var": 1,
"max_nb_var": 3,
"min_nb_diff_opcodes": 3,
"max_nb_diff_opcodes": 6
}
}
],
"blk_rules": [
{
"name": "JumpFixer",
"is_activated": true,
"config": {
"enabled_rules": [
"CompareConstantRule1",
"CompareConstantRule2",
"CompareConstantRule3",
"JaeRule1",
"JbRule1",
"JnzRule1",
"JnzRule2",
"JnzRule3",
"JnzRule4",
"JnzRule5",
"JnzRule6",
"JnzRule7",
"JnzRule8"
]
}
}
]
}

View File

@@ -0,0 +1,926 @@
{
"description": "Unflattening O-LLVM with control flow flattening",
"ins_rules": [
{
"name": "AddXor_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "AddXor_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_5",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Add_SpecialConstantRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Add_SpecialConstantRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_SpecialConstantRule_3",
"is_activated": true,
"config": {}
},
{
"name": "And1_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "AndGetUpperBits_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndOr_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndXor_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "And_OllvmRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_OllvmRule_2",
"is_activated": true,
"config": {}
},
{
"name": "And_OllvmRule_3",
"is_activated": true,
"config": {}
},
{
"name": "BnotAdd_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "BnotOr_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_Rule_3",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_XorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule1",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule10",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule11",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule12",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule13",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule14",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule15",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule16",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule17",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule18",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule19",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule2",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule20",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule21",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule22",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule3",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule4",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule5",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule6",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule7",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule8",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule9",
"is_activated": true,
"config": {}
},
{
"name": "GetIdentRule1",
"is_activated": true,
"config": {}
},
{
"name": "GetIdentRule2",
"is_activated": true,
"config": {}
},
{
"name": "GetIdentRule3",
"is_activated": true,
"config": {}
},
{
"name": "Mul_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Mul_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_4",
"is_activated": true,
"config": {}
},
{
"name": "NegAdd_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "NegAdd_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "NegOr_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "NegXor_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "NegXor_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Neg_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Neg_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Or_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Or_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_HackersDelightRule_2_variant_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_MbaRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_MbaRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Or_OllvmRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_3",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_4",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule1",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule2",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule3",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule4",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule5",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule2",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule3",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule4",
"is_activated": true,
"config": {}
},
{
"name": "PredOdd1",
"is_activated": true,
"config": {}
},
{
"name": "PredOdd2",
"is_activated": true,
"config": {}
},
{
"name": "PredOr1_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "PredOr2_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetbRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule2",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule3",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule4",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule5",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule6",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule8",
"is_activated": true,
"config": {}
},
{
"name": "PredSetzRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetzRule2",
"is_activated": true,
"config": {}
},
{
"name": "PredSetzRule3",
"is_activated": true,
"config": {}
},
{
"name": "Sub1Add_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1And1_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1And_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1Or_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule1",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule2",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule3",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule4",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule5",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule6",
"is_activated": true,
"config": {}
},
{
"name": "Xor1_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "XorAlmost_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_5",
"is_activated": true,
"config": {}
},
{
"name": "Xor_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_MbaRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_MbaRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_NestedStuff",
"is_activated": true,
"config": {}
},
{
"name": "Xor_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_Rule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_SpecialConstantRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_SpecialConstantRule_2",
"is_activated": true,
"config": {}
},
{
"name": "AndChain",
"is_activated": true,
"config": {}
},
{
"name": "ArithmeticChain",
"is_activated": true,
"config": {}
},
{
"name": "OrChain",
"is_activated": true,
"config": {}
},
{
"name": "XorChain",
"is_activated": true,
"config": {}
},
{
"name": "Z3ConstantOptimization",
"is_activated": true,
"config": {
"min_nb_opcode": 4,
"min_nb_constant": 3
}
},
{
"name": "Z3SmodRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "Z3lnotRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "Z3setnzRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "Z3setzRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "ExampleGuessingRule",
"is_activated": true,
"config": {
"min_nb_var": 1,
"max_nb_var": 3,
"min_nb_diff_opcodes": 3,
"max_nb_diff_opcodes": 6
}
}
],
"blk_rules": [
{
"name": "Unflattener",
"is_activated": true,
"config": {}
},
{
"name": "JumpFixer",
"is_activated": true,
"config": {
"enabled_rules": [
"CompareConstantRule1",
"CompareConstantRule2",
"CompareConstantRule3",
"JaeRule1",
"JbRule1",
"JnzRule1",
"JnzRule2",
"JnzRule3",
"JnzRule4",
"JnzRule5",
"JnzRule6",
"JnzRule7",
"JnzRule8"
]
}
}
]
}

View File

@@ -0,0 +1,926 @@
{
"description": "Unflattening Tigress with switch case dispatcher",
"ins_rules": [
{
"name": "AddXor_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "AddXor_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_5",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Add_SpecialConstantRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Add_SpecialConstantRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_SpecialConstantRule_3",
"is_activated": true,
"config": {}
},
{
"name": "And1_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "AndGetUpperBits_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndOr_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndXor_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "And_OllvmRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_OllvmRule_2",
"is_activated": true,
"config": {}
},
{
"name": "And_OllvmRule_3",
"is_activated": true,
"config": {}
},
{
"name": "BnotAdd_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "BnotOr_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_Rule_3",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_XorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule1",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule10",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule11",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule12",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule13",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule14",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule15",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule16",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule17",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule18",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule19",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule2",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule20",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule21",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule22",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule3",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule4",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule5",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule6",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule7",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule8",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule9",
"is_activated": true,
"config": {}
},
{
"name": "GetIdentRule1",
"is_activated": true,
"config": {}
},
{
"name": "GetIdentRule2",
"is_activated": true,
"config": {}
},
{
"name": "GetIdentRule3",
"is_activated": true,
"config": {}
},
{
"name": "Mul_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Mul_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_4",
"is_activated": true,
"config": {}
},
{
"name": "NegAdd_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "NegAdd_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "NegOr_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "NegXor_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "NegXor_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Neg_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Neg_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Or_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Or_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_HackersDelightRule_2_variant_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_MbaRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_MbaRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Or_OllvmRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_3",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_4",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule1",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule2",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule3",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule4",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule5",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule2",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule3",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule4",
"is_activated": true,
"config": {}
},
{
"name": "PredOdd1",
"is_activated": true,
"config": {}
},
{
"name": "PredOdd2",
"is_activated": true,
"config": {}
},
{
"name": "PredOr1_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "PredOr2_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetbRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule2",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule3",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule4",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule5",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule6",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule8",
"is_activated": true,
"config": {}
},
{
"name": "PredSetzRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetzRule2",
"is_activated": true,
"config": {}
},
{
"name": "PredSetzRule3",
"is_activated": true,
"config": {}
},
{
"name": "Sub1Add_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1And1_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1And_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1Or_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule1",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule2",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule3",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule4",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule5",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule6",
"is_activated": true,
"config": {}
},
{
"name": "Xor1_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "XorAlmost_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_5",
"is_activated": true,
"config": {}
},
{
"name": "Xor_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_MbaRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_MbaRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_NestedStuff",
"is_activated": true,
"config": {}
},
{
"name": "Xor_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_Rule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_SpecialConstantRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_SpecialConstantRule_2",
"is_activated": true,
"config": {}
},
{
"name": "AndChain",
"is_activated": true,
"config": {}
},
{
"name": "ArithmeticChain",
"is_activated": true,
"config": {}
},
{
"name": "OrChain",
"is_activated": true,
"config": {}
},
{
"name": "XorChain",
"is_activated": true,
"config": {}
},
{
"name": "Z3ConstantOptimization",
"is_activated": true,
"config": {
"min_nb_opcode": 4,
"min_nb_constant": 3
}
},
{
"name": "Z3SmodRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "Z3lnotRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "Z3setnzRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "Z3setzRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "ExampleGuessingRule",
"is_activated": true,
"config": {
"min_nb_var": 1,
"max_nb_var": 3,
"min_nb_diff_opcodes": 3,
"max_nb_diff_opcodes": 6
}
}
],
"blk_rules": [
{
"name": "UnflattenerSwitchCase",
"is_activated": true,
"config": {}
},
{
"name": "JumpFixer",
"is_activated": true,
"config": {
"enabled_rules": [
"CompareConstantRule1",
"CompareConstantRule2",
"CompareConstantRule3",
"JaeRule1",
"JbRule1",
"JnzRule1",
"JnzRule2",
"JnzRule3",
"JnzRule4",
"JnzRule5",
"JnzRule6",
"JnzRule7",
"JnzRule8"
]
}
}
]
}

942
d810/conf/example_anel.json Normal file
View File

@@ -0,0 +1,942 @@
{
"description": "Configuration to deobfuscate ANEL malware",
"ins_rules": [
{
"name": "AddXor_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "AddXor_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Add_HackersDelightRule_5",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Add_OllvmRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Add_SpecialConstantRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Add_SpecialConstantRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Add_SpecialConstantRule_3",
"is_activated": true,
"config": {}
},
{
"name": "And1_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndBnot_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "AndGetUpperBits_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndOr_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "AndXor_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "And_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "And_OllvmRule_1",
"is_activated": true,
"config": {}
},
{
"name": "And_OllvmRule_2",
"is_activated": true,
"config": {}
},
{
"name": "And_OllvmRule_3",
"is_activated": true,
"config": {}
},
{
"name": "BnotAdd_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "BnotAnd_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "BnotOr_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "BnotXor_Rule_3",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Bnot_XorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule1",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule10",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule11",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule12",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule13",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule14",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule15",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule16",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule17",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule18",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule19",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule2",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule20",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule21",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule22",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule3",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule4",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule5",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule6",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule7",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule8",
"is_activated": true,
"config": {}
},
{
"name": "CstSimplificationRule9",
"is_activated": true,
"config": {}
},
{
"name": "GetIdentRule1",
"is_activated": true,
"config": {}
},
{
"name": "GetIdentRule2",
"is_activated": true,
"config": {}
},
{
"name": "GetIdentRule3",
"is_activated": true,
"config": {}
},
{
"name": "Mul_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Mul_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Mul_MbaRule_4",
"is_activated": true,
"config": {}
},
{
"name": "NegAdd_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "NegAdd_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "NegOr_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "NegXor_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "NegXor_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Neg_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Neg_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "OrBnot_FactorRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Or_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Or_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_HackersDelightRule_2_variant_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_MbaRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_MbaRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Or_OllvmRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_3",
"is_activated": true,
"config": {}
},
{
"name": "Or_Rule_4",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule1",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule2",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule3",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule4",
"is_activated": true,
"config": {}
},
{
"name": "Pred0Rule5",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule2",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule3",
"is_activated": true,
"config": {}
},
{
"name": "PredFFRule4",
"is_activated": true,
"config": {}
},
{
"name": "PredOdd1",
"is_activated": true,
"config": {}
},
{
"name": "PredOdd2",
"is_activated": true,
"config": {}
},
{
"name": "PredOr1_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "PredOr2_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetbRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule2",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule3",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule4",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule5",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule6",
"is_activated": true,
"config": {}
},
{
"name": "PredSetnzRule8",
"is_activated": true,
"config": {}
},
{
"name": "PredSetzRule1",
"is_activated": true,
"config": {}
},
{
"name": "PredSetzRule2",
"is_activated": true,
"config": {}
},
{
"name": "PredSetzRule3",
"is_activated": true,
"config": {}
},
{
"name": "Sub1Add_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1And1_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1And_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1Or_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub1_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Sub_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule1",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule2",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule3",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule4",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule5",
"is_activated": true,
"config": {}
},
{
"name": "WeirdRule6",
"is_activated": true,
"config": {}
},
{
"name": "Xor1_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "XorAlmost_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_FactorRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_FactorRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_FactorRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_4",
"is_activated": true,
"config": {}
},
{
"name": "Xor_HackersDelightRule_5",
"is_activated": true,
"config": {}
},
{
"name": "Xor_MbaRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_MbaRule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_MbaRule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_NestedStuff",
"is_activated": true,
"config": {}
},
{
"name": "Xor_Rule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_Rule_2",
"is_activated": true,
"config": {}
},
{
"name": "Xor_Rule_3",
"is_activated": true,
"config": {}
},
{
"name": "Xor_SpecialConstantRule_1",
"is_activated": true,
"config": {}
},
{
"name": "Xor_SpecialConstantRule_2",
"is_activated": true,
"config": {}
},
{
"name": "AndChain",
"is_activated": true,
"config": {}
},
{
"name": "ArithmeticChain",
"is_activated": true,
"config": {}
},
{
"name": "OrChain",
"is_activated": true,
"config": {}
},
{
"name": "XorChain",
"is_activated": true,
"config": {}
},
{
"name": "Z3ConstantOptimization",
"is_activated": true,
"config": {
"min_nb_opcode": 4,
"min_nb_constant": 3
}
},
{
"name": "Z3SmodRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "Z3lnotRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "Z3setnzRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "Z3setzRuleGeneric",
"is_activated": true,
"config": {}
},
{
"name": "SetGlobalVariablesToZeroIfDetectedReadOnly",
"is_activated": true,
"config": {}
},
{
"name": "ExampleGuessingRule",
"is_activated": true,
"config": {
"min_nb_var": 1,
"max_nb_var": 3,
"min_nb_diff_opcodes": 3,
"max_nb_diff_opcodes": 6
}
}
],
"blk_rules": [
{
"name": "Unflattener",
"is_activated": true,
"config": {
"maturities": [
"MMAT_CALLS",
"MMAT_GLBOPT1",
"MMAT_GLBOPT2"
]
}
},
{
"name": "UnflattenerFakeJump",
"is_activated": true,
"config": {}
},
{
"name": "JumpFixer",
"is_activated": true,
"config": {
"enabled_rules": [
"CompareConstantRule1",
"CompareConstantRule2",
"CompareConstantRule3",
"JaeRule1",
"JbRule1",
"JnzRule1",
"JnzRule2",
"JnzRule3",
"JnzRule4",
"JnzRule5",
"JnzRule6",
"JnzRule7",
"JnzRule8"
]
}
}
]
}

14
d810/conf/options.json Normal file
View File

@@ -0,0 +1,14 @@
{
"erase_logs_on_reload": true,
"generate_z3_code": true,
"dump_intermediate_microcode": true,
"log_dir": null,
"configurations": [
"default_instruction_only.json",
"default_unflattening_ollvm.json",
"default_unflattening_switch_case.json",
"example_anel.json",
"example_unflattening_indirect.json"
],
"last_project_index": 0
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

505
d810/emulator.py Normal file
View File

@@ -0,0 +1,505 @@
from __future__ import annotations
import logging
from typing import List, Union, Dict
from idaapi import getseg, get_qword, SEGPERM_WRITE
from ida_hexrays import *
from d810.utils import unsigned_to_signed, signed_to_unsigned, get_add_cf, get_add_of, get_sub_of, ror, get_parity_flag
from d810.hexrays_helpers import equal_mops_ignore_size, get_mop_index, AND_TABLE, CONTROL_FLOW_OPCODES, \
CONDITIONAL_JUMP_OPCODES
from d810.hexrays_formatters import format_minsn_t, format_mop_t, mop_type_to_string, opcode_to_string
from d810.cfg_utils import get_block_serials_by_address
from d810.errors import EmulationException, EmulationIndirectJumpException, UnresolvedMopException, \
WritableMemoryReadException
emulator_log = logging.getLogger('D810.emulator')
class MicroCodeInterpreter(object):
def __init__(self, global_environment=None):
self.global_environment = MicroCodeEnvironment() if global_environment is None else global_environment
def _eval_instruction_and_update_environment(self, blk: mblock_t, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
environment.set_cur_flow(blk, ins)
res = self._eval_instruction(ins, environment)
if res is not None:
if (ins.d is not None) and ins.d.t != mop_z:
environment.assign(ins.d, res, auto_define=True)
return res
def _eval_instruction(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
if ins is None:
return None
is_flow_instruction = self._eval_control_flow_instruction(ins, environment)
if is_flow_instruction:
return None
call_helper_res = self._eval_call_helper(ins, environment)
if call_helper_res is not None:
return call_helper_res
if ins.opcode == m_call:
return self._eval_call(ins, environment)
elif ins.opcode == m_icall:
return self._eval_call(ins, environment)
res_mask = AND_TABLE[ins.d.size]
if ins.opcode == m_ldx:
return self._eval_load(ins, environment)
elif ins.opcode == m_stx:
return self._eval_store(ins, environment)
elif ins.opcode == m_mov:
return (self.eval(ins.l, environment)) & res_mask
elif ins.opcode == m_neg:
return (- self.eval(ins.l, environment)) & res_mask
elif ins.opcode == m_lnot:
return self.eval(ins.l, environment) != 0
elif ins.opcode == m_bnot:
return (self.eval(ins.l, environment) ^ res_mask) & res_mask
elif ins.opcode == m_xds:
left_value_signed = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
return signed_to_unsigned(left_value_signed, ins.d.size) & res_mask
elif ins.opcode == m_xdu:
return (self.eval(ins.l, environment)) & res_mask
elif ins.opcode == m_low:
return (self.eval(ins.l, environment)) & res_mask
elif ins.opcode == m_add:
return (self.eval(ins.l, environment) + self.eval(ins.r, environment)) & res_mask
elif ins.opcode == m_sub:
return (self.eval(ins.l, environment) - self.eval(ins.r, environment)) & res_mask
elif ins.opcode == m_mul:
return (self.eval(ins.l, environment) * self.eval(ins.r, environment)) & res_mask
elif ins.opcode == m_udiv:
return (self.eval(ins.l, environment) // self.eval(ins.r, environment)) & res_mask
elif ins.opcode == m_sdiv:
return (self.eval(ins.l, environment) // self.eval(ins.r, environment)) & res_mask
elif ins.opcode == m_umod:
return (self.eval(ins.l, environment) % self.eval(ins.r, environment)) & res_mask
elif ins.opcode == m_smod:
return (self.eval(ins.l, environment) % self.eval(ins.r, environment)) & res_mask
elif ins.opcode == m_or:
return (self.eval(ins.l, environment) | self.eval(ins.r, environment)) & res_mask
elif ins.opcode == m_and:
return (self.eval(ins.l, environment) & self.eval(ins.r, environment)) & res_mask
elif ins.opcode == m_xor:
return (self.eval(ins.l, environment) ^ self.eval(ins.r, environment)) & res_mask
elif ins.opcode == m_shl:
return (self.eval(ins.l, environment) << self.eval(ins.r, environment)) & res_mask
elif ins.opcode == m_shr:
return (self.eval(ins.l, environment) >> self.eval(ins.r, environment)) & res_mask
elif ins.opcode == m_sar:
res_signed = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size) >> self.eval(ins.r, environment)
return signed_to_unsigned(res_signed, ins.d.size) & res_mask
elif ins.opcode == m_cfadd:
tmp = get_add_cf(self.eval(ins.l, environment), self.eval(ins.r, environment), ins.l.size)
return tmp & res_mask
elif ins.opcode == m_ofadd:
tmp = get_add_of(self.eval(ins.l, environment), self.eval(ins.r, environment), ins.l.size)
return tmp & res_mask
elif ins.opcode == m_sets:
left_value_signed = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
res = 1 if left_value_signed < 0 else 0
return res & res_mask
elif ins.opcode == m_seto:
left_value_signed = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
right_value_signed = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
sub_overflow = get_sub_of(left_value_signed, right_value_signed, ins.l.size)
return sub_overflow & res_mask
elif ins.opcode == m_setnz:
res = 1 if self.eval(ins.l, environment) != self.eval(ins.r, environment) else 0
return res & res_mask
elif ins.opcode == m_setz:
res = 1 if self.eval(ins.l, environment) == self.eval(ins.r, environment) else 0
return res & res_mask
elif ins.opcode == m_setae:
res = 1 if self.eval(ins.l, environment) >= self.eval(ins.r, environment) else 0
return res & res_mask
elif ins.opcode == m_setb:
res = 1 if self.eval(ins.l, environment) < self.eval(ins.r, environment) else 0
return res & res_mask
elif ins.opcode == m_seta:
res = 1 if self.eval(ins.l, environment) > self.eval(ins.r, environment) else 0
return res & res_mask
elif ins.opcode == m_setbe:
res = 1 if self.eval(ins.l, environment) <= self.eval(ins.r, environment) else 0
return res & res_mask
elif ins.opcode == m_setg:
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
res = 1 if left_value > right_value else 0
return res & res_mask
elif ins.opcode == m_setge:
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
res = 1 if left_value >= right_value else 0
return res & res_mask
elif ins.opcode == m_setl:
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
res = 1 if left_value < right_value else 0
return res & res_mask
elif ins.opcode == m_setle:
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
res = 1 if left_value <= right_value else 0
return res & res_mask
elif ins.opcode == m_setp:
res = get_parity_flag(self.eval(ins.l, environment), self.eval(ins.r, environment), ins.l.size)
return res & res_mask
raise EmulationException("Unsupported instruction opcode '{0}': '{1}'"
.format(opcode_to_string(ins.opcode), format_minsn_t(ins)))
@staticmethod
def _get_blk_serial(mop: mop_t) -> int:
if mop.t == mop_b:
return mop.b
raise EmulationException("Get block serial with an unsupported mop type '{0}': '{1}'"
.format(mop_type_to_string(mop.t), format_mop_t(mop)))
def _eval_conditional_jump(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
if ins.opcode not in CONDITIONAL_JUMP_OPCODES:
return None
if ins.opcode == m_jtbl:
# This is not handled the same way
return None
cur_blk = environment.cur_blk
direct_child_serial = cur_blk.serial + 1
if ins.opcode == m_jcnd:
jump_taken = self.eval(ins.l, environment) != 0
elif ins.opcode == m_jnz:
jump_taken = self.eval(ins.l, environment) != self.eval(ins.r, environment)
elif ins.opcode == m_jz:
jump_taken = self.eval(ins.l, environment) == self.eval(ins.r, environment)
elif ins.opcode == m_jae:
jump_taken = self.eval(ins.l, environment) >= self.eval(ins.r, environment)
elif ins.opcode == m_jb:
jump_taken = self.eval(ins.l, environment) < self.eval(ins.r, environment)
elif ins.opcode == m_ja:
jump_taken = self.eval(ins.l, environment) > self.eval(ins.r, environment)
elif ins.opcode == m_jbe:
jump_taken = self.eval(ins.l, environment) <= self.eval(ins.r, environment)
elif ins.opcode == m_jg:
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
jump_taken = left_value > right_value
elif ins.opcode == m_jge:
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
jump_taken = left_value >= right_value
elif ins.opcode == m_jl:
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
jump_taken = left_value < right_value
elif ins.opcode == m_jle:
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
jump_taken = left_value <= right_value
else:
# This should never happen
raise EmulationException("Unhandled conditional jump: '{0}'".format(format_minsn_t(ins)))
return self._get_blk_serial(ins.d) if jump_taken else direct_child_serial
def _eval_control_flow_instruction(self, ins: minsn_t, environment: MicroCodeEnvironment) -> bool:
if ins.opcode not in CONTROL_FLOW_OPCODES:
return False
cur_blk = environment.cur_blk
if cur_blk is None:
raise EmulationException("Can't evaluate control flow instruction with null block: '{0}'"
.format(format_minsn_t(ins)))
next_blk_serial = self._eval_conditional_jump(ins, environment)
if next_blk_serial is not None:
next_blk = cur_blk.mba.get_mblock(next_blk_serial)
next_ins = next_blk.head
environment.set_next_flow(next_blk, next_ins)
return True
if ins.opcode == m_goto:
next_blk_serial = self._get_blk_serial(ins.l)
elif ins.opcode == m_jtbl:
left_value = self.eval(ins.l, environment)
cases = ins.r.c
# Initialize to default case
next_blk_serial = [x for x in cases.targets][-1]
for possible_values, target_block_serial in zip(cases.values, cases.targets):
for test_value in possible_values:
if left_value == test_value:
next_blk_serial = target_block_serial
break
elif ins.opcode == m_ijmp:
ijmp_dest_ea = self.eval(ins.d, environment)
dest_block_serials = get_block_serials_by_address(environment.cur_blk.mba, ijmp_dest_ea)
if len(dest_block_serials) == 0:
raise EmulationIndirectJumpException("No blocks found at address {0:x}".format(ijmp_dest_ea),
ijmp_dest_ea, dest_block_serials)
if len(dest_block_serials) > 1:
raise EmulationIndirectJumpException("Multiple blocks at address {0:x}: {1}".format(ijmp_dest_ea,
dest_block_serials),
ijmp_dest_ea, dest_block_serials)
next_blk_serial = dest_block_serials[0]
if next_blk_serial is None:
return False
next_blk = cur_blk.mba.get_mblock(next_blk_serial)
next_ins = next_blk.head
environment.set_next_flow(next_blk, next_ins)
return True
def _eval_call_helper(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
# Currently, we only support helper calls, (but end goal is to allow to hook calls)
if ins.opcode != m_call or ins.l.t != mop_h:
return None
res_mask = AND_TABLE[ins.d.size]
helper_name = ins.l.helper
args_list = ins.d
emulator_log.debug("Call helper for {0}".format(helper_name))
# and we support only __ROR4__ (we should add other Hex-Rays created helper calls)
if helper_name == "__ROR4__":
data_1 = self.eval(args_list.f.args[0], environment)
data_2 = self.eval(args_list.f.args[1], environment)
return ror(data_1, data_2, 8 * args_list.f.args[0].size) & res_mask
elif helper_name == "__readfsqword":
return 0
return None
def _eval_load(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
res_mask = AND_TABLE[ins.d.size]
if ins.opcode == m_ldx:
load_address = self.eval(ins.r, environment)
formatted_seg_register = format_mop_t(ins.l)
if formatted_seg_register == "ss.2":
stack_mop = mop_t()
stack_mop.erase()
stack_mop._make_stkvar(environment.cur_blk.mba, load_address)
emulator_log.debug("Searching for stack mop {0}".format(format_mop_t(stack_mop)))
stack_mop_value = environment.lookup(stack_mop)
emulator_log.debug(" stack mop {0} value : {1}".format(format_mop_t(stack_mop), stack_mop_value))
return stack_mop_value & res_mask
else:
mem_seg = getseg(load_address)
seg_perm = mem_seg.perm
if (seg_perm & SEGPERM_WRITE) != 0:
raise WritableMemoryReadException("ldx {0:x} (writable -> return None)".format(load_address))
else:
memory_value = get_qword(load_address)
emulator_log.debug("ldx {0:x} (non writable -> return {1:x})"
.format(load_address, memory_value & res_mask))
return memory_value & res_mask
def _eval_store(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
# TODO: implement
emulator_log.warning("Evaluation of {0} not implemented: bypassing".format(format_minsn_t(ins)))
return None
def _eval_call(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
# TODO: implement
emulator_log.warning("Evaluation of {0} not implemented: bypassing".format(format_minsn_t(ins)))
return None
def eval(self, mop: mop_t, environment: MicroCodeEnvironment) -> Union[None, int]:
if mop.t == mop_n:
return mop.nnn.value
elif mop.t in [mop_r, mop_S]:
return environment.lookup(mop)
elif mop.t == mop_d:
return self._eval_instruction(mop.d, environment)
elif mop.t == mop_a:
if mop.a.t == mop_v:
emulator_log.debug("Reading a mop_a '{0}' -> {1:x}".format(format_mop_t(mop), mop.a.g))
return mop.a.g
elif mop.a.t == mop_S:
emulator_log.debug("Reading a mop_a '{0}' -> {1:x}".format(format_mop_t(mop), mop.a.s.off))
return mop.a.s.off
raise UnresolvedMopException("Calling get_cst with unsupported mop type {0} - {1}: '{2}'"
.format(mop.t, mop.a.t, format_mop_t(mop)))
elif mop.t == mop_v:
mem_seg = getseg(mop.g)
seg_perm = mem_seg.perm
if (seg_perm & SEGPERM_WRITE) != 0:
emulator_log.debug("Reading a (writable) mop_v {0}".format(format_mop_t(mop)))
return environment.lookup(mop)
else:
memory_value = get_qword(mop.g)
emulator_log.debug("Reading a mop_v {0:x} (non writable -> return {1:x})".format(mop.g, memory_value))
return mop.g
raise EmulationException("Unsupported mop type '{0}': '{1}'"
.format(mop_type_to_string(mop.t), format_mop_t(mop)))
def eval_instruction(self, blk: mblock_t, ins: minsn_t, environment: Union[None, MicroCodeEnvironment] = None,
raise_exception: bool = False) -> bool:
try:
if environment is None:
environment = self.global_environment
emulator_log.info("Evaluating microcode instruction : '{0}'".format(format_minsn_t(ins)))
if ins is None:
return False
self._eval_instruction_and_update_environment(blk, ins, environment)
return True
except EmulationException as e:
emulator_log.warning("Can't evaluate instruction: '{0}': {1}".format(format_minsn_t(ins), e))
if raise_exception:
raise e
except Exception as e:
emulator_log.warning("Error during evaluation of: '{0}': {1}".format(format_minsn_t(ins), e))
if raise_exception:
raise e
return False
def eval_mop(self, mop: mop_t, environment: Union[None, MicroCodeEnvironment] = None,
raise_exception: bool = False) -> Union[None, int]:
try:
if environment is None:
environment = self.global_environment
res = self.eval(mop, environment)
return res
except EmulationException as e:
emulator_log.warning("Can't get constant mop value: '{0}': {1}".format(format_mop_t(mop), e))
if raise_exception:
raise e
else:
return None
class MopMapping(object):
def __init__(self):
self.mops = []
self.mops_values = []
def __setitem__(self, mop: mop_t, mop_value: int):
mop_index = get_mop_index(mop, self.mops)
mop_value &= AND_TABLE[mop.size]
if mop_index != -1:
self.mops_values[mop_index] = mop_value
return
self.mops.append(mop)
self.mops_values.append(mop_value)
def __getitem__(self, mop: mop_t) -> int:
mop_index = get_mop_index(mop, self.mops)
if mop_index == -1:
raise KeyError
return self.mops_values[mop_index]
def __len__(self):
return len(self.mops)
def __delitem__(self, mop: mop_t):
mop_index = get_mop_index(mop, self.mops)
if mop_index == -1:
raise KeyError
del self.mops[mop_index]
del self.mops_values[mop_index]
def clear(self):
self.mops = []
self.mops_values = []
def copy(self):
new_mapping = MopMapping()
for mop, mop_value in self.items():
new_mapping[mop] = mop_value
return new_mapping
def has_key(self, mop: mop_t):
mop_index = get_mop_index(mop, self.mops)
return mop_index != -1
def keys(self) -> List[mop_t]:
return self.mops
def values(self) -> List[int]:
return self.mops_values
def items(self):
return [(x, y) for x, y in zip(self.mops, self.mops_values)]
def __contains__(self, mop: mop_t):
return self.has_key(mop)
class MicroCodeEnvironment(object):
def __init__(self, parent: Union[None, MicroCodeEnvironment] = None):
self.parent = parent
self.mop_r_record = MopMapping()
self.mop_S_record = MopMapping()
self.cur_blk = None
self.cur_ins = None
self.next_blk = None
self.next_ins = None
def items(self):
return [x for x in self.mop_r_record.items() + self.mop_S_record.items()]
def get_copy(self, copy_parent=True) -> MicroCodeEnvironment:
parent_copy = self.parent
if parent_copy is not None and copy_parent:
parent_copy = self.parent.get_copy(copy_parent=True)
new_env = MicroCodeEnvironment(parent_copy)
for mop, mop_value in self.mop_r_record.items():
new_env.define(mop, mop_value)
for mop, mop_value in self.mop_S_record.items():
new_env.define(mop, mop_value)
new_env.cur_blk = self.cur_blk
new_env.cur_ins = self.cur_ins
new_env.next_blk = self.next_blk
new_env.next_ins = self.next_ins
return new_env
def set_cur_flow(self, cur_blk: mblock_t, cur_ins: minsn_t):
self.cur_blk = cur_blk
self.cur_ins = cur_ins
self.next_blk = cur_blk
if self.cur_ins is None:
self.next_blk = self.cur_blk.mba.get_mblock(self.cur_blk.serial + 1)
self.next_ins = self.next_blk.head
else:
self.next_ins = self.cur_ins.next
if self.next_ins is None:
self.next_blk = self.cur_blk.mba.get_mblock(self.cur_blk.serial + 1)
self.next_ins = self.next_blk.head
emulator_log.debug(
"Setting next block {0} and next ins {1}".format(self.next_blk.serial, format_minsn_t(self.next_ins)))
def set_next_flow(self, next_blk: mblock_t, next_ins: minsn_t):
self.next_blk = next_blk
self.next_ins = next_ins
def define(self, mop: mblock_t, value: int) -> int:
if mop.t == mop_r:
self.mop_r_record[mop] = value
return value
elif mop.t == mop_S:
self.mop_S_record[mop] = value
return value
raise EmulationException("Defining an unsupported mop type '{0}': '{1}'"
.format(mop_type_to_string(mop.t), format_mop_t(mop)))
def _lookup_mop(self, searched_mop: mop_t, mop_value_dict: Dict[mop_t, int], new_mop_value: Union[None, int] = None,
auto_define=True, raise_exception=True) -> int:
for known_mop, mop_value in mop_value_dict.items():
if equal_mops_ignore_size(searched_mop, known_mop):
if new_mop_value is not None:
mop_value_dict[searched_mop] = new_mop_value
return new_mop_value
return mop_value
if (new_mop_value is not None) and auto_define:
self.define(searched_mop, new_mop_value)
return new_mop_value
if raise_exception:
raise EmulationException("Variable '{0}' is not defined".format(format_mop_t(searched_mop)))
else:
return None
def lookup(self, mop: mop_t, raise_exception=True) -> int:
if mop.t == mop_r:
return self._lookup_mop(mop, self.mop_r_record, raise_exception=raise_exception)
elif mop.t == mop_S:
return self._lookup_mop(mop, self.mop_S_record, raise_exception=raise_exception)
def assign(self, mop: mop_t, value: int, auto_define=True) -> int:
if mop.t == mop_r:
return self._lookup_mop(mop, self.mop_r_record, value, auto_define)
elif mop.t == mop_S:
return self._lookup_mop(mop, self.mop_S_record, value, auto_define)
raise EmulationException("Assigning an unsupported mop type '{0}': '{1}'"
.format(mop_type_to_string(mop.t), format_mop_t(mop)))

41
d810/errors.py Normal file
View File

@@ -0,0 +1,41 @@
class D810Exception(Exception):
pass
class AstException(D810Exception):
pass
class AstEvaluationException(AstException):
pass
class D810Z3Exception(D810Exception):
pass
class ControlFlowException(D810Exception):
pass
class EmulationException(D810Exception):
pass
class EmulationIndirectJumpException(EmulationException):
def __init__(self, message, dest_ea, dest_serial_list):
super().__init__(message)
self.dest_ea = dest_ea
self.dest_serial_list = dest_serial_list
class UnresolvedMopException(EmulationException):
pass
class WritableMemoryReadException(EmulationException):
pass
class UnsupportedInstructionException(EmulationException):
pass

View File

@@ -0,0 +1,95 @@
import os
import logging
from typing import List
from d810.hexrays_helpers import OPCODES_INFO, MATURITY_TO_STRING_DICT, STRING_TO_MATURITY_DICT, MOP_TYPE_TO_STRING_DICT
from ida_hexrays import minsn_t, mop_t, vd_printer_t, mbl_array_t
logger = logging.getLogger('D810.helper')
def format_minsn_t(ins: minsn_t) -> str:
if ins is None:
return "minsn_t is None"
tmp = ins._print()
pp_ins = "".join([c if 0x20 <= ord(c) <= 0x7e else "" for c in tmp])
return pp_ins
def format_mop_t(mop_in: mop_t) -> str:
if mop_in is None:
return "mop_t is None"
if mop_in.t > 15:
# To avoid error 50581
return "Unknown mop type {0}".format(mop_in.t)
return mop_in.dstr()
def format_mop_list(mop_list: List[mop_t]) -> str:
return ", ".join([format_mop_t(x) for x in mop_list])
def maturity_to_string(maturity_level: int) -> str:
return MATURITY_TO_STRING_DICT.get(maturity_level, "Unknown maturity: {0}".format(maturity_level))
def string_to_maturity(maturity_string: str) -> int:
return STRING_TO_MATURITY_DICT.get(maturity_string)
def mop_type_to_string(mop_type: int) -> str:
return MOP_TYPE_TO_STRING_DICT.get(mop_type, "Unknown mop type: {0}".format(mop_type))
def opcode_to_string(opcode) -> str:
try:
return OPCODES_INFO[opcode]["name"]
except KeyError:
return "Unknown opcode: {0}".format(opcode)
class mba_printer(vd_printer_t):
def __init__(self):
vd_printer_t.__init__(self)
self.mc = []
def get_mc(self):
return self.mc
def _print(self, indent, line):
self.mc.append("".join([c if 0x20 <= ord(c) <= 0x7e else "" for c in line])+"\n")
return 1
class block_printer(vd_printer_t):
def __init__(self):
vd_printer_t.__init__(self)
self.block_ins = []
def get_block_mc(self):
return "\n".join(self.block_ins)
def _print(self, indent, line):
self.block_ins.append("".join([c if 0x20 <= ord(c) <= 0x7e else "" for c in line]))
return 1
def write_mc_to_file(mba: mbl_array_t, filename: str, mba_flags: int = 0) -> bool:
if not mba:
return False
vp = mba_printer()
mba.set_mba_flags(mba_flags)
mba._print(vp)
with open(filename, "w") as f:
f.writelines(vp.get_mc())
return True
def dump_microcode_for_debug(mba: mbl_array_t, log_dir_path: str, name: str = ""):
mc_filename = os.path.join(log_dir_path, "{0:x}_maturity_{1}_{2}.log".format(mba.entry_ea, mba.maturity, name))
logger.info("Dumping microcode in file {0}...".format(mc_filename))
write_mc_to_file(mba, mc_filename)

356
d810/hexrays_helpers.py Normal file
View File

@@ -0,0 +1,356 @@
from ida_hexrays import *
from typing import List, Tuple
from ida_hexrays import mop_d, mop_n, m_stx, m_ldx, m_xdu, m_xds, mop_z, mop_fn, mop_S, mop_v, EQ_IGNSIZE, mop_b, \
mop_r, mop_f, mop_l, mop_a, mop_h, mop_str, mop_c, mop_p, mop_sc
OPCODES_INFO = {
m_nop: {"name": "nop", "nb_operands": 0, "is_commutative": True},
m_stx: {"name": "stx", "nb_operands": 2, "is_commutative": False},
m_ldx: {"name": "ldx", "nb_operands": 2, "is_commutative": False},
m_ldc: {"name": "ldc", "nb_operands": 1, "is_commutative": False},
m_mov: {"name": "mov", "nb_operands": 1, "is_commutative": False, "symbol": ""},
m_neg: {"name": "neg", "nb_operands": 1, "is_commutative": False, "symbol": "-"},
m_lnot: {"name": "lnot", "nb_operands": 1, "is_commutative": False, "symbol": "!"},
m_bnot: {"name": "bnot", "nb_operands": 1, "is_commutative": False, "symbol": "~"},
m_xds: {"name": "xds", "nb_operands": 1, "is_commutative": False, "symbol": "xds"},
m_xdu: {"name": "xdu", "nb_operands": 1, "is_commutative": False, "symbol": "xdu"},
m_low: {"name": "low", "nb_operands": 1, "is_commutative": False, "symbol": "low"},
m_high: {"name": "high", "nb_operands": 1, "is_commutative": False, "symbol": "high"},
m_add: {"name": "add", "nb_operands": 2, "is_commutative": True, "symbol": "+"},
m_sub: {"name": "sub", "nb_operands": 2, "is_commutative": False, "symbol": "-"},
m_mul: {"name": "mul", "nb_operands": 2, "is_commutative": True, "symbol": "*"},
m_udiv: {"name": "udiv", "nb_operands": 2, "is_commutative": False, "symbol": "UDiv"},
m_sdiv: {"name": "sdiv", "nb_operands": 2, "is_commutative": False, "symbol": "/"},
m_umod: {"name": "umod", "nb_operands": 2, "is_commutative": False, "symbol": "URem"},
m_smod: {"name": "smod", "nb_operands": 2, "is_commutative": False, "symbol": "%"},
m_or: {"name": "or", "nb_operands": 2, "is_commutative": True, "symbol": "|"},
m_and: {"name": "and", "nb_operands": 2, "is_commutative": True, "symbol": "&"},
m_xor: {"name": "xor", "nb_operands": 2, "is_commutative": True, "symbol": "^"},
m_shl: {"name": "shl", "nb_operands": 2, "is_commutative": False, "symbol": "<<"},
m_shr: {"name": "shr", "nb_operands": 2, "is_commutative": False, "symbol": "LShR"},
m_sar: {"name": "sar", "nb_operands": 2, "is_commutative": False, "symbol": ">>"},
m_cfadd: {"name": "cfadd", "nb_operands": 2, "is_commutative": True},
m_ofadd: {"name": "ofadd", "nb_operands": 2, "is_commutative": True},
m_cfshl: {"name": "cfshl", "nb_operands": 2, "is_commutative": False},
m_cfshr: {"name": "cfshr", "nb_operands": 2, "is_commutative": False},
m_sets: {"name": "sets", "nb_operands": 2, "is_commutative": False},
m_seto: {"name": "seto", "nb_operands": 2, "is_commutative": False},
m_setp: {"name": "setp", "nb_operands": 2, "is_commutative": False},
m_setnz: {"name": "setnz", "nb_operands": 2, "is_commutative": True, "symbol": "!="},
m_setz: {"name": "setz", "nb_operands": 2, "is_commutative": True, "symbol": "=="},
m_seta: {"name": "seta", "nb_operands": 2, "is_commutative": False, "symbol": ">"},
m_setae: {"name": "setae", "nb_operands": 2, "is_commutative": False, "symbol": ">="},
m_setb: {"name": "setb", "nb_operands": 2, "is_commutative": False, "symbol": "<"},
m_setbe: {"name": "setbe", "nb_operands": 2, "is_commutative": False, "symbol": "<="},
m_setg: {"name": "setg", "nb_operands": 2, "is_commutative": False, "symbol": "UGT"},
m_setge: {"name": "setge", "nb_operands": 2, "is_commutative": False, "symbol": "UGE"},
m_setl: {"name": "setl", "nb_operands": 2, "is_commutative": False, "symbol": "ULT"},
m_setle: {"name": "setle", "nb_operands": 2, "is_commutative": False, "symbol": "ULE"},
m_jcnd: {"name": "jcnd", "nb_operands": 1, "is_commutative": False},
m_jnz: {"name": "jnz", "nb_operands": 2, "is_commutative": True},
m_jz: {"name": "jz", "nb_operands": 2, "is_commutative": True},
m_jae: {"name": "jae", "nb_operands": 2, "is_commutative": False},
m_jb: {"name": "jb", "nb_operands": 2, "is_commutative": False},
m_ja: {"name": "ja", "nb_operands": 2, "is_commutative": False},
m_jbe: {"name": "jbe", "nb_operands": 2, "is_commutative": False},
m_jg: {"name": "jg", "nb_operands": 2, "is_commutative": False},
m_jge: {"name": "jge", "nb_operands": 2, "is_commutative": False},
m_jl: {"name": "jl", "nb_operands": 2, "is_commutative": False},
m_jle: {"name": "jle", "nb_operands": 2, "is_commutative": False},
m_jtbl: {"name": "jtbl", "nb_operands": 2, "is_commutative": False},
m_ijmp: {"name": "ijmp", "nb_operands": 2, "is_commutative": False},
m_goto: {"name": "goto", "nb_operands": 1, "is_commutative": False},
m_call: {"name": "call", "nb_operands": 2, "is_commutative": False},
m_icall: {"name": "icall", "nb_operands": 2, "is_commutative": False},
m_ret: {"name": "ret", "nb_operands": 0, "is_commutative": False},
m_push: {"name": "push", "nb_operands": 0, "is_commutative": False},
m_pop: {"name": "pop", "nb_operands": 0, "is_commutative": False},
m_und: {"name": "und", "nb_operands": 0, "is_commutative": False},
m_ext: {"name": "ext", "nb_operands": 0, "is_commutative": False},
m_f2i: {"name": "f2i", "nb_operands": 2, "is_commutative": False},
m_f2u: {"name": "f2u", "nb_operands": 2, "is_commutative": False},
m_i2f: {"name": "i2f", "nb_operands": 2, "is_commutative": False},
m_u2f: {"name": "u2f", "nb_operands": 2, "is_commutative": False},
m_f2f: {"name": "f2f", "nb_operands": 2, "is_commutative": False},
m_fneg: {"name": "fneg", "nb_operands": 2, "is_commutative": False},
m_fadd: {"name": "fadd", "nb_operands": 2, "is_commutative": True},
m_fsub: {"name": "fsub", "nb_operands": 2, "is_commutative": False},
m_fmul: {"name": "fmul", "nb_operands": 2, "is_commutative": True},
m_fdiv: {"name": "fdiv", "nb_operands": 2, "is_commutative": False},
}
MATURITY_TO_STRING_DICT = {
MMAT_ZERO: "MMAT_ZERO",
MMAT_GENERATED: "MMAT_GENERATED",
MMAT_PREOPTIMIZED: "MMAT_PREOPTIMIZED",
MMAT_LOCOPT: "MMAT_LOCOPT",
MMAT_CALLS: "MMAT_CALLS",
MMAT_GLBOPT1: "MMAT_GLBOPT1",
MMAT_GLBOPT2: "MMAT_GLBOPT2",
MMAT_GLBOPT3: "MMAT_GLBOPT3",
MMAT_LVARS: "MMAT_LVARS",
}
STRING_TO_MATURITY_DICT = {v: k for k, v in MATURITY_TO_STRING_DICT.items()}
MOP_TYPE_TO_STRING_DICT = {
mop_z: "mop_z",
mop_r: "mop_r",
mop_n: "mop_n",
mop_str: "mop_str",
mop_d: "mop_d",
mop_S: "mop_S",
mop_v: "mop_v",
mop_b: "mop_b",
mop_f: "mop_f",
mop_l: "mop_l",
mop_a: "mop_a",
mop_h: "mop_h",
mop_c: "mop_c",
mop_fn: "mop_fn",
mop_p: "mop_p",
mop_sc: "mop_sc",
}
Z3_SPECIAL_OPERANDS = ["UDiv", "URem", "LShR", "UGT", "UGE", "ULT", "ULE"]
BOOLEAN_OPCODES = [m_lnot, m_bnot, m_or, m_and, m_xor]
ARITHMETICAL_OPCODES = [m_neg, m_add, m_sub, m_mul, m_udiv, m_sdiv, m_umod, m_smod]
BIT_OPERATIONS_OPCODES = [m_shl, m_shr, m_sar, m_mov, m_xds, m_xdu, m_low, m_high]
CHECK_OPCODES = [m_sets, m_seto, m_setp, m_setnz, m_setz, m_seta, m_setae, m_setb,
m_setbe, m_setg, m_setge, m_setl, m_setle]
MBA_RELATED_OPCODES = BOOLEAN_OPCODES + ARITHMETICAL_OPCODES + BIT_OPERATIONS_OPCODES + CHECK_OPCODES
CONDITIONAL_JUMP_OPCODES = [m_jcnd, m_jnz, m_jz, m_jae, m_ja, m_jb, m_jbe, m_jg, m_jge, m_jl, m_jle, m_jtbl]
UNCONDITIONAL_JUMP_OPCODES = [m_goto, m_ijmp]
CONTROL_FLOW_OPCODES = CONDITIONAL_JUMP_OPCODES + UNCONDITIONAL_JUMP_OPCODES
MINSN_TO_AST_FORBIDDEN_OPCODES = CONTROL_FLOW_OPCODES + [m_ret, m_nop, m_stx, m_push, m_pop, m_und, m_ext, m_call]
SUB_TABLE = {1: 0x100, 2: 0x10000, 4: 0x100000000, 8: 0x10000000000000000}
AND_TABLE = {1: 0xff, 2: 0xffff, 4: 0xffffffff, 8: 0xffffffffffffffff}
MSB_TABLE = {1: 0x80, 2: 0x8000, 4: 0x80000000, 8: 0x8000000000000000}
# Hex-Rays mop equality checking
def equal_bnot_cst(lo: mop_t, ro: mop_t) -> bool:
if (lo.t != mop_n) or (ro.t != mop_n):
return False
if lo.size != ro.size:
return False
return lo.nnn.value ^ ro.nnn.value == AND_TABLE[lo.size]
def equal_bnot_mop(lo: mop_t, ro: mop_t, test_two_sides=True) -> bool:
if lo.t == mop_n:
return equal_bnot_cst(lo, ro)
# We first check for a bnot operand
if (lo.t == mop_d) and lo.d.opcode == m_bnot:
if equal_mops_ignore_size(lo.d.l, ro):
return True
# Otherwise Hexrays may have optimized using ~(-x) = x - 1
if (lo.t == mop_d) and lo.d.opcode == m_neg:
if (ro.t == mop_d) and ro.d.opcode == m_sub:
if ro.d.r.t == mop_n and ro.d.r.nnn.value == 1:
if equal_mops_ignore_size(ro.d.l, lo.d.l):
return True
if (lo.t == mop_d) and lo.d.opcode == m_xds:
if equal_bnot_mop(lo.d.l, ro):
return True
if test_two_sides:
return equal_bnot_mop(ro, lo, test_two_sides=False)
return False
def equal_ignore_msb_cst(lo: mop_t, ro: mop_t) -> bool:
if (lo.t != mop_n) or (ro.t != mop_n):
return False
if lo.size != ro.size:
return False
mask = AND_TABLE[lo.size] ^ MSB_TABLE[lo.size]
return lo.nnn.value & mask == ro.nnn.value & mask
def equal_mops_bypass_xdu(lo: mop_t, ro: mop_t) -> bool:
if (lo is None) or (ro is None):
return False
if (lo.t == mop_d) and (lo.d.opcode == m_xdu):
return equal_mops_bypass_xdu(lo.d.l, ro)
if (ro.t == mop_d) and (ro.d.opcode == m_xdu):
return equal_mops_bypass_xdu(lo, ro.d.l)
return equal_mops_ignore_size(lo, ro)
def equal_mops_ignore_size(lo: mop_t, ro: mop_t) -> bool:
if (lo is None) or (ro is None):
return False
if lo.t != ro.t:
return False
if lo.t == mop_z:
return True
elif lo.t == mop_fn:
return lo.fpc == ro.fpc
elif lo.t == mop_n:
return lo.nnn.value == ro.nnn.value
elif lo.t == mop_S:
if lo.s == ro.s:
return True
if lo.s.off == ro.s.off:
# Is it right?
return True
return False
elif lo.t == mop_v:
return lo.g == ro.g
elif lo.t == mop_d:
return lo.d.equal_insns(ro.d, EQ_IGNSIZE)
# return lo.d.equal_insns(ro.d, EQ_IGNSIZE | EQ_IGNCODE)
elif lo.t == mop_b:
return lo.b == ro.b
elif lo.t == mop_r:
return lo.r == ro.r
elif lo.t == mop_f:
return False
elif lo.t == mop_l:
return lo.l == ro.l
elif lo.t == mop_a:
if lo.a.insize != ro.a.insize:
return False
if lo.a.outsize != ro.a.outsize:
return False
return equal_mops_ignore_size(lo.a, ro.a)
elif lo.t == mop_h:
return ro.helper == lo.helper
elif lo.t == mop_str:
return ro.cstr == lo.cstr
elif lo.t == mop_c:
return ro.c == lo.c
elif lo.t == mop_p:
return equal_mops_ignore_size(lo.pair.lop, ro.pair.lop) and equal_mops_ignore_size(lo.pair.hop, ro.pair.hop)
elif lo.t == mop_sc:
return False
else:
return False
def is_check_mop(lo: mop_t) -> bool:
if lo.t != mop_d:
return False
if lo.d.opcode in CHECK_OPCODES:
return True
if lo.d.opcode in [m_xds, m_xdu]:
return is_check_mop(lo.d.l)
return False
def extract_num_mop(ins: minsn_t) -> Tuple[mop_t, mop_t]:
num_mop = None
other_mop = None
if ins.l.t == mop_n:
num_mop = ins.l
other_mop = ins.r
if ins.r.t == mop_n:
num_mop = ins.r
other_mop = ins.l
return [num_mop, other_mop]
def check_ins_mop_size_are_ok(ins: minsn_t) -> bool:
"""
This function can be used to check if a created instruction has consistent mop size
Use it to avoid Hex-Rays decompilation errors when replacing instructions
:param ins:
:return:
"""
ins_dest_size = ins.d.size
if ins.opcode in [m_stx, m_ldx]:
if ins.r.t == mop_d:
if not check_ins_mop_size_are_ok(ins.r.d):
return False
return True
if ins.opcode in [m_xdu, m_xds, m_low, m_high]:
if (ins.l.t == mop_d) and (not check_ins_mop_size_are_ok(ins.l.d)):
return False
return True
if ins.opcode in [m_sar, m_shr, m_shl]:
if ins.l.size != ins_dest_size:
return False
if (ins.l.t == mop_d) and (not check_ins_mop_size_are_ok(ins.l.d)):
return False
if (ins.r.t == mop_d) and (not check_ins_mop_size_are_ok(ins.r.d)):
return False
return True
if ins.opcode in CHECK_OPCODES:
if (ins.l.t == mop_d) and (not check_ins_mop_size_are_ok(ins.l.d)):
return False
if (ins.r.t == mop_d) and (not check_ins_mop_size_are_ok(ins.r.d)):
return False
return True
if ins.l is not None:
if ins.l.size != ins_dest_size:
return False
if ins.l.t == mop_d and (not check_ins_mop_size_are_ok(ins.l.d)):
return False
if ins.r is not None and ins.r.t != mop_z:
if ins.r.size != ins_dest_size:
return False
if ins.r.t == mop_d and (not check_ins_mop_size_are_ok(ins.r.d)):
return False
return True
def check_mop_is_result_of(lo: mop_t, mc) -> bool:
if lo.t != mop_d:
return False
return lo.d.opcode == mc
def extract_by_opcode_type(ins: minsn_t, mc) -> Tuple[mop_t, mop_t]:
if check_mop_is_result_of(ins.l, mc):
return [ins.l, ins.r]
if check_mop_is_result_of(ins.r, mc):
return [ins.r, ins.l]
return [None, None]
def check_ins_have_same_operands(ins1: minsn_t, ins2: minsn_t, ignore_order=False) -> bool:
if equal_mops_ignore_size(ins1.l, ins2.l) and equal_mops_ignore_size(ins1.r, ins2.r):
return True
if not ignore_order:
return False
return equal_mops_ignore_size(ins1.l, ins2.r) and equal_mops_ignore_size(ins1.r, ins2.l)
def get_mop_index(searched_mop: mop_t, mop_list) -> int:
for i, test_mop in enumerate(mop_list):
if equal_mops_ignore_size(searched_mop, test_mop):
return i
return -1
def append_mop_if_not_in_list(mop: mop_t, mop_list) -> bool:
mop_index = get_mop_index(mop, mop_list)
if mop_index == -1:
mop_list.append(mop)
return True
return False
def get_blk_index(searched_blk: mblock_t, blk_list: List[mblock_t]) -> int:
blk_serial_list = [blk.serial for blk in blk_list]
try:
return blk_serial_list.index(searched_blk.serial)
except ValueError:
return -1

280
d810/hexrays_hooks.py Normal file
View File

@@ -0,0 +1,280 @@
from __future__ import annotations
import logging
from ida_hexrays import *
from d810.optimizers.instructions import PatternOptimizer, ChainOptimizer, Z3Optimizer, EarlyOptimizer, \
InstructionAnalyzer
from d810.hexrays_helpers import check_ins_mop_size_are_ok, append_mop_if_not_in_list
from d810.hexrays_formatters import format_minsn_t, format_mop_t, maturity_to_string, mop_type_to_string, \
dump_microcode_for_debug
from d810.errors import D810Exception
from d810.z3_utils import log_z3_instructions
from typing import TYPE_CHECKING, List
if TYPE_CHECKING:
from d810.manager import D810Manager
from d810.optimizers.instructions.handler import InstructionOptimizer, InstructionOptimizationRule
from d810.optimizers.flow.handler import FlowOptimizationRule
main_logger = logging.getLogger('D810')
optimizer_logger = logging.getLogger('D810.optimizer')
helper_logger = logging.getLogger('D810.helper')
DEFAULT_OPTIMIZATION_PATTERN_MATURITIES = [MMAT_PREOPTIMIZED, MMAT_LOCOPT, MMAT_CALLS, MMAT_GLBOPT1]
DEFAULT_OPTIMIZATION_CHAIN_MATURITIES = [MMAT_PREOPTIMIZED, MMAT_LOCOPT, MMAT_CALLS, MMAT_GLBOPT1]
DEFAULT_OPTIMIZATION_Z3_MATURITIES = [MMAT_LOCOPT, MMAT_CALLS, MMAT_GLBOPT1]
DEFAULT_OPTIMIZATION_EARLY_MATURITIES = [MMAT_GENERATED, MMAT_PREOPTIMIZED]
DEFAULT_ANALYZER_MATURITIES = [MMAT_PREOPTIMIZED, MMAT_LOCOPT, MMAT_CALLS, MMAT_GLBOPT1]
class InstructionDefUseCollector(mop_visitor_t):
def __init__(self):
super().__init__()
self.unresolved_ins_mops = []
self.memory_unresolved_ins_mops = []
self.target_mops = []
def visit_mop(self, op: mop_t, op_type: int, is_target: bool):
if is_target:
append_mop_if_not_in_list(op, self.target_mops)
else:
# TODO whatever the case, in the end we will always return 0. May be this code can be better optimized.
# TODO handle other special case (e.g. ldx ins, ...)
if op.t == mop_S:
append_mop_if_not_in_list(op, self.unresolved_ins_mops)
elif op.t == mop_r:
append_mop_if_not_in_list(op, self.unresolved_ins_mops)
elif op.t == mop_v:
append_mop_if_not_in_list(op, self.memory_unresolved_ins_mops)
elif op.t == mop_a:
if op.a.t == mop_v:
return 0
elif op.a.t == mop_S:
return 0
helper_logger.warning("Calling visit_mop with unsupported mop type {0} - {1}: '{2}'"
.format(mop_type_to_string(op.t), mop_type_to_string(op.a.t), format_mop_t(op)))
return 0
elif op.t == mop_n:
return 0
elif op.t == mop_d:
return 0
elif op.t == mop_h:
return 0
elif op.t == mop_b:
return 0
else:
helper_logger.warning("Calling visit_mop with unsupported mop type {0}: '{1}'"
.format(mop_type_to_string(op.t), format_mop_t(op)))
return 0
class InstructionOptimizerManager(optinsn_t):
def __init__(self, manager: D810Manager):
optimizer_logger.debug("Initializing {0}...".format(self.__class__.__name__))
super().__init__()
self.manager = manager
self.instruction_visitor = InstructionVisitorManager(self)
self._last_optimizer_tried = None
self.current_maturity = None
self.current_blk_serial = None
self.generate_z3_code = False
self.dump_intermediate_microcode = False
self.instruction_optimizers = []
self.optimizer_usage_info = {}
self.add_optimizer(PatternOptimizer(DEFAULT_OPTIMIZATION_PATTERN_MATURITIES, log_dir=self.manager.log_dir))
self.add_optimizer(ChainOptimizer(DEFAULT_OPTIMIZATION_CHAIN_MATURITIES, log_dir=self.manager.log_dir))
self.add_optimizer(Z3Optimizer(DEFAULT_OPTIMIZATION_Z3_MATURITIES, log_dir=self.manager.log_dir))
self.add_optimizer(EarlyOptimizer(DEFAULT_OPTIMIZATION_EARLY_MATURITIES, log_dir=self.manager.log_dir))
self.analyzer = InstructionAnalyzer(DEFAULT_ANALYZER_MATURITIES, log_dir=self.manager.log_dir)
def func(self, blk: mblock_t, ins: minsn_t) -> bool:
self.log_info_on_input(blk, ins)
try:
optimization_performed = self.optimize(blk, ins)
if not optimization_performed:
optimization_performed = ins.for_all_insns(self.instruction_visitor)
if optimization_performed:
ins.optimize_solo()
if blk is not None:
blk.mark_lists_dirty()
blk.mba.verify(True)
return optimization_performed
except RuntimeError as e:
optimizer_logger.error("RuntimeError while optimizing ins {0} with {1}: {2}"
.format(format_minsn_t(ins), self._last_optimizer_tried, e))
except D810Exception as e:
optimizer_logger.error("D810Exception while optimizing ins {0} with {1}: {2}"
.format(format_minsn_t(ins), self._last_optimizer_tried, e))
return False
def reset_rule_usage_statistic(self):
self.optimizer_usage_info = {}
for ins_optimizer in self.instruction_optimizers:
self.optimizer_usage_info[ins_optimizer.name] = 0
ins_optimizer.reset_rule_usage_statistic()
def show_rule_usage_statistic(self):
for optimizer_name, optimizer_nb_match in self.optimizer_usage_info.items():
if optimizer_nb_match > 0:
main_logger.info("Instruction optimizer '{0}' has been used {1} times"
.format(optimizer_name, optimizer_nb_match))
for ins_optimizer in self.instruction_optimizers:
ins_optimizer.show_rule_usage_statistic()
def log_info_on_input(self, blk: mblock_t, ins: minsn_t):
if blk is None:
return
mba: mbl_array_t = blk.mba
if (mba is not None) and (mba.maturity != self.current_maturity):
self.current_maturity = mba.maturity
main_logger.debug("Instruction optimization function called at maturity: {0}"
.format(maturity_to_string(self.current_maturity)))
self.analyzer.set_maturity(self.current_maturity)
self.current_blk_serial = None
for ins_optimizer in self.instruction_optimizers:
ins_optimizer.cur_maturity = self.current_maturity
if self.dump_intermediate_microcode:
dump_microcode_for_debug(mba, self.manager.log_dir, "input_instruction_optimizer")
if blk.serial != self.current_blk_serial:
self.current_blk_serial = blk.serial
def add_optimizer(self, optimizer: InstructionOptimizer):
self.instruction_optimizers.append(optimizer)
self.optimizer_usage_info[optimizer.name] = 0
def add_rule(self, rule: InstructionOptimizationRule):
# optimizer_log.info("Trying to add rule {0}".format(rule))
for ins_optimizer in self.instruction_optimizers:
ins_optimizer.add_rule(rule)
self.analyzer.add_rule(rule)
def configure(self, generate_z3_code=False, dump_intermediate_microcode=False, **kwargs):
self.generate_z3_code = generate_z3_code
self.dump_intermediate_microcode = dump_intermediate_microcode
def optimize(self, blk: mblock_t, ins: minsn_t) -> bool:
# optimizer_log.info("Trying to optimize {0}".format(format_minsn_t(ins)))
for ins_optimizer in self.instruction_optimizers:
self._last_optimizer_tried = ins_optimizer
new_ins = ins_optimizer.get_optimized_instruction(blk, ins)
if new_ins is not None:
if not check_ins_mop_size_are_ok(new_ins):
if check_ins_mop_size_are_ok(ins):
main_logger.error("Invalid optimized instruction: {0} (original was {1})".format(
format_minsn_t(new_ins), format_minsn_t(ins)))
else:
main_logger.error("Invalid original instruction : {0} (original was {1})".format(
format_minsn_t(new_ins), format_minsn_t(ins)))
else:
ins.swap(new_ins)
self.optimizer_usage_info[ins_optimizer.name] += 1
if self.generate_z3_code:
try:
log_z3_instructions(new_ins, ins)
except KeyError:
pass
return True
self.analyzer.analyze(blk, ins)
return False
class InstructionVisitorManager(minsn_visitor_t):
def __init__(self, optimizer: InstructionOptimizerManager):
optimizer_logger.debug("Initializing {0}...".format(self.__class__.__name__))
super().__init__()
self.instruction_optimizer = optimizer
def visit_minsn(self) -> bool:
return self.instruction_optimizer.optimize(self.blk, self.curins)
class BlockOptimizerManager(optblock_t):
def __init__(self, manager: D810Manager):
optimizer_logger.debug("Initializing {0}...".format(self.__class__.__name__))
super().__init__()
self.manager = manager
self.cfg_rules = set()
self.current_maturity = None
self.cfg_rules_usage_info = {}
def func(self, blk: mblock_t):
self.log_info_on_input(blk)
nb_patch = self.optimize(blk)
return nb_patch
def reset_rule_usage_statistic(self):
self.cfg_rules_usage_info = {}
for rule in self.cfg_rules:
self.cfg_rules_usage_info[rule.name] = []
def show_rule_usage_statistic(self):
for rule_name, rule_nb_patch_list in self.cfg_rules_usage_info.items():
nb_use = len(rule_nb_patch_list)
if nb_use > 0:
main_logger.info("BlkRule '{0}' has been used {1} times for a total of {2} patches"
.format(rule_name, nb_use, sum(rule_nb_patch_list)))
def log_info_on_input(self, blk: mblock_t):
if blk is None:
return
mba: mbl_array_t = blk.mba
if (mba is not None) and (mba.maturity != self.current_maturity):
main_logger.debug("BlockOptimizer called at maturity: {0}".format(maturity_to_string(mba.maturity)))
self.current_maturity = mba.maturity
def optimize(self, blk: mblock_t):
for cfg_rule in self.cfg_rules:
if self.check_if_rule_is_activated_for_address(cfg_rule, blk.mba.entry_ea):
nb_patch = cfg_rule.optimize(blk)
if nb_patch > 0:
optimizer_logger.info("Rule {0} matched: {1} patches".format(cfg_rule.name, nb_patch))
self.cfg_rules_usage_info[cfg_rule.name].append(nb_patch)
return nb_patch
return 0
def add_rule(self, cfg_rule: FlowOptimizationRule):
optimizer_logger.info("Adding cfg rule {0}".format(cfg_rule))
self.cfg_rules.add(cfg_rule)
self.cfg_rules_usage_info[cfg_rule.name] = []
def configure(self, **kwargs):
pass
def check_if_rule_is_activated_for_address(self, cfg_rule: FlowOptimizationRule, func_entry_ea: int):
if cfg_rule.use_whitelist and (func_entry_ea not in cfg_rule.whitelisted_function_ea_list):
return False
if cfg_rule.use_blacklist and (func_entry_ea in cfg_rule.blacklisted_function_ea_list):
return False
return True
class HexraysDecompilationHook(Hexrays_Hooks):
def __init__(self, manager):
super().__init__()
self.manager = manager
def prolog(self, mba: mbl_array_t, fc, reachable_blocks, decomp_flags) -> "int":
main_logger.info("Starting decompilation of function at 0x{0:x}".format(mba.entry_ea))
self.manager.instruction_optimizer.reset_rule_usage_statistic()
self.manager.block_optimizer.reset_rule_usage_statistic()
return 0
def glbopt(self, mba: mbl_array_t) -> "int":
main_logger.info("glbopt finished for function at 0x{0:x}".format(mba.entry_ea))
self.manager.instruction_optimizer.show_rule_usage_statistic()
self.manager.block_optimizer.show_rule_usage_statistic()
return 0

541
d810/ida_ui.py Normal file
View File

@@ -0,0 +1,541 @@
# -*- coding: utf-8 -*-
import json
import logging
import idaapi
import ida_kernwin
from PyQt5 import QtCore, QtWidgets, QtGui
from d810.conf import ProjectConfiguration, RuleConfiguration
logger = logging.getLogger('D810.ui')
class PluginConfigurationFileForm_t(QtWidgets.QDialog):
def __init__(self, parent, state):
logger.debug("Initializing PluginConfigurationFileForm_t")
super().__init__(parent)
self.state = state
self.log_dir_changed = False
self.log_dir = self.state.d810_config.get("log_dir")
self.erase_logs_on_reload = self.state.d810_config.get("erase_logs_on_reload")
self.generate_z3_code = self.state.d810_config.get("generate_z3_code")
self.dump_intermediate_microcode = self.state.d810_config.get("dump_intermediate_microcode")
self.resize(1000, 500)
self.setWindowTitle("Plugin Configuration")
# Main layout
self.config_layout = QtWidgets.QVBoxLayout(self)
self.layout_log_dir = QtWidgets.QHBoxLayout()
self.lbl_log_dir_info = QtWidgets.QLabel(self)
self.lbl_log_dir_info.setText("Current log directory path: ")
self.layout_log_dir.addWidget(self.lbl_log_dir_info)
self.lbl_log_dir = QtWidgets.QLabel(self)
self.lbl_log_dir.setText(self.log_dir)
self.layout_log_dir.addWidget(self.lbl_log_dir)
self.button_change_log_dir = QtWidgets.QPushButton(self)
self.button_change_log_dir.setText("Change log directory")
self.button_change_log_dir.clicked.connect(self.choose_log_dir)
self.layout_log_dir.addWidget(self.button_change_log_dir)
self.config_layout.addLayout(self.layout_log_dir)
self.checkbox_generate_z3_code = QtWidgets.QCheckBox("Generate Z3 code for simplification performed", self)
self.checkbox_generate_z3_code.setChecked(self.state.d810_config.get("generate_z3_code"))
self.config_layout.addWidget(self.checkbox_generate_z3_code)
self.checkbox_dump_intermediate_microcode = QtWidgets.QCheckBox("Dump functions microcode at each maturity", self)
self.checkbox_dump_intermediate_microcode.setChecked(self.state.d810_config.get("dump_intermediate_microcode"))
self.config_layout.addWidget(self.checkbox_dump_intermediate_microcode)
self.checkbox_erase_logs_on_reload = QtWidgets.QCheckBox("Erase log directory content when plugin is reloaded", self)
self.checkbox_erase_logs_on_reload.setChecked(self.state.d810_config.get("erase_logs_on_reload"))
self.config_layout.addWidget(self.checkbox_erase_logs_on_reload)
self.layout_button = QtWidgets.QHBoxLayout()
self.button_save = QtWidgets.QPushButton(self)
self.button_save.setText("Save")
self.button_save.clicked.connect(self.save_config)
self.layout_button.addWidget(self.button_save)
self.button_cancel = QtWidgets.QPushButton(self)
self.button_cancel.setText("Cancel")
self.button_cancel.clicked.connect(self.reject)
self.layout_button.addWidget(self.button_cancel)
self.config_layout.addLayout(self.layout_button)
self.setLayout(self.config_layout)
def choose_log_dir(self):
logger.debug("Calling save_rule_configuration")
log_dir = QtWidgets.QFileDialog.getExistingDirectory(self, "Open Directory", "/home",
QtWidgets.QFileDialog.ShowDirsOnly |
QtWidgets.QFileDialog.DontResolveSymlinks)
if log_dir != "":
self.log_dir = log_dir
self.log_dir_changed = True
self.lbl_log_dir.setText(self.log_dir)
def save_config(self):
if self.log_dir_changed:
self.state.d810_config.set("log_dir", self.log_dir)
self.state.d810_config.set("erase_logs_on_reload", self.checkbox_erase_logs_on_reload.isChecked())
self.state.d810_config.set("generate_z3_code", self.checkbox_generate_z3_code.isChecked())
self.state.d810_config.set("dump_intermediate_microcode", self.checkbox_dump_intermediate_microcode.isChecked())
self.state.d810_config.save()
self.accept()
class EditConfigurationFileForm_t(QtWidgets.QDialog):
def __init__(self, parent, state):
logger.debug("Initializing EditConfigurationFileForm_t")
super().__init__(parent)
self.state = state
self.resize(1000, 500)
self.setWindowTitle("Rule Configuration Editor")
# Main layout
self.config_layout = QtWidgets.QVBoxLayout(self)
# Configuration Name Selection Layout
self.layout_cfg_name = QtWidgets.QHBoxLayout()
self.lbl_cfg_name = QtWidgets.QLabel(self)
self.lbl_cfg_name.setText("Rule Name")
self.layout_cfg_name.addWidget(self.lbl_cfg_name)
self.in_cfg_name = QtWidgets.QLineEdit(self)
self.layout_cfg_name.addWidget(self.in_cfg_name)
self.config_layout.addLayout(self.layout_cfg_name)
# Instructions rule Selection Layout
self.table_ins_rule_selection = QtWidgets.QTableWidget(self)
# self.table_ins_rule_selection.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.table_ins_rule_selection.setRowCount(2)
self.table_ins_rule_selection.setColumnCount(4)
item = QtWidgets.QTableWidgetItem()
item.setText("Is activated")
self.table_ins_rule_selection.setHorizontalHeaderItem(0, item)
item = QtWidgets.QTableWidgetItem()
item.setText("Rule Name")
self.table_ins_rule_selection.setHorizontalHeaderItem(1, item)
item = QtWidgets.QTableWidgetItem()
item.setText("Rule Description")
self.table_ins_rule_selection.setHorizontalHeaderItem(2, item)
item = QtWidgets.QTableWidgetItem()
item.setText("Rule Configuration")
self.table_ins_rule_selection.setHorizontalHeaderItem(3, item)
self.table_ins_rule_selection.horizontalHeader().setStretchLastSection(True)
self.table_ins_rule_selection.verticalHeader().setVisible(False)
self.table_ins_rule_selection.setSortingEnabled(True)
# self.table_ins_rule_selection.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.config_layout.addWidget(self.table_ins_rule_selection)
# Block rule Selection Layout
self.table_blk_rule_selection = QtWidgets.QTableWidget(self)
# self.table_blk_rule_selection.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.table_blk_rule_selection.setRowCount(2)
self.table_blk_rule_selection.setColumnCount(4)
item = QtWidgets.QTableWidgetItem()
item.setText("Is activated")
self.table_blk_rule_selection.setHorizontalHeaderItem(0, item)
item = QtWidgets.QTableWidgetItem()
item.setText("Rule Name")
self.table_blk_rule_selection.setHorizontalHeaderItem(1, item)
item = QtWidgets.QTableWidgetItem()
item.setText("Rule Description")
self.table_blk_rule_selection.setHorizontalHeaderItem(2, item)
item = QtWidgets.QTableWidgetItem()
item.setText("Rule Configuration")
self.table_blk_rule_selection.setHorizontalHeaderItem(3, item)
self.table_blk_rule_selection.horizontalHeader().setStretchLastSection(True)
self.table_blk_rule_selection.verticalHeader().setVisible(False)
self.table_blk_rule_selection.setSortingEnabled(True)
# self.table_blk_rule_selection.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.config_layout.addWidget(self.table_blk_rule_selection)
self.layout_button = QtWidgets.QHBoxLayout()
self.button_save = QtWidgets.QPushButton(self)
self.button_save.setText("Save")
self.button_save.clicked.connect(self.save_rule_configuration)
self.layout_button.addWidget(self.button_save)
self.button_cancel = QtWidgets.QPushButton(self)
self.button_cancel.setText("Cancel")
self.button_cancel.clicked.connect(self.reject)
self.layout_button.addWidget(self.button_cancel)
self.config_layout.addLayout(self.layout_button)
self.setLayout(self.config_layout)
self.config_path = None
self.config_description = None
self.config_rules = []
self.update_table_rule_selection()
def update_form(self, config_description=None, activated_ins_rule_config_list=None, activated_blk_rule_config_list=None, config_path=None):
logger.debug("Calling update_form")
if config_description is not None:
self.in_cfg_name.setText(config_description)
if activated_ins_rule_config_list is not None or activated_blk_rule_config_list is not None:
self.update_table_rule_selection(activated_ins_rule_config_list, activated_blk_rule_config_list)
if config_path is not None:
self.config_path = config_path
def update_table_rule_selection(self, activated_ins_rule_config_list=None, activated_blk_rule_config_list=None):
logger.debug("Calling update_table_rule_selection")
self.update_table_ins_rule_selection(activated_ins_rule_config_list)
self.update_table_blk_rule_selection(activated_blk_rule_config_list)
def _get_rule_config(self, rule_name, rule_config_list):
logger.debug("Calling _get_rule_config")
try:
rule_name_list = [rule_conf.name for rule_conf in rule_config_list]
rule_index = rule_name_list.index(rule_name)
return rule_config_list[rule_index]
except ValueError:
return None
def update_table_ins_rule_selection(self, activated_ins_rule_config_list=None):
logger.debug("Calling update_table_ins_rule_selection")
if activated_ins_rule_config_list is None:
activated_ins_rule_config_list = []
self.table_ins_rule_selection.setRowCount(len(self.state.known_ins_rules))
for i, rule in enumerate(self.state.known_ins_rules):
rule_config = self._get_rule_config(rule.name, activated_ins_rule_config_list)
item = QtWidgets.QTableWidgetItem()
item.setTextAlignment(QtCore.Qt.AlignCenter)
if rule_config is not None and rule_config.is_activated:
item.setCheckState(QtCore.Qt.Checked)
else:
item.setCheckState(QtCore.Qt.Unchecked)
self.table_ins_rule_selection.setItem(i, 0, item)
item = QtWidgets.QTableWidgetItem()
item.setText(rule.name)
item.setFlags(QtCore.Qt.ItemIsEnabled)
self.table_ins_rule_selection.setItem(i, 1, item)
item = QtWidgets.QTableWidgetItem()
item.setText(rule.description)
item.setFlags(QtCore.Qt.ItemIsEnabled)
self.table_ins_rule_selection.setItem(i, 2, item)
item = QtWidgets.QTableWidgetItem()
if rule_config is not None:
item.setText(json.dumps(rule_config.config))
else:
item.setText("{}")
self.table_ins_rule_selection.setItem(i, 3, item)
self.table_ins_rule_selection.resizeColumnsToContents()
def update_table_blk_rule_selection(self, activated_blk_rule_config_list=None):
logger.debug("Calling update_table_blk_rule_selection")
if activated_blk_rule_config_list is None:
activated_blk_rule_config_list = []
self.table_blk_rule_selection.setRowCount(len(self.state.known_blk_rules))
for i, rule in enumerate(self.state.known_blk_rules):
rule_config = self._get_rule_config(rule.name, activated_blk_rule_config_list)
item = QtWidgets.QTableWidgetItem()
item.setTextAlignment(QtCore.Qt.AlignCenter)
if rule_config is not None and rule_config.is_activated:
item.setCheckState(QtCore.Qt.Checked)
else:
item.setCheckState(QtCore.Qt.Unchecked)
self.table_blk_rule_selection.setItem(i, 0, item)
item = QtWidgets.QTableWidgetItem()
item.setText(rule.name)
item.setFlags(QtCore.Qt.ItemIsEnabled)
self.table_blk_rule_selection.setItem(i, 1, item)
item = QtWidgets.QTableWidgetItem()
item.setText(rule.description)
item.setFlags(QtCore.Qt.ItemIsEnabled)
self.table_blk_rule_selection.setItem(i, 2, item)
item = QtWidgets.QTableWidgetItem()
if rule_config is not None:
item.setText(json.dumps(rule_config.config))
else:
item.setText("{}")
self.table_blk_rule_selection.setItem(i, 3, item)
self.table_blk_rule_selection.resizeColumnsToContents()
def save_rule_configuration(self):
logger.debug("Calling save_rule_configuration")
fname, _ = QtWidgets.QFileDialog.getSaveFileName(self, 'Save file', self.config_path, "Project configuration (*.json)")
if fname:
self.config_path = fname
self.config_description = self.in_cfg_name.text()
self.config_ins_rules = self.get_ins_rules()
self.config_blk_rules = self.get_blk_rules()
self.accept()
def get_ins_rules(self):
logger.debug("Calling get_ins_rules")
activated_rule_names = []
nb_rules = self.table_ins_rule_selection.rowCount()
for i in range(nb_rules):
if self.table_ins_rule_selection.item(i, 0).checkState():
rule_conf = RuleConfiguration(name=self.table_ins_rule_selection.item(i, 1).text(),
is_activated=self.table_ins_rule_selection.item(i, 0).checkState() == QtCore.Qt.Checked,
config=json.loads(self.table_ins_rule_selection.item(i, 3).text()))
activated_rule_names.append(rule_conf)
# activated_rule_names.append(self.table_ins_rule_selection.item(i, 1).text())
return activated_rule_names
def get_blk_rules(self):
logger.debug("Calling get_blk_rules")
activated_rule_names = []
nb_rules = self.table_blk_rule_selection.rowCount()
for i in range(nb_rules):
if self.table_blk_rule_selection.item(i, 0).checkState():
rule_conf = RuleConfiguration(name=self.table_blk_rule_selection.item(i, 1).text(),
is_activated=self.table_blk_rule_selection.item(i, 0).checkState() == QtCore.Qt.Checked,
config=json.loads(self.table_blk_rule_selection.item(i, 3).text()))
activated_rule_names.append(rule_conf)
# activated_rule_names.append(self.table_blk_rule_selection.item(i, 1).text())
return activated_rule_names
class D810ConfigForm_t(ida_kernwin.PluginForm):
def __init__(self, state):
super().__init__()
self.state = state
self.shown = False
self.created = False
self.parent = None
def OnClose(self, form):
logger.debug("Calling OnClose")
self.shown = False
# self.parent.close()
def Show(self):
logger.debug("Calling Show")
if self.shown:
return
self.shown = True
return ida_kernwin.PluginForm.Show(
self, "D-810 Configuration",
options=(ida_kernwin.PluginForm.WOPN_PERSIST |
ida_kernwin.PluginForm.WCLS_SAVE |
ida_kernwin.PluginForm.WOPN_MENU |
ida_kernwin.PluginForm.WOPN_RESTORE |
ida_kernwin.PluginForm.WOPN_TAB))
def OnCreate(self, form):
logger.debug("Calling OnCreate")
self.created = True
# Get parent widget
self.parent = self.FormToPyQtWidget(form)
layout = QtWidgets.QGridLayout(self.parent)
# ----------- Config options -----------------------
# Horizontal splitter for config boxes
cfg_split = QtWidgets.QSplitter(self.parent)
layout.addWidget(cfg_split, 0, 0)
# Config name label
self.curlabel = QtWidgets.QLabel('Current file loaded:')
cfg_split.addWidget(self.curlabel)
self.cfg_select = QtWidgets.QComboBox(self.parent)
cfg_split.addWidget(self.cfg_select)
self.btn_new_cfg = QtWidgets.QPushButton('New')
self.btn_new_cfg.clicked.connect(self._create_config)
cfg_split.addWidget(self.btn_new_cfg)
self.btn_duplicate_cfg = QtWidgets.QPushButton('Duplicate')
self.btn_duplicate_cfg.clicked.connect(self._duplicate_config)
cfg_split.addWidget(self.btn_duplicate_cfg)
self.btn_edit_cfg = QtWidgets.QPushButton('Edit')
self.btn_edit_cfg.clicked.connect(self._edit_config)
cfg_split.addWidget(self.btn_edit_cfg)
self.btn_delele_cfg = QtWidgets.QPushButton('Delete')
self.btn_delele_cfg.clicked.connect(self._delete_config)
cfg_split.addWidget(self.btn_delele_cfg)
# leave space for comboboxes in cfg_split, rather than between widgets
cfg_split.setStretchFactor(0, 0)
cfg_split.setStretchFactor(1, 1)
cfg_split.setStretchFactor(2, 0)
description_split = QtWidgets.QSplitter(self.parent)
layout.addWidget(description_split, 1, 0)
self.cfg_description_layout = QtWidgets.QHBoxLayout(description_split)
self.cfg_description_label = QtWidgets.QLabel("Description")
description_split.addWidget(self.cfg_description_label)
self.cfg_description = QtWidgets.QLabel("No description")
description_split.addWidget(self.cfg_description)
description_split.setStretchFactor(0, 0)
description_split.setStretchFactor(1, 1)
self.cfg_ins_preview = QtWidgets.QTableWidget(self.parent)
layout.addWidget(self.cfg_ins_preview, 2, 0)
self.cfg_blk_preview = QtWidgets.QTableWidget(self.parent)
layout.addWidget(self.cfg_blk_preview, 3, 0)
self.update_cfg_preview()
# ----------- Analysis buttons -----------------------
# Horizontal splitter for buttons
btn_split = QtWidgets.QSplitter(self.parent)
layout.addWidget(btn_split, 4, 0)
self.btn_config = QtWidgets.QPushButton('Configuration')
self.btn_config.clicked.connect(self._configure_plugin)
btn_split.addWidget(self.btn_config)
self.btn_start = QtWidgets.QPushButton('Start')
self.btn_start.clicked.connect(self._start_d810)
btn_split.addWidget(self.btn_start)
self.btn_stop = QtWidgets.QPushButton('Stop')
self.btn_stop.clicked.connect(self._stop_d810)
btn_split.addWidget(self.btn_stop)
self.plugin_status = QtWidgets.QLabel()
self.plugin_status.setText("<span style=\" font-size:8pt; font-weight:600; color:#ff0000;\" >Not Loaded</span>")
description_split.addWidget(self.plugin_status)
btn_split.addWidget(self.plugin_status)
self.update_cfg_select()
self.cfg_select.setCurrentIndex(self.state.current_project_index)
self.cfg_select.currentIndexChanged.connect(self._load_config)
def update_cfg_preview(self):
logger.debug("Calling update_cfg_preview")
self.update_cfg_ins_preview()
self.update_cfg_blk_preview()
def update_cfg_ins_preview(self):
# return
logger.debug("Calling update_cfg_ins_preview")
self.cfg_ins_preview.setRowCount(len(self.state.current_ins_rules))
self.cfg_ins_preview.setColumnCount(3)
self.cfg_ins_preview.setHorizontalHeaderLabels(("Name", "Description", "Configuration"))
self.cfg_ins_preview.horizontalHeader().setStretchLastSection(True)
self.cfg_ins_preview.setSortingEnabled(True)
# self.cfg_ins_preview.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
i = 0
for rule in self.state.current_ins_rules:
cell_file_path = QtWidgets.QTableWidgetItem(rule.name)
cell_file_path.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
cell_rule_description = QtWidgets.QTableWidgetItem(rule.description)
cell_rule_description.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
cell_rule_config = QtWidgets.QTableWidgetItem(json.dumps(rule.config))
cell_rule_config.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.cfg_ins_preview.setItem(i, 0, cell_file_path)
self.cfg_ins_preview.setItem(i, 1, cell_rule_description)
self.cfg_ins_preview.setItem(i, 2, cell_rule_config)
i += 1
self.cfg_ins_preview.resizeColumnsToContents()
def update_cfg_blk_preview(self):
logger.debug("Calling update_cfg_blk_preview")
self.cfg_blk_preview.setRowCount(len(self.state.current_blk_rules))
self.cfg_blk_preview.setColumnCount(3)
self.cfg_blk_preview.setHorizontalHeaderLabels(("Name", "Description", "Configuration"))
self.cfg_blk_preview.horizontalHeader().setStretchLastSection(True)
self.cfg_blk_preview.setSortingEnabled(True)
# self.cfg_blk_preview.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
i = 0
for rule in self.state.current_blk_rules:
cell_file_path = QtWidgets.QTableWidgetItem(rule.name)
cell_file_path.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
cell_rule_description = QtWidgets.QTableWidgetItem(rule.description)
cell_rule_description.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
cell_rule_config = QtWidgets.QTableWidgetItem(json.dumps(rule.config))
cell_rule_config.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.cfg_blk_preview.setItem(i, 0, cell_file_path)
self.cfg_blk_preview.setItem(i, 1, cell_rule_description)
self.cfg_blk_preview.setItem(i, 2, cell_rule_config)
i += 1
self.cfg_blk_preview.resizeColumnsToContents()
def update_cfg_select(self):
logger.debug("Calling update_cfg_select")
tmp = self.state.current_project_index
self.cfg_select.clear()
self.cfg_select.addItems([proj.path for proj in self.state.projects])
self.cfg_select.setCurrentIndex(tmp)
def _create_config(self):
logger.debug("Calling _create_config")
self._internal_config_creation(None, None, None, self.state.d810_config.config_dir)
def _duplicate_config(self):
logger.debug("Calling _duplicate_config")
cur_cfg = self.state.current_project
self._internal_config_creation(None, cur_cfg.ins_rules, cur_cfg.blk_rules, self.state.d810_config.config_dir)
def _edit_config(self):
logger.debug("Calling _edit_config")
cur_cfg = self.state.current_project
self._internal_config_creation(cur_cfg.description, cur_cfg.ins_rules, cur_cfg.blk_rules, cur_cfg.path, cur_cfg)
def _internal_config_creation(self, description, start_ins_rules, start_blk_rules, path, old_conf=None):
logger.debug("Calling _internal_config_creation")
editdlg = EditConfigurationFileForm_t(self.parent, self.state)
editdlg.update_form(description, start_ins_rules, start_blk_rules, path)
if editdlg.exec_() == QtWidgets.QDialog.Accepted:
new_config = ProjectConfiguration(editdlg.config_path, editdlg.config_description, editdlg.config_ins_rules, editdlg.config_blk_rules)
new_config.save()
if old_conf is None:
self.state.add_project(new_config)
else:
self.state.update_project(old_conf, new_config)
self.update_cfg_select()
return new_config
return None
# callback when the "Delete" button is clicked
def _delete_config(self):
logger.debug("Calling _delete_config")
self.state.del_project(self.state.current_project)
self.update_cfg_select()
# Called when the edit combo is changed
def _load_config(self, index):
logger.debug("Calling _load_config")
self.state.load_project(index)
self.cfg_description.setText(self.state.current_project.description)
self.update_cfg_preview()
return
def _configure_plugin(self):
editdlg = PluginConfigurationFileForm_t(self.parent, self.state)
if editdlg.exec_() == QtWidgets.QDialog.Accepted:
return
return
def _start_d810(self):
logger.debug("Calling _start_d810")
self.state.start_d810()
# self.plugin_status.clear()
self.plugin_status.setText("<span style=\" font-size:8pt; font-weight:600; color:#00FF00;\" >Loaded</span>")
return
def _stop_d810(self):
logger.debug("Calling _stop_d810")
self.state.stop_d810()
# self.plugin_status.clear()
self.plugin_status.setText("<span style=\" font-size:8pt; font-weight:600; color:#FF0000;\" >Not Loaded</span>")
return
class D810GUI(object):
def __init__(self, state):
"""
Instanciate D-810 views
"""
logger.debug("Initializing D810GUI")
self.state = state
self.d810_config_form = D810ConfigForm_t(self.state)
# XXX fix
idaapi.set_dock_pos("D-810", "IDA View-A", idaapi.DP_TAB)
def show_windows(self):
logger.debug("Calling show_windows")
self.d810_config_form.Show()
def term(self):
logger.debug("Calling term")
self.d810_config_form.Close(ida_kernwin.PluginForm.WCLS_SAVE)

102
d810/log.ini Normal file
View File

@@ -0,0 +1,102 @@
[loggers]
keys=root,D810,D810Ui,D810Optimizer,D810RulesChain,D810PatternSearch,D810BranchFixer,D810Unflat,D810Tracker,D810Emulator,D810Helper,D810Z3Test
[handlers]
keys=consoleHandler,defaultFileHandler,z3FileHandler
[formatters]
keys=defaultFormatter,rawFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_D810]
level=DEBUG
handlers=consoleHandler,defaultFileHandler
qualname=D810
propagate=0
[logger_D810Ui]
level=ERROR
handlers=defaultFileHandler
qualname=D810.ui
propagate=0
[logger_D810Optimizer]
level=INFO
handlers=defaultFileHandler
qualname=D810.optimizer
propagate=0
[logger_D810RulesChain]
level=INFO
handlers=defaultFileHandler
qualname=D810.chain
propagate=0
[logger_D810BranchFixer]
level=INFO
handlers=defaultFileHandler
qualname=D810.branch_fixer
propagate=0
[logger_D810Unflat]
level=INFO
handlers=defaultFileHandler
qualname=D810.unflat
propagate=0
[logger_D810Tracker]
level=INFO
handlers=defaultFileHandler
qualname=D810.tracker
propagate=0
[logger_D810Emulator]
level=INFO
handlers=defaultFileHandler
qualname=D810.emulator
propagate=0
[logger_D810Helper]
level=INFO
handlers=defaultFileHandler
qualname=D810.helper
propagate=0
[logger_D810PatternSearch]
level=ERROR
handlers=defaultFileHandler
qualname=D810.pattern_search
propagate=0
[logger_D810Z3Test]
level=INFO
handlers=z3FileHandler
qualname=D810.z3_test
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=defaultFormatter
args=(sys.stdout,)
[handler_defaultFileHandler]
class=FileHandler
level=DEBUG
formatter=defaultFormatter
args=('%(default_log_filename)s',)
[handler_z3FileHandler]
class=FileHandler
level=DEBUG
formatter=rawFormatter
args=('%(z3_log_filename)s',)
[formatter_defaultFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
[formatter_rawFormatter]
format=%(message)s

23
d810/log.py Normal file
View File

@@ -0,0 +1,23 @@
import os
import shutil
import logging
import logging.config
LOG_CONFIG_FILENAME = "log.ini"""
LOG_FILENAME = "d810.log"
Z3_TEST_FILENAME = "z3_check_instructions_substitution.py"
def clear_logs(log_dir):
shutil.rmtree(log_dir, ignore_errors=True)
def configure_loggers(log_dir):
os.makedirs(log_dir, exist_ok=True)
log_main_file = os.path.join(log_dir, LOG_FILENAME)
z3_test_file = os.path.join(log_dir, Z3_TEST_FILENAME)
log_conf_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), LOG_CONFIG_FILENAME)
logging.config.fileConfig(log_conf_file, defaults={"default_log_filename": log_main_file,
"z3_log_filename": z3_test_file})
z3_file_logger = logging.getLogger('D810.z3_test')
z3_file_logger.info("from z3 import BitVec, BitVecVal, UDiv, URem, LShR, UGT, UGE, ULT, ULE, prove\n\n")

196
d810/manager.py Normal file
View File

@@ -0,0 +1,196 @@
from __future__ import annotations
import os
import json
import logging
import idaapi
from typing import TYPE_CHECKING, List
if TYPE_CHECKING:
from d810.conf import D810Configuration, ProjectConfiguration
# Note that imports are performed directly in the functions so that they are reloaded each time the plugin is restarted
# This allow to load change code/drop new rules without having to reboot IDA
d810_state = None
D810_LOG_DIR_NAME = "d810_logs"
MANAGER_INFO_FILENAME = "manager_info.json"
logger = logging.getLogger('D810')
def reload_all_modules():
manager_info_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), MANAGER_INFO_FILENAME)
with open(manager_info_path, "r") as f:
manager_info = json.load(f)
for module_name in manager_info["module_list"]:
idaapi.require(module_name)
class D810Manager(object):
def __init__(self, log_dir):
self.instruction_optimizer_rules = []
self.instruction_optimizer_config = {}
self.block_optimizer_rules = []
self.block_optimizer_config = {}
self.instruction_optimizer = None
self.block_optimizer = None
self.hx_decompiler_hook = None
self.log_dir = log_dir
self.config = {}
def configure(self, **kwargs):
self.config = kwargs
def reload(self):
self.stop()
logger.debug("Reloading manager...")
from d810.hexrays_hooks import InstructionOptimizerManager, BlockOptimizerManager, HexraysDecompilationHook
self.instruction_optimizer = InstructionOptimizerManager(self)
self.instruction_optimizer.configure(**self.instruction_optimizer_config)
self.block_optimizer = BlockOptimizerManager(self)
self.block_optimizer.configure(**self.block_optimizer_config)
for rule in self.instruction_optimizer_rules:
rule.log_dir = self.log_dir
self.instruction_optimizer.add_rule(rule)
for cfg_rule in self.block_optimizer_rules:
cfg_rule.log_dir = self.log_dir
self.block_optimizer.add_rule(cfg_rule)
self.instruction_optimizer.install()
self.block_optimizer.install()
self.hx_decompiler_hook = HexraysDecompilationHook(self)
self.hx_decompiler_hook.hook()
def configure_instruction_optimizer(self, rules, **kwargs):
self.instruction_optimizer_rules = [rule for rule in rules]
self.instruction_optimizer_config = kwargs
def configure_block_optimizer(self, rules, **kwargs):
self.block_optimizer_rules = [rule for rule in rules]
self.block_optimizer_config = kwargs
def stop(self):
if self.instruction_optimizer is not None:
logger.debug("Removing InstructionOptimizer...")
self.instruction_optimizer.remove()
self.instruction_optimizer = None
if self.block_optimizer is not None:
logger.debug("Removing ControlFlowFixer...")
self.block_optimizer.remove()
self.block_optimizer = None
if self.hx_decompiler_hook is not None:
logger.debug("Removing HexraysDecompilationHook...")
self.hx_decompiler_hook.unhook()
self.hx_decompiler_hook = None
class D810State(object):
def __init__(self, d810_config: D810Configuration):
# For debugging purposes, to interact with this object from the console
# Type in IDA Python shell 'from d810.manager import d810_state' to access it
global d810_state
d810_state = self
reload_all_modules()
self.d810_config = d810_config
self.log_dir = os.path.join(self.d810_config.get("log_dir"), D810_LOG_DIR_NAME)
self.manager = D810Manager(self.log_dir)
from d810.optimizers.instructions import KNOWN_INS_RULES
from d810.optimizers.flow import KNOWN_BLK_RULES
self.known_ins_rules = [x for x in KNOWN_INS_RULES]
self.known_blk_rules = [x for x in KNOWN_BLK_RULES]
self.gui = None
self.current_project = None
self.projects: List[ProjectConfiguration] = []
self.current_project_index = self.d810_config.get("last_project_index")
self.current_ins_rules = []
self.current_blk_rules = []
self.register_default_projects()
self.load_project(self.current_project_index)
def register_default_projects(self):
from d810.conf import ProjectConfiguration
self.projects = []
for project_configuration_path in self.d810_config.get("configurations"):
project_configuration = ProjectConfiguration(project_configuration_path,
conf_dir=self.d810_config.config_dir)
project_configuration.load()
self.projects.append(project_configuration)
logger.debug("Rule configurations loaded: {0}".format(self.projects))
def add_project(self, config: ProjectConfiguration):
self.projects.append(config)
self.d810_config.get("configurations").append(config.path)
self.d810_config.save()
def update_project(self, old_config: ProjectConfiguration, new_config: ProjectConfiguration):
old_config_index = self.projects.index(old_config)
self.projects[old_config_index] = new_config
def del_project(self, config: ProjectConfiguration):
self.projects.remove(config)
self.d810_config.get("configurations").remove(config.path)
self.d810_config.save()
os.remove(config.path)
def load_project(self, project_index: int):
self.current_project_index = project_index
self.current_project = self.projects[project_index]
self.current_ins_rules = []
self.current_blk_rules = []
for rule in self.known_ins_rules:
for rule_conf in self.current_project.ins_rules:
if rule.name == rule_conf.name:
rule.configure(rule_conf.config)
rule.set_log_dir(self.log_dir)
self.current_ins_rules.append(rule)
logger.debug("Instruction rules configured")
for blk_rule in self.known_blk_rules:
for rule_conf in self.current_project.blk_rules:
if blk_rule.name == rule_conf.name:
blk_rule.configure(rule_conf.config)
blk_rule.set_log_dir(self.log_dir)
self.current_blk_rules.append(blk_rule)
logger.debug("Block rules configured")
self.manager.configure(**self.current_project.additional_configuration)
logger.debug("Project loaded.")
def start_d810(self):
print("D-810 ready to deobfuscate...")
self.manager.configure_instruction_optimizer([rule for rule in self.current_ins_rules],
generate_z3_code=self.d810_config.get("generate_z3_code"),
dump_intermediate_microcode=self.d810_config.get(
"dump_intermediate_microcode"),
**self.current_project.additional_configuration)
self.manager.configure_block_optimizer([rule for rule in self.current_blk_rules],
**self.current_project.additional_configuration)
self.manager.reload()
self.d810_config.set("last_project_index", self.current_project_index)
self.d810_config.save()
def stop_d810(self):
print("Stopping D-810...")
self.manager.stop()
def start_plugin(self):
from d810.ida_ui import D810GUI
self.gui = D810GUI(self)
self.gui.show_windows()
def stop_plugin(self):
self.manager.stop()
if self.gui:
self.gui.term()
self.gui = None

60
d810/manager_info.json Normal file
View File

@@ -0,0 +1,60 @@
{
"_comment": "Order of module in module list matters",
"module_list": [
"d810.cfg_utils",
"d810.emulator",
"d810.ast",
"d810.optimizers.handler",
"d810.optimizers.instructions.handler",
"d810.optimizers.instructions.pattern_matching.handler",
"d810.optimizers.instructions.pattern_matching.rewrite_add",
"d810.optimizers.instructions.pattern_matching.rewrite_and",
"d810.optimizers.instructions.pattern_matching.rewrite_bnot",
"d810.optimizers.instructions.pattern_matching.rewrite_cst",
"d810.optimizers.instructions.pattern_matching.rewrite_mov",
"d810.optimizers.instructions.pattern_matching.rewrite_mul",
"d810.optimizers.instructions.pattern_matching.rewrite_neg",
"d810.optimizers.instructions.pattern_matching.rewrite_or",
"d810.optimizers.instructions.pattern_matching.rewrite_predicates",
"d810.optimizers.instructions.pattern_matching.rewrite_sub",
"d810.optimizers.instructions.pattern_matching.rewrite_xor",
"d810.optimizers.instructions.pattern_matching.weird",
"d810.optimizers.instructions.pattern_matching",
"d810.optimizers.instructions.chain.handler",
"d810.optimizers.instructions.chain.chain_rules",
"d810.optimizers.instructions.chain",
"d810.optimizers.instructions.z3.handler",
"d810.optimizers.instructions.z3.cst",
"d810.optimizers.instructions.z3.predicates",
"d810.optimizers.instructions.z3",
"d810.optimizers.instructions.analysis.utils",
"d810.optimizers.instructions.analysis.handler",
"d810.optimizers.instructions.analysis.pattern_guess",
"d810.optimizers.instructions.analysis",
"d810.optimizers.instructions.early.handler",
"d810.optimizers.instructions.early.mem_read",
"d810.optimizers.instructions.early",
"d810.optimizers.instructions",
"d810.optimizers.flow.handler",
"d810.optimizers.flow.jumps.handler",
"d810.optimizers.flow.jumps.opaque",
"d810.optimizers.flow.jumps.tricks",
"d810.optimizers.flow.jumps",
"d810.optimizers.flow.flattening.utils",
"d810.optimizers.flow.flattening.generic",
"d810.optimizers.flow.flattening.unflattener",
"d810.optimizers.flow.flattening.unflattener_fake_jump",
"d810.optimizers.flow.flattening.unflattener_switch_case",
"d810.optimizers.flow.flattening.unflattener_indirect",
"d810.optimizers.flow.flattening",
"d810.optimizers.flow",
"d810.hexrays_helpers",
"d810.hexrays_formatters",
"d810.hexrays_hooks",
"d810.ida_ui",
"d810.log",
"d810.tracker",
"d810.utils",
"d810.z3_utils"
]
}

View File

View File

@@ -0,0 +1,5 @@
from d810.optimizers.flow.flattening import UNFLATTENING_BLK_RULES
from d810.optimizers.flow.jumps import JUMP_OPTIMIZATION_BLOCK_RULES, JUMP_OPTIMIZATION_RULES
KNOWN_BLK_RULES = UNFLATTENING_BLK_RULES + JUMP_OPTIMIZATION_BLOCK_RULES

View File

@@ -0,0 +1,6 @@
from d810.optimizers.flow.flattening.unflattener import Unflattener
from d810.optimizers.flow.flattening.unflattener_switch_case import UnflattenerSwitchCase
from d810.optimizers.flow.flattening.unflattener_indirect import UnflattenerTigressIndirect
from d810.optimizers.flow.flattening.unflattener_fake_jump import UnflattenerFakeJump
UNFLATTENING_BLK_RULES = [Unflattener(), UnflattenerSwitchCase(), UnflattenerTigressIndirect(), UnflattenerFakeJump()]

View File

@@ -0,0 +1,469 @@
from __future__ import annotations
import logging
from typing import List, Union, Tuple
from ida_hexrays import *
from d810.optimizers.flow.handler import FlowOptimizationRule
from d810.tracker import MopTracker, MopHistory, remove_segment_registers, duplicate_histories
from d810.emulator import MicroCodeEnvironment, MicroCodeInterpreter
from d810.hexrays_hooks import InstructionDefUseCollector
from d810.hexrays_helpers import extract_num_mop, get_mop_index, append_mop_if_not_in_list, CONTROL_FLOW_OPCODES, \
CONDITIONAL_JUMP_OPCODES
from d810.hexrays_formatters import format_minsn_t, format_mop_t, dump_microcode_for_debug, format_mop_list
from d810.cfg_utils import mba_deep_cleaning, ensure_child_has_an_unconditional_father, ensure_last_block_is_goto, \
change_1way_block_successor, create_block
from d810.optimizers.flow.flattening.utils import NotResolvableFatherException, NotDuplicableFatherException, \
DispatcherUnflatteningException, get_all_possibles_values, check_if_all_values_are_found
unflat_logger = logging.getLogger('D810.unflat')
class GenericDispatcherBlockInfo(object):
def __init__(self, blk, father=None):
self.blk = blk
self.ins = []
self.use_list = []
self.use_before_def_list = []
self.def_list = []
self.assume_def_list = []
self.comparison_value = None
self.compared_mop = None
self.father = None
if father is not None:
self.register_father(father)
@property
def serial(self) -> int:
return self.blk.serial
def register_father(self, father: GenericDispatcherBlockInfo):
self.father = father
self.assume_def_list = [x for x in father.assume_def_list]
def update_use_def_lists(self, ins_mops_used: List[mop_t], ins_mops_def: List[mop_t]):
for mop_used in ins_mops_used:
append_mop_if_not_in_list(mop_used, self.use_list)
mop_used_index = get_mop_index(mop_used, self.def_list)
if mop_used_index == -1:
append_mop_if_not_in_list(mop_used, self.use_before_def_list)
for mop_def in ins_mops_def:
append_mop_if_not_in_list(mop_def, self.def_list)
def update_with_ins(self, ins: minsn_t):
ins_mop_info = InstructionDefUseCollector()
ins.for_all_ops(ins_mop_info)
cleaned_unresolved_ins_mops = remove_segment_registers(ins_mop_info.unresolved_ins_mops)
self.update_use_def_lists(cleaned_unresolved_ins_mops + ins_mop_info.memory_unresolved_ins_mops,
ins_mop_info.target_mops)
self.ins.append(ins)
if ins.opcode in CONDITIONAL_JUMP_OPCODES:
num_mop, other_mop = extract_num_mop(ins)
if num_mop is not None:
self.comparison_value = num_mop.nnn.value
self.compared_mop = other_mop
def parse(self):
curins = self.blk.head
while curins is not None:
self.update_with_ins(curins)
curins = curins.next
for mop_def in self.def_list:
append_mop_if_not_in_list(mop_def, self.assume_def_list)
def does_only_need(self, prerequisite_mop_list: List[mop_t]) -> bool:
for used_before_def_mop in self.use_before_def_list:
mop_index = get_mop_index(used_before_def_mop, prerequisite_mop_list)
if mop_index == -1:
return False
return True
def recursive_get_father(self) -> List[GenericDispatcherBlockInfo]:
if self.father is None:
return [self]
else:
return self.father.recursive_get_father() + [self]
def show_history(self):
full_father_list = self.recursive_get_father()
unflat_logger.info(" Show history of Block {0}".format(self.blk.serial))
for father in full_father_list[:-1]:
for ins in father.ins:
unflat_logger.info(" {0}.{1}".format(father.blk.serial, format_minsn_t(ins)))
def print_info(self):
unflat_logger.info("Block {0} information:".format(self.blk.serial))
unflat_logger.info(" USE list: {0}".format(format_mop_list(self.use_list)))
unflat_logger.info(" DEF list: {0}".format(format_mop_list(self.def_list)))
unflat_logger.info(" USE BEFORE DEF list: {0}".format(format_mop_list(self.use_before_def_list)))
unflat_logger.info(" ASSUME DEF list: {0}".format(format_mop_list(self.assume_def_list)))
class GenericDispatcherInfo(object):
def __init__(self, mba: mbl_array_t):
self.mba = mba
self.mop_compared = None
self.entry_block = None
self.comparison_values = []
self.dispatcher_internal_blocks = []
self.dispatcher_exit_blocks = []
def reset(self):
self.mop_compared = None
self.entry_block = None
self.comparison_values = []
self.dispatcher_internal_blocks = []
self.dispatcher_exit_blocks = []
def explore(self, blk: mblock_t) -> bool:
return False
def get_shared_internal_blocks(self, other_dispatcher: GenericDispatcherInfo) -> List[mblock_t]:
my_dispatcher_block_serial = [blk_info.blk.serial for blk_info in self.dispatcher_internal_blocks]
other_dispatcher_block_serial = [blk_info.blk.serial
for blk_info in other_dispatcher.dispatcher_internal_blocks]
return [self.mba.get_mblock(blk_serial) for blk_serial in my_dispatcher_block_serial
if blk_serial in other_dispatcher_block_serial]
def is_sub_dispatcher(self, other_dispatcher: GenericDispatcherInfo) -> bool:
shared_blocks = self.get_shared_internal_blocks(other_dispatcher)
if (len(shared_blocks) > 0) and (self.entry_block.blk.npred() < other_dispatcher.entry_block.blk.npred()):
return True
return False
def should_emulation_continue(self, cur_blk: mblock_t) -> bool:
exit_block_serial_list = [exit_block.serial for exit_block in self.dispatcher_exit_blocks]
if (cur_blk is not None) and (cur_blk.serial not in exit_block_serial_list):
return True
return False
def emulate_dispatcher_with_father_history(self, father_history: MopHistory) -> Tuple[mblock_t, List[minsn_t]]:
microcode_interpreter = MicroCodeInterpreter()
microcode_environment = MicroCodeEnvironment()
dispatcher_input_info = []
for initialization_mop in self.entry_block.use_before_def_list:
initialization_mop_value = father_history.get_mop_constant_value(initialization_mop)
if initialization_mop_value is None:
raise NotResolvableFatherException("Can't emulate dispatcher {0} with history {1}"
.format(self.entry_block.serial, father_history.block_serial_path))
microcode_environment.define(initialization_mop, initialization_mop_value)
dispatcher_input_info.append("{0} = {1:x}".format(format_mop_t(initialization_mop),
initialization_mop_value))
unflat_logger.info("Executing dispatcher {0} with: {1}"
.format(self.entry_block.blk.serial, ", ".join(dispatcher_input_info)))
instructions_executed = []
cur_blk = self.entry_block.blk
cur_ins = cur_blk.head
while self.should_emulation_continue(cur_blk):
unflat_logger.debug(" Executing: {0}.{1}".format(cur_blk.serial, format_minsn_t(cur_ins)))
is_ok = microcode_interpreter.eval_instruction(cur_blk, cur_ins, microcode_environment)
if not is_ok:
return cur_blk, instructions_executed
instructions_executed.append(cur_ins)
cur_blk = microcode_environment.next_blk
cur_ins = microcode_environment.next_ins
return cur_blk, instructions_executed
def print_info(self, verbose=False):
unflat_logger.info("Dispatcher information: ")
unflat_logger.info(" Entry block: {0}.{1}: ".format(self.entry_block.blk.serial,
format_minsn_t(self.entry_block.blk.tail)))
unflat_logger.info(" Entry block predecessors: {0}: "
.format([blk_serial for blk_serial in self.entry_block.blk.predset]))
unflat_logger.info(" Compared mop: {0} ".format(format_mop_t(self.mop_compared)))
unflat_logger.info(" Comparison values: {0} ".format(", ".join([hex(x) for x in self.comparison_values])))
self.entry_block.print_info()
unflat_logger.info(" Number of internal blocks: {0} ({1})"
.format(len(self.dispatcher_internal_blocks),
[blk_info.blk.serial for blk_info in self.dispatcher_internal_blocks]))
if verbose:
for disp_blk in self.dispatcher_internal_blocks:
unflat_logger.info(" Internal block: {0}.{1} ".format(disp_blk.blk.serial,
format_minsn_t(disp_blk.blk.tail)))
disp_blk.show_history()
unflat_logger.info(" Number of Exit blocks: {0} ({1})"
.format(len(self.dispatcher_exit_blocks),
[blk_info.blk.serial for blk_info in self.dispatcher_exit_blocks]))
if verbose:
for exit_blk in self.dispatcher_exit_blocks:
unflat_logger.info(" Exit block: {0}.{1} ".format(exit_blk.blk.serial,
format_minsn_t(exit_blk.blk.head)))
exit_blk.show_history()
class GenericDispatcherCollector(minsn_visitor_t):
DISPATCHER_CLASS = GenericDispatcherInfo
DEFAULT_DISPATCHER_MIN_INTERNAL_BLOCK = 2
DEFAULT_DISPATCHER_MIN_EXIT_BLOCK = 2
DEFAULT_DISPATCHER_MIN_COMPARISON_VALUE = 2
def __init__(self):
super().__init__()
self.dispatcher_list = []
self.explored_blk_serials = []
self.dispatcher_min_internal_block = self.DEFAULT_DISPATCHER_MIN_INTERNAL_BLOCK
self.dispatcher_min_exit_block = self.DEFAULT_DISPATCHER_MIN_EXIT_BLOCK
self.dispatcher_min_comparison_value = self.DEFAULT_DISPATCHER_MIN_COMPARISON_VALUE
def configure(self, kwargs):
if "min_dispatcher_internal_block" in kwargs.keys():
self.dispatcher_min_internal_block = kwargs["min_dispatcher_internal_block"]
if "min_dispatcher_exit_block" in kwargs.keys():
self.dispatcher_min_exit_block = kwargs["min_dispatcher_exit_block"]
if "min_dispatcher_comparison_value" in kwargs.keys():
self.dispatcher_min_comparison_value = kwargs["min_dispatcher_comparison_value"]
def specific_checks(self, disp_info: GenericDispatcherInfo) -> bool:
unflat_logger.debug("DispatcherInfo {0} : {1} internals, {2} exits, {3} comparison"
.format(self.blk.serial, len(disp_info.dispatcher_internal_blocks),
len(disp_info.dispatcher_exit_blocks), len(set(disp_info.comparison_values))))
if len(disp_info.dispatcher_internal_blocks) < self.dispatcher_min_internal_block:
return False
if len(disp_info.dispatcher_exit_blocks) < self.dispatcher_min_exit_block:
return False
if len(set(disp_info.comparison_values)) < self.dispatcher_min_comparison_value:
return False
self.dispatcher_list.append(disp_info)
return True
def visit_minsn(self):
if self.blk.serial in self.explored_blk_serials:
return 0
self.explored_blk_serials.append(self.blk.serial)
disp_info = self.DISPATCHER_CLASS(self.blk.mba)
is_good_candidate = disp_info.explore(self.blk)
if not is_good_candidate:
return 0
if not self.specific_checks(disp_info):
return 0
self.dispatcher_list.append(disp_info)
return 0
def remove_sub_dispatchers(self):
main_dispatcher_list = []
for dispatcher_1 in self.dispatcher_list:
is_dispatcher_1_sub_dispatcher = False
for dispatcher_2 in self.dispatcher_list:
if dispatcher_1.is_sub_dispatcher(dispatcher_2):
is_dispatcher_1_sub_dispatcher = True
break
if not is_dispatcher_1_sub_dispatcher:
main_dispatcher_list.append(dispatcher_1)
self.dispatcher_list = [x for x in main_dispatcher_list]
def reset(self):
self.dispatcher_list = []
self.explored_blk_serials = []
def get_dispatcher_list(self) -> List[GenericDispatcherInfo]:
self.remove_sub_dispatchers()
return self.dispatcher_list
class GenericUnflatteningRule(FlowOptimizationRule):
DEFAULT_UNFLATTENING_MATURITIES = [MMAT_CALLS, MMAT_GLBOPT1, MMAT_GLBOPT2]
def __init__(self):
super().__init__()
self.mba = None
self.cur_maturity = MMAT_ZERO
self.cur_maturity_pass = 0
self.last_pass_nb_patch_done = 0
self.maturities = self.DEFAULT_UNFLATTENING_MATURITIES
def check_if_rule_should_be_used(self, blk: mblock_t) -> bool:
if self.cur_maturity == self.mba.maturity:
self.cur_maturity_pass += 1
else:
self.cur_maturity = self.mba.maturity
self.cur_maturity_pass = 0
if self.cur_maturity not in self.maturities:
return False
return True
class GenericDispatcherUnflatteningRule(GenericUnflatteningRule):
DISPATCHER_COLLECTOR_CLASS = GenericDispatcherCollector
MOP_TRACKER_MAX_NB_BLOCK = 100
MOP_TRACKER_MAX_NB_PATH = 100
DEFAULT_MAX_DUPLICATION_PASSES = 20
DEFAULT_MAX_PASSES = 5
def __init__(self):
super().__init__()
self.dispatcher_collector = self.DISPATCHER_COLLECTOR_CLASS()
self.dispatcher_list = []
self.max_duplication_passes = self.DEFAULT_MAX_DUPLICATION_PASSES
self.max_passes = self.DEFAULT_MAX_PASSES
def check_if_rule_should_be_used(self, blk: mblock_t) -> bool:
if not super().check_if_rule_should_be_used(blk):
return False
if (self.cur_maturity_pass >= 1) and (self.last_pass_nb_patch_done == 0):
return False
if (self.max_passes is not None) and (self.cur_maturity_pass >= self.max_passes):
return False
return True
def configure(self, kwargs):
super().configure(kwargs)
if "max_passes" in self.config.keys():
self.max_passes = self.config["max_passes"]
if "max_duplication_passes" in self.config.keys():
self.max_duplication_passes = self.config["max_duplication_passes"]
self.dispatcher_collector.configure(kwargs)
def retrieve_all_dispatchers(self):
self.dispatcher_list = []
self.dispatcher_collector.reset()
self.mba.for_all_topinsns(self.dispatcher_collector)
self.dispatcher_list = [x for x in self.dispatcher_collector.get_dispatcher_list()]
def ensure_all_dispatcher_fathers_are_direct(self) -> int:
nb_change = 0
for dispatcher_info in self.dispatcher_list:
dispatcher_father_list = [self.mba.get_mblock(x) for x in dispatcher_info.entry_block.blk.predset]
for dispatcher_father in dispatcher_father_list:
nb_change += ensure_child_has_an_unconditional_father(dispatcher_father,
dispatcher_info.entry_block.blk)
return nb_change
def register_initialization_variables(self, mop_tracker):
pass
def get_dispatcher_father_histories(self, dispatcher_father: mblock_t,
dispatcher_entry_block: GenericDispatcherBlockInfo) -> List[MopHistory]:
father_tracker = MopTracker(dispatcher_entry_block.use_before_def_list,
max_nb_block=self.MOP_TRACKER_MAX_NB_BLOCK, max_path=self.MOP_TRACKER_MAX_NB_PATH)
father_tracker.reset()
self.register_initialization_variables(father_tracker)
father_histories = father_tracker.search_backward(dispatcher_father, None)
return father_histories
def check_if_histories_are_resolved(self, mop_histories: List[MopHistory]) -> bool:
return all([mop_history.is_resolved() for mop_history in mop_histories])
def ensure_dispatcher_father_is_resolvable(self, dispatcher_father: mblock_t,
dispatcher_entry_block: GenericDispatcherBlockInfo) -> int:
father_histories = self.get_dispatcher_father_histories(dispatcher_father, dispatcher_entry_block)
father_histories_cst = get_all_possibles_values(father_histories, dispatcher_entry_block.use_before_def_list,
verbose=False)
father_is_resolvable = self.check_if_histories_are_resolved(father_histories)
if not father_is_resolvable:
raise NotDuplicableFatherException("Dispatcher {0} predecessor {1} is not duplicable: {2}"
.format(dispatcher_entry_block.serial, dispatcher_father.serial,
father_histories_cst))
unflat_logger.info("Dispatcher {0} predecessor {1} is resolvable: {2}"
.format(dispatcher_entry_block.serial, dispatcher_father.serial, father_histories_cst))
nb_duplication, nb_change = duplicate_histories(father_histories, max_nb_pass=self.max_duplication_passes)
unflat_logger.info("Dispatcher {0} predecessor {1} duplication: {2} blocks created, {3} changes made"
.format(dispatcher_entry_block.serial, dispatcher_father.serial, nb_duplication, nb_change))
return nb_duplication + nb_change
def resolve_dispatcher_father(self, dispatcher_father: mblock_t, dispatcher_info: GenericDispatcherInfo) -> int:
dispatcher_father_histories = self.get_dispatcher_father_histories(dispatcher_father,
dispatcher_info.entry_block)
father_is_resolvable = self.check_if_histories_are_resolved(dispatcher_father_histories)
if not father_is_resolvable:
raise NotResolvableFatherException("Can't fix block {0}".format(dispatcher_father.serial))
mop_searched_values_list = get_all_possibles_values(dispatcher_father_histories,
dispatcher_info.entry_block.use_before_def_list,
verbose=False)
all_values_found = check_if_all_values_are_found(mop_searched_values_list)
if not all_values_found:
raise NotResolvableFatherException("Can't fix block {0}".format(dispatcher_father.serial))
ref_mop_searched_values = mop_searched_values_list[0]
for tmp_mop_searched_values in mop_searched_values_list:
if tmp_mop_searched_values != ref_mop_searched_values:
raise NotResolvableFatherException("Dispatcher {0} predecessor {1} is not resolvable: {2}"
.format(dispatcher_info.entry_block.serial, dispatcher_father.serial,
mop_searched_values_list))
target_blk, disp_ins = dispatcher_info.emulate_dispatcher_with_father_history(dispatcher_father_histories[0])
if target_blk is not None:
unflat_logger.debug("Unflattening graph: Making {0} goto {1}"
.format(dispatcher_father.serial, target_blk.serial))
ins_to_copy = [ins for ins in disp_ins if ((ins is not None) and (ins.opcode not in CONTROL_FLOW_OPCODES))]
if len(ins_to_copy) > 0:
unflat_logger.info("Instruction copied: {0}: {1}"
.format(len(ins_to_copy),
", ".join([format_minsn_t(ins_copied) for ins_copied in ins_to_copy])))
dispatcher_side_effect_blk = create_block(self.mba.get_mblock(self.mba.qty - 2), ins_to_copy,
is_0_way=(target_blk.type == BLT_0WAY))
change_1way_block_successor(dispatcher_father, dispatcher_side_effect_blk.serial)
change_1way_block_successor(dispatcher_side_effect_blk, target_blk.serial)
else:
change_1way_block_successor(dispatcher_father, target_blk.serial)
return 2
raise NotResolvableFatherException("Can't fix block {0}: no block for key: {1}"
.format(dispatcher_father.serial, mop_searched_values_list))
def remove_flattening(self) -> int:
total_nb_change = ensure_last_block_is_goto(self.mba)
total_nb_change += self.ensure_all_dispatcher_fathers_are_direct()
nb_flattened_branches = 0
for dispatcher_info in self.dispatcher_list:
dump_microcode_for_debug(self.mba, self.log_dir, "unflat_{0}_dispatcher_{1}_before_duplication"
.format(self.cur_maturity_pass, dispatcher_info.entry_block.serial))
unflat_logger.info("Searching dispatcher for entry block {0} {1} -> with variables ({2})..."
.format(dispatcher_info.entry_block.serial, format_mop_t(dispatcher_info.mop_compared),
format_mop_list(dispatcher_info.entry_block.use_before_def_list)))
dispatcher_father_list = [self.mba.get_mblock(x) for x in dispatcher_info.entry_block.blk.predset]
for dispatcher_father in dispatcher_father_list:
try:
total_nb_change += self.ensure_dispatcher_father_is_resolvable(dispatcher_father,
dispatcher_info.entry_block)
except NotDuplicableFatherException as e:
unflat_logger.warning(e)
pass
dump_microcode_for_debug(self.mba, self.log_dir, "unflat_{0}_dispatcher_{1}_after_duplication"
.format(self.cur_maturity_pass, dispatcher_info.entry_block.serial))
# During the previous step we changed dispatcher entry block fathers, so we need to reload them
dispatcher_father_list = [self.mba.get_mblock(x) for x in dispatcher_info.entry_block.blk.predset]
nb_flattened_branches = 0
for dispatcher_father in dispatcher_father_list:
try:
nb_flattened_branches += self.resolve_dispatcher_father(dispatcher_father, dispatcher_info)
except NotResolvableFatherException as e:
unflat_logger.warning(e)
pass
dump_microcode_for_debug(self.mba, self.log_dir, "unflat_{0}_dispatcher_{1}_after_unflattening"
.format(self.cur_maturity_pass, dispatcher_info.entry_block.serial))
unflat_logger.info("Unflattening removed {0} branch".format(nb_flattened_branches))
total_nb_change += nb_flattened_branches
return total_nb_change
def optimize(self, blk: mblock_t) -> int:
self.mba = blk.mba
if not self.check_if_rule_should_be_used(blk):
return 0
self.last_pass_nb_patch_done = 0
unflat_logger.info("Unflattening at maturity {0} path {1}".format(self.cur_maturity, self.cur_maturity_pass))
dump_microcode_for_debug(self.mba, self.log_dir, "unflat_{0}_start".format(self.cur_maturity_pass))
self.retrieve_all_dispatchers()
if len(self.dispatcher_list) == 0:
unflat_logger.info("No dispatcher found at maturity {0}".format(self.mba.maturity))
return 0
else:
unflat_logger.info("Unflattening: {0} dispatcher(s) found".format(len(self.dispatcher_list)))
for dispatcher_info in self.dispatcher_list:
dispatcher_info.print_info()
self.last_pass_nb_patch_done = self.remove_flattening()
unflat_logger.info("Unflattening at maturity {0} path {1}: {2} changes"
.format(self.cur_maturity, self.cur_maturity_pass, self.last_pass_nb_patch_done))
mba_deep_cleaning(self.mba)
dump_microcode_for_debug(self.mba, self.log_dir, "unflat_{0}_after_cleaning".format(self.cur_maturity_pass))
if self.last_pass_nb_patch_done > 0:
self.mba.mark_chains_dirty()
self.mba.optimize_local(0)
self.mba.verify(True)
return self.last_pass_nb_patch_done

View File

@@ -0,0 +1,115 @@
import logging
from typing import Tuple, List
from ida_hexrays import *
from d810.hexrays_helpers import extract_num_mop, append_mop_if_not_in_list
from d810.optimizers.flow.flattening.generic import GenericDispatcherCollector, GenericDispatcherInfo, \
GenericDispatcherBlockInfo, GenericDispatcherUnflatteningRule
unflat_logger = logging.getLogger('D810.unflat')
FLATTENING_JUMP_OPCODES = [m_jnz, m_jz, m_jae, m_jb, m_ja, m_jbe, m_jg, m_jge, m_jl, m_jle]
class OllvmDispatcherBlockInfo(GenericDispatcherBlockInfo):
pass
class OllvmDispatcherInfo(GenericDispatcherInfo):
def explore(self, blk: mblock_t) -> bool:
self.reset()
if not self._is_candidate_for_dispatcher_entry_block(blk):
return False
self.entry_block = OllvmDispatcherBlockInfo(blk)
self.entry_block.parse()
for used_mop in self.entry_block.use_list:
append_mop_if_not_in_list(used_mop, self.entry_block.assume_def_list)
self.dispatcher_internal_blocks.append(self.entry_block)
num_mop, self.mop_compared = self._get_comparison_info(self.entry_block.blk)
self.comparison_values.append(num_mop.nnn.value)
self._explore_children(self.entry_block)
dispatcher_blk_with_external_father = self._get_dispatcher_blocks_with_external_father()
if len(dispatcher_blk_with_external_father) != 0:
return False
return True
def _is_candidate_for_dispatcher_entry_block(self, blk: mblock_t) -> bool:
# blk must be a condition branch with one numerical operand
num_mop, mop_compared = self._get_comparison_info(blk)
if (num_mop is None) or (mop_compared is None):
return False
# Its fathers are not conditional branch with this mop
for father_serial in blk.predset:
father_blk = self.mba.get_mblock(father_serial)
father_num_mop, father_mop_compared = self._get_comparison_info(father_blk)
if (father_num_mop is not None) and (father_mop_compared is not None):
if mop_compared.equal_mops(father_mop_compared, EQ_IGNSIZE):
return False
return True
def _get_comparison_info(self, blk: mblock_t) -> Tuple[mop_t, mop_t]:
# We check if blk is a good candidate for dispatcher entry block: blk.tail must be a conditional branch
if (blk.tail is None) or (blk.tail.opcode not in FLATTENING_JUMP_OPCODES):
return None, None
# One operand must be numerical
num_mop, mop_compared = extract_num_mop(blk.tail)
if num_mop is None or mop_compared is None:
return None, None
return num_mop, mop_compared
def is_part_of_dispatcher(self, block_info: OllvmDispatcherBlockInfo) -> bool:
is_ok = block_info.does_only_need(block_info.father.assume_def_list)
if not is_ok:
return False
if (block_info.blk.tail is not None) and (block_info.blk.tail.opcode not in FLATTENING_JUMP_OPCODES):
return False
return True
def _explore_children(self, father_info: OllvmDispatcherBlockInfo):
for child_serial in father_info.blk.succset:
if child_serial in [blk_info.blk.serial for blk_info in self.dispatcher_internal_blocks]:
return
if child_serial in [blk_info.blk.serial for blk_info in self.dispatcher_exit_blocks]:
return
child_blk = self.mba.get_mblock(child_serial)
child_info = OllvmDispatcherBlockInfo(child_blk, father_info)
child_info.parse()
if not self.is_part_of_dispatcher(child_info):
self.dispatcher_exit_blocks.append(child_info)
else:
self.dispatcher_internal_blocks.append(child_info)
if child_info.comparison_value is not None:
self.comparison_values.append(child_info.comparison_value)
self._explore_children(child_info)
def _get_external_fathers(self, block_info: OllvmDispatcherBlockInfo) -> List[mblock_t]:
internal_serials = [blk_info.blk.serial for blk_info in self.dispatcher_internal_blocks]
external_fathers = []
for blk_father in block_info.blk.predset:
if blk_father not in internal_serials:
external_fathers.append(blk_father)
return external_fathers
def _get_dispatcher_blocks_with_external_father(self) -> List[mblock_t]:
dispatcher_blocks_with_external_father = []
for blk_info in self.dispatcher_internal_blocks:
if blk_info.blk.serial != self.entry_block.blk.serial:
external_fathers = self._get_external_fathers(blk_info)
if len(external_fathers) > 0:
dispatcher_blocks_with_external_father.append(blk_info)
return dispatcher_blocks_with_external_father
class OllvmDispatcherCollector(GenericDispatcherCollector):
DISPATCHER_CLASS = OllvmDispatcherInfo
DEFAULT_DISPATCHER_MIN_INTERNAL_BLOCK = 2
DEFAULT_DISPATCHER_MIN_EXIT_BLOCK = 2
DEFAULT_DISPATCHER_MIN_COMPARISON_VALUE = 2
class Unflattener(GenericDispatcherUnflatteningRule):
DESCRIPTION = "Remove control flow flattening generated by OLLVM"
DISPATCHER_COLLECTOR_CLASS = OllvmDispatcherCollector
DEFAULT_UNFLATTENING_MATURITIES = [MMAT_CALLS, MMAT_GLBOPT1, MMAT_GLBOPT2]
DEFAULT_MAX_DUPLICATION_PASSES = 20
DEFAULT_MAX_PASSES = 5

View File

@@ -0,0 +1,95 @@
import logging
from typing import List
from ida_hexrays import *
from d810.tracker import MopTracker
from d810.cfg_utils import change_1way_block_successor
from d810.hexrays_formatters import format_minsn_t, dump_microcode_for_debug
from d810.optimizers.flow.flattening.utils import get_all_possibles_values
from d810.optimizers.flow.flattening.generic import GenericUnflatteningRule
unflat_logger = logging.getLogger('D810.unflat')
FAKE_LOOP_OPCODES = [m_jz, m_jnz]
class UnflattenerFakeJump(GenericUnflatteningRule):
DESCRIPTION = "Check if a jump is always taken for each father blocks and remove them"
DEFAULT_UNFLATTENING_MATURITIES = [MMAT_CALLS, MMAT_GLBOPT1]
DEFAULT_MAX_PASSES = None
def analyze_blk(self, blk: mblock_t) -> int:
if (blk.tail is None) or blk.tail.opcode not in FAKE_LOOP_OPCODES:
return 0
if blk.get_reginsn_qty() != 1:
return 0
if blk.tail.r.t != mop_n:
return 0
unflat_logger.info("Checking if block {0} is fake loop: {1}".format(blk.serial, format_minsn_t(blk.tail)))
op_compared = mop_t(blk.tail.l)
blk_preset_list = [x for x in blk.predset]
nb_change = 0
for pred_serial in blk_preset_list:
cmp_variable_tracker = MopTracker([op_compared], max_nb_block=100, max_path=1000)
cmp_variable_tracker.reset()
pred_blk = blk.mba.get_mblock(pred_serial)
pred_histories = cmp_variable_tracker.search_backward(pred_blk, pred_blk.tail)
father_is_resolvable = all([father_history.is_resolved() for father_history in pred_histories])
if not father_is_resolvable:
return 0
pred_values = get_all_possibles_values(pred_histories, [op_compared])
pred_values = [x[0] for x in pred_values]
if None in pred_values:
unflat_logger.info("Some path are not resolved, can't fix jump")
return 0
unflat_logger.info("Pred {0} has {1} possible path ({2} different cst): {3}"
.format(pred_blk.serial, len(pred_values), len(set(pred_values)), pred_values))
if self.fix_successor(blk, pred_blk, pred_values):
nb_change += 1
return nb_change
def fix_successor(self, fake_loop_block: mblock_t, pred: mblock_t, pred_comparison_values: List[int]) -> bool:
if len(pred_comparison_values) == 0:
return False
jmp_ins = fake_loop_block.tail
compared_value = jmp_ins.r.nnn.value
jmp_taken = False
jmp_not_taken = False
dst_serial = None
if jmp_ins.opcode == m_jz:
jmp_taken = all([possible_value == compared_value for possible_value in pred_comparison_values])
jmp_not_taken = all([possible_value != compared_value for possible_value in pred_comparison_values])
elif jmp_ins.opcode == m_jnz:
jmp_taken = all([possible_value != compared_value for possible_value in pred_comparison_values])
jmp_not_taken = all([possible_value == compared_value for possible_value in pred_comparison_values])
# TODO: handles other jumps cases
if jmp_taken:
unflat_logger.info("It seems that '{0}' is always taken when coming from {1}: {2}"
.format(format_minsn_t(jmp_ins), pred.serial, pred_comparison_values))
dst_serial = jmp_ins.d.b
if jmp_not_taken:
unflat_logger.info("It seems that '{0}' is never taken when coming from {1}: {2}"
.format(format_minsn_t(jmp_ins), pred.serial, pred_comparison_values))
dst_serial = fake_loop_block.serial + 1
if dst_serial is None:
unflat_logger.debug("Jump seems legit '{0}' from {1}: {2}"
.format(format_minsn_t(jmp_ins), pred.serial, pred_comparison_values))
return False
dump_microcode_for_debug(self.mba, self.log_dir, "{0}_before_fake_jump".format(self.cur_maturity_pass))
unflat_logger.info("Making pred {0} with value {1} goto {2} ({3})"
.format(pred.serial, pred_comparison_values, dst_serial, format_minsn_t(jmp_ins)))
dump_microcode_for_debug(self.mba, self.log_dir, "{0}_after_fake_jump".format(self.cur_maturity_pass))
return change_1way_block_successor(pred, dst_serial)
def optimize(self, blk: mblock_t) -> int:
self.mba = blk.mba
if not self.check_if_rule_should_be_used(blk):
return 0
self.last_pass_nb_patch_done = self.analyze_blk(blk)
if self.last_pass_nb_patch_done > 0:
self.mba.mark_chains_dirty()
self.mba.optimize_local(0)
self.mba.verify(True)
return self.last_pass_nb_patch_done

View File

@@ -0,0 +1,116 @@
import logging
import idaapi
from typing import List
from ida_hexrays import *
from d810.hexrays_helpers import append_mop_if_not_in_list, AND_TABLE, CONTROL_FLOW_OPCODES
from d810.tracker import MopTracker, MopHistory
from d810.optimizers.flow.flattening.generic import GenericDispatcherBlockInfo, GenericDispatcherInfo, \
GenericDispatcherCollector, GenericDispatcherUnflatteningRule, NotDuplicableFatherException, DispatcherUnflatteningException, NotResolvableFatherException
from d810.optimizers.flow.flattening.utils import configure_mop_tracker_log_verbosity, restore_mop_tracker_log_verbosity
from d810.tracker import duplicate_histories
from d810.cfg_utils import create_block, change_1way_block_successor
from d810.hexrays_formatters import format_minsn_t, format_mop_t
from d810.emulator import MicroCodeEnvironment, MicroCodeInterpreter
unflat_logger = logging.getLogger('D810.unflat')
FLATTENING_JUMP_OPCODES = [m_jtbl]
class TigressIndirectDispatcherBlockInfo(GenericDispatcherBlockInfo):
pass
class TigressIndirectDispatcherInfo(GenericDispatcherInfo):
def explore(self, blk: mblock_t):
self.reset()
if not self._is_candidate_for_dispatcher_entry_block(blk):
return
self.mop_compared = self._get_comparison_info(blk)
self.entry_block = TigressIndirectDispatcherBlockInfo(blk)
self.entry_block.parse()
for used_mop in self.entry_block.use_list:
append_mop_if_not_in_list(used_mop, self.entry_block.assume_def_list)
self.dispatcher_internal_blocks.append(self.entry_block)
self.dispatcher_exit_blocks = []
self.comparison_values = []
return True
def _get_comparison_info(self, blk: mblock_t):
# blk.tail must be a jtbl
if (blk.tail is None) or (blk.tail.opcode != m_ijmp):
return None, None
return blk.tail.l
def _is_candidate_for_dispatcher_entry_block(self, blk: mblock_t):
if (blk.tail is None) or (blk.tail.opcode != m_ijmp):
return False
return True
def should_emulation_continue(self, cur_blk: mblock_t):
if (cur_blk is not None) and (cur_blk.serial == self.entry_block.serial):
return True
return False
class TigressIndirectDispatcherCollector(GenericDispatcherCollector):
DISPATCHER_CLASS = TigressIndirectDispatcherInfo
DEFAULT_DISPATCHER_MIN_INTERNAL_BLOCK = 0
DEFAULT_DISPATCHER_MIN_EXIT_BLOCK = 0
DEFAULT_DISPATCHER_MIN_COMPARISON_VALUE = 0
class LabelTableInfo(object):
def __init__(self, sp_offset, mem_offset, nb_elt):
self.sp_offset = sp_offset
self.mem_offset = mem_offset
self.nb_elt = nb_elt
def update_mop_tracker(self, mba: mbl_array_t, mop_tracker: MopTracker):
stack_array_base_address = mba.stkoff_ida2vd(self.sp_offset)
# print("stack_array_base_address: {0:x}".format(stack_array_base_address))
for i in range(self.nb_elt):
tmp_mop = mop_t()
tmp_mop.erase()
tmp_mop._make_stkvar(mba, stack_array_base_address + 8 * i)
tmp_mop.size = 8
mem_val = idaapi.get_qword(self.mem_offset + 8 * i) & AND_TABLE[8]
mop_tracker.add_mop_definition(tmp_mop, mem_val)
class UnflattenerTigressIndirect(GenericDispatcherUnflatteningRule):
DESCRIPTION = ""
DISPATCHER_COLLECTOR_CLASS = TigressIndirectDispatcherCollector
DEFAULT_UNFLATTENING_MATURITIES = [MMAT_LOCOPT]
DEFAULT_MAX_DUPLICATION_PASSES = 20
DEFAULT_MAX_PASSES = 1
def __init__(self):
super().__init__()
self.label_info = None
self.goto_table_info = {}
def configure(self, kwargs):
super().configure(kwargs)
if "goto_table_info" in self.config.keys():
for ea_str, table_info in self.config["goto_table_info"].items():
self.goto_table_info[int(ea_str, 16)] = LabelTableInfo(sp_offset=int(table_info["stack_table_offset"], 16),
mem_offset=int(table_info["table_address"], 16),
nb_elt=table_info["table_nb_elt"])
def check_if_rule_should_be_used(self, blk: mblock_t):
if not super().check_if_rule_should_be_used(blk):
return False
if self.mba.entry_ea not in self.goto_table_info:
return False
if (self.cur_maturity_pass >= 1) and (self.last_pass_nb_patch_done == 0):
return False
self.label_info = self.goto_table_info[self.mba.entry_ea]
return True
def register_initialization_variables(self, mop_tracker: MopTracker):
self.label_info.update_mop_tracker(self.mba, mop_tracker)
def check_if_histories_are_resolved(self, mop_histories: List[MopHistory]):
return True

View File

@@ -0,0 +1,60 @@
import logging
from ida_hexrays import *
from d810.hexrays_helpers import append_mop_if_not_in_list
from d810.optimizers.flow.flattening.generic import GenericDispatcherBlockInfo, GenericDispatcherInfo, \
GenericDispatcherCollector, GenericDispatcherUnflatteningRule
unflat_logger = logging.getLogger('D810.unflat')
FLATTENING_JUMP_OPCODES = [m_jtbl]
class TigressSwitchDispatcherBlockInfo(GenericDispatcherBlockInfo):
pass
class TigressSwitchDispatcherInfo(GenericDispatcherInfo):
def explore(self, blk: mblock_t):
self.reset()
if not self._is_candidate_for_dispatcher_entry_block(blk):
return
self.mop_compared, mcases = self._get_comparison_info(blk)
self.entry_block = TigressSwitchDispatcherBlockInfo(blk)
self.entry_block.parse()
for used_mop in self.entry_block.use_list:
append_mop_if_not_in_list(used_mop, self.entry_block.assume_def_list)
self.dispatcher_internal_blocks.append(self.entry_block)
for possible_values, target_block_serial in zip(mcases.c.values, mcases.c.targets):
if target_block_serial == self.entry_block.blk.serial:
continue
exit_block = TigressSwitchDispatcherBlockInfo(blk.mba.get_mblock(target_block_serial), self.entry_block)
self.dispatcher_exit_blocks.append(exit_block)
self.comparison_values.append(possible_values[0])
return True
def _get_comparison_info(self, blk: mblock_t):
# blk.tail must be a jtbl
if (blk.tail is None) or (blk.tail.opcode != m_jtbl):
return None, None
return blk.tail.l, blk.tail.r
def _is_candidate_for_dispatcher_entry_block(self, blk: mblock_t):
if (blk.tail is None) or (blk.tail.opcode != m_jtbl):
return False
return True
class TigressSwitchDispatcherCollector(GenericDispatcherCollector):
DISPATCHER_CLASS = TigressSwitchDispatcherInfo
DEFAULT_DISPATCHER_MIN_INTERNAL_BLOCK = 0
DEFAULT_DISPATCHER_MIN_EXIT_BLOCK = 4
DEFAULT_DISPATCHER_MIN_COMPARISON_VALUE = 4
class UnflattenerSwitchCase(GenericDispatcherUnflatteningRule):
DESCRIPTION = "Remove control flow flattening generated by Tigress with Switch case dispatcher"
DISPATCHER_COLLECTOR_CLASS = TigressSwitchDispatcherCollector
DEFAULT_UNFLATTENING_MATURITIES = [MMAT_GLBOPT1]
DEFAULT_MAX_DUPLICATION_PASSES = 20
DEFAULT_MAX_PASSES = 5

View File

@@ -0,0 +1,56 @@
import logging
tracker_logger = logging.getLogger('D810.tracker')
emulator_logger = logging.getLogger('D810.emulator')
class UnflatteningException(Exception):
pass
class DispatcherUnflatteningException(UnflatteningException):
pass
class NotDuplicableFatherException(UnflatteningException):
pass
class NotResolvableFatherException(UnflatteningException):
pass
def configure_mop_tracker_log_verbosity(verbose=False):
tracker_log_level = tracker_logger.getEffectiveLevel()
emulator_log_level = emulator_logger.getEffectiveLevel()
if not verbose:
tracker_logger.setLevel(logging.ERROR)
emulator_logger.setLevel(logging.ERROR)
return [tracker_log_level, emulator_log_level]
def restore_mop_tracker_log_verbosity(tracker_log_level, emulator_log_level):
tracker_logger.setLevel(tracker_log_level)
emulator_logger.setLevel(emulator_log_level)
def get_all_possibles_values(mop_histories, searched_mop_list, verbose=False):
log_levels = configure_mop_tracker_log_verbosity(verbose)
mop_cst_values_list = []
for mop_history in mop_histories:
mop_cst_values_list.append([mop_history.get_mop_constant_value(searched_mop)
for searched_mop in searched_mop_list])
restore_mop_tracker_log_verbosity(*log_levels)
return mop_cst_values_list
def check_if_all_values_are_found(mop_cst_values_list):
all_values_are_found = True
for cst_list in mop_cst_values_list:
if None in cst_list:
all_values_are_found = False
break
return all_values_are_found

View File

@@ -0,0 +1,39 @@
import logging
import idc
from d810.optimizers.handler import OptimizationRule, DEFAULT_FLOW_MATURITIES
logger = logging.getLogger('D810.optimizer')
class FlowOptimizationRule(OptimizationRule):
def __init__(self):
super().__init__()
self.maturities = DEFAULT_FLOW_MATURITIES
self.use_whitelist = False
self.whitelisted_function_ea_list = []
self.use_blacklist = False
self.blacklisted_function_ea_list = []
def configure(self, kwargs):
super().configure(kwargs)
self.use_whitelist = False
self.whitelisted_function_ea_list = []
self.use_blacklist = False
self.blacklisted_function_ea_list = []
if "whitelisted_functions" in self.config.keys():
self.use_whitelist = True
for func_ea in self.config["whitelisted_functions"]:
self.whitelisted_function_ea_list.append(int(func_ea, 16))
func_name_list = [idc.get_func_name(ea) for ea in self.whitelisted_function_ea_list]
logger.info("Whitelisted functions for {0}: {1} -> {2}".format(self.__class__.__name__,
self.whitelisted_function_ea_list,
func_name_list))
if "blacklisted_functions" in self.config.keys():
self.use_blacklist = True
for func_ea in self.config["whitelisted_functions"]:
self.blacklisted_function_ea_list.append(int(func_ea, 16))
func_name_list = [idc.get_func_name(ea) for ea in self.blacklisted_function_ea_list]
logger.info("Blacklisted functions for {0}: {1} -> {2}".format(self.__class__.__name__,
self.blacklisted_function_ea_list,
func_name_list))

View File

@@ -0,0 +1,11 @@
from d810.utils import get_all_subclasses
from d810.optimizers.flow.jumps.handler import JumpOptimizationRule, JumpFixer
from d810.optimizers.flow.jumps.opaque import *
from d810.optimizers.flow.jumps.tricks import *
JUMP_OPTIMIZATION_RULES = [x() for x in get_all_subclasses(JumpOptimizationRule)]
jump_fixer = JumpFixer()
for jump_optimization_rule in JUMP_OPTIMIZATION_RULES:
jump_fixer.register_rule(jump_optimization_rule)
JUMP_OPTIMIZATION_BLOCK_RULES = [jump_fixer]

View File

@@ -0,0 +1,183 @@
import logging
from ida_hexrays import *
from typing import Union
from d810.optimizers.instructions.handler import InstructionOptimizationRule
from d810.optimizers.instructions.pattern_matching.handler import ast_generator
from d810.ast import mop_to_ast, AstNode
from d810.hexrays_formatters import format_minsn_t, opcode_to_string
from d810.optimizers.flow.handler import FlowOptimizationRule
from d810.cfg_utils import make_2way_block_goto, is_conditional_jump, change_2way_block_conditional_successor
logger = logging.getLogger("D810.branch_fixer")
optimizer_logger = logging.getLogger('D810.optimizer')
class JumpOptimizationRule(InstructionOptimizationRule):
ORIGINAL_JUMP_OPCODES = []
LEFT_PATTERN = None
RIGHT_PATTERN = None
REPLACEMENT_OPCODE = None
REPLACEMENT_LEFT_PATTERN = None
REPLACEMENT_RIGHT_PATTERN = None
FUZZ_PATTERNS = True
def __init__(self):
super().__init__()
self.fuzz_patterns = self.FUZZ_PATTERNS
self.left_pattern_candidates = []
self.right_pattern_candidates = []
self.jump_original_block_serial = None
self.direct_block_serial = None
self.jump_replacement_block_serial = None
def configure(self, fuzz_pattern=None, **kwargs):
super().configure(kwargs)
if fuzz_pattern is not None:
self.fuzz_patterns = fuzz_pattern
self._generate_pattern_candidates()
def _generate_pattern_candidates(self):
self.fuzz_patterns = self.FUZZ_PATTERNS
if self.LEFT_PATTERN is not None:
self.LEFT_PATTERN.reset_mops()
if not self.fuzz_patterns:
self.left_pattern_candidates = [self.LEFT_PATTERN]
else:
self.left_pattern_candidates = ast_generator(self.LEFT_PATTERN)
if self.RIGHT_PATTERN is not None:
self.RIGHT_PATTERN.reset_mops()
if not self.fuzz_patterns:
self.right_pattern_candidates = [self.RIGHT_PATTERN]
else:
self.right_pattern_candidates = ast_generator(self.RIGHT_PATTERN)
def check_candidate(self, opcode, left_candidate: AstNode, right_candidate: AstNode):
return False
def get_valid_candidates(self, instruction, left_ast: AstNode, right_ast: AstNode, stop_early=True):
valid_candidates = []
if left_ast is None or right_ast is None:
return []
for left_candidate_pattern in self.left_pattern_candidates:
if not left_candidate_pattern.check_pattern_and_copy_mops(left_ast):
continue
for right_candidate_pattern in self.right_pattern_candidates:
if not right_candidate_pattern.check_pattern_and_copy_mops(right_ast):
continue
if not self.check_candidate(instruction.opcode, left_candidate_pattern, right_candidate_pattern):
continue
valid_candidates.append([left_candidate_pattern, right_candidate_pattern])
if stop_early:
return valid_candidates
return []
def check_pattern_and_replace(self, blk: mblock_t, instruction: minsn_t, left_ast: AstNode, right_ast: AstNode):
if instruction.opcode not in self.ORIGINAL_JUMP_OPCODES:
return None
self.jump_original_block_serial = instruction.d.b
self.direct_block_serial = blk.serial + 1
self.jump_replacement_block_serial = None
valid_candidates = self.get_valid_candidates(instruction, left_ast, right_ast, stop_early=True)
if len(valid_candidates) == 0:
return None
if self.jump_original_block_serial is None:
self.jump_replacement_block_serial = self.jump_original_block_serial
left_candidate, right_candidate = valid_candidates[0]
new_ins = self.get_replacement(instruction, left_candidate, right_candidate)
return new_ins
def get_replacement(self, original_ins: minsn_t, left_candidate: AstNode, right_candidate: AstNode):
new_left_mop = None
new_right_mop = None
new_dst_mop = None
if self.jump_original_block_serial is not None:
new_dst_mop = mop_t()
new_dst_mop.make_blkref(self.jump_replacement_block_serial)
if self.REPLACEMENT_LEFT_PATTERN is not None:
is_ok = self.REPLACEMENT_LEFT_PATTERN.update_leafs_mop(left_candidate, right_candidate)
if not is_ok:
return None
new_left_mop = self.REPLACEMENT_LEFT_PATTERN.create_mop(original_ins.ea)
if self.REPLACEMENT_RIGHT_PATTERN is not None:
is_ok = self.REPLACEMENT_RIGHT_PATTERN.update_leafs_mop(left_candidate, right_candidate)
if not is_ok:
return None
new_right_mop = self.REPLACEMENT_RIGHT_PATTERN.create_mop(original_ins.ea)
new_ins = self.create_new_ins(original_ins, new_left_mop, new_right_mop, new_dst_mop)
return new_ins
def create_new_ins(self, original_ins: minsn_t, new_left_mop: mop_t,
new_right_mop: Union[None, mop_t] = None, new_dst_mop: Union[None, mop_t] = None) -> minsn_t:
new_ins = minsn_t(original_ins)
new_ins.opcode = self.REPLACEMENT_OPCODE
if self.REPLACEMENT_OPCODE == m_goto:
new_ins.l = new_dst_mop
new_ins.r.erase()
new_ins.d.erase()
return new_ins
new_ins.l = new_left_mop
if new_right_mop is not None:
new_ins.r = new_right_mop
if new_dst_mop is not None:
new_ins.d = new_dst_mop
return new_ins
@property
def description(self):
self.LEFT_PATTERN.reset_mops()
self.RIGHT_PATTERN.reset_mops()
return "{0}: {1}, {2}".format(",".join([opcode_to_string(x) for x in self.JMP_OPCODES]),
self.LEFT_PATTERN, self.RIGHT_PATTERN)
class JumpFixer(FlowOptimizationRule):
def __init__(self):
super().__init__()
self.known_rules = []
self.rules = []
def register_rule(self, rule: JumpOptimizationRule):
self.known_rules.append(rule)
def configure(self, kwargs):
super().configure(kwargs)
if "enabled_rules" in self.config.keys():
for rule in self.known_rules:
if rule.name in self.config["enabled_rules"]:
rule.configure()
self.rules.append(rule)
optimizer_logger.debug("JumpFixer enables rule {0}".format(rule.name))
else:
optimizer_logger.debug("JumpFixer disables rule {0}".format(rule.name))
def optimize(self, blk: mblock_t) -> bool:
if not is_conditional_jump(blk):
return False
left_ast = mop_to_ast(blk.tail.l)
right_ast = mop_to_ast(blk.tail.r)
for rule in self.rules:
try:
new_ins = rule.check_pattern_and_replace(blk, blk.tail, left_ast, right_ast)
if new_ins:
optimizer_logger.info("Rule {0} matched:".format(rule.name))
optimizer_logger.info(" orig: {0}".format(format_minsn_t(blk.tail)))
optimizer_logger.info(" new : {0}".format(format_minsn_t(new_ins)))
if new_ins.opcode == m_goto:
make_2way_block_goto(blk, new_ins.d.b)
else:
change_2way_block_conditional_successor(blk, new_ins.d.b)
blk.make_nop(blk.tail)
blk.insert_into_block(new_ins, blk.tail)
return True
except RuntimeError as e:
optimizer_logger.error("Error during rule {0} for instruction {1}: {2}"
.format(rule, format_minsn_t(blk.tail), e))
return False

View File

@@ -0,0 +1,184 @@
from ida_hexrays import *
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.optimizers.flow.jumps.handler import JumpOptimizationRule
class JnzRule1(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
LEFT_PATTERN = AstNode(m_neg,
AstNode(m_and,
AstNode(m_bnot,
AstLeaf("x_0")),
AstConstant("1", 1)))
RIGHT_PATTERN = AstLeaf("x_0")
REPLACEMENT_OPCODE = m_goto
def check_candidate(self, opcode, left_candidate, right_candidate):
if opcode == m_jnz:
self.jump_replacement_block_serial = self.jump_original_block_serial
else:
self.jump_replacement_block_serial = self.direct_block_serial
return True
class JnzRule2(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
LEFT_PATTERN = AstNode(m_or,
AstNode(m_bnot,
AstLeaf("x_0")),
AstConstant("1", 1))
RIGHT_PATTERN = AstConstant("0", 0)
REPLACEMENT_OPCODE = m_goto
def check_candidate(self, opcode, left_candidate, right_candidate):
if opcode == m_jnz:
self.jump_replacement_block_serial = self.jump_original_block_serial
else:
self.jump_replacement_block_serial = self.direct_block_serial
return True
class JnzRule3(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
LEFT_PATTERN = AstNode(m_xor,
AstNode(m_xor,
AstLeaf("x_0"),
AstConstant("c_1")),
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_2")))
RIGHT_PATTERN = AstConstant("0", 0)
REPLACEMENT_OPCODE = m_goto
def check_candidate(self, opcode, left_candidate, right_candidate):
tmp = left_candidate["c_1"].value & left_candidate["c_2"].value
if tmp == 0:
return False
if opcode == m_jnz:
self.jump_replacement_block_serial = self.jump_original_block_serial
else:
self.jump_replacement_block_serial = self.direct_block_serial
return True
class JnzRule4(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
LEFT_PATTERN = AstNode(m_sub,
AstConstant("3", 3),
AstLeaf("x_0"))
RIGHT_PATTERN = AstLeaf("x_0")
REPLACEMENT_OPCODE = m_goto
def check_candidate(self, opcode, left_candidate, right_candidate):
if opcode == m_jnz:
self.jump_replacement_block_serial = self.jump_original_block_serial
else:
self.jump_replacement_block_serial = self.direct_block_serial
return True
class JnzRule5(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
LEFT_PATTERN = AstNode(m_xor,
AstNode(m_sub,
AstConstant("3", 3),
AstLeaf("x_0")),
AstLeaf("x_0"))
RIGHT_PATTERN = AstConstant("0", 0)
REPLACEMENT_OPCODE = m_goto
def check_candidate(self, opcode, left_candidate, right_candidate):
if opcode == m_jnz:
self.jump_replacement_block_serial = self.jump_original_block_serial
else:
self.jump_replacement_block_serial = self.direct_block_serial
return True
class JnzRule6(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
LEFT_PATTERN = AstNode(m_xor,
AstNode(m_bnot,
AstNode(m_sub,
AstConstant("3", 3),
AstLeaf("x_0"))),
AstNode(m_bnot,
AstLeaf("x_0")))
RIGHT_PATTERN = AstConstant("0", 0)
REPLACEMENT_OPCODE = m_goto
def check_candidate(self, opcode, left_candidate, right_candidate):
if opcode == m_jnz:
self.jump_replacement_block_serial = self.jump_original_block_serial
else:
self.jump_replacement_block_serial = self.direct_block_serial
return True
class JnzRule7(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
LEFT_PATTERN = AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_1"))
RIGHT_PATTERN = AstConstant("c_2")
REPLACEMENT_OPCODE = m_goto
def check_candidate(self, opcode, left_candidate, right_candidate):
tmp = left_candidate["c_1"].value & right_candidate["c_2"].value
if tmp == right_candidate["c_2"].value:
return False
if opcode == m_jnz:
self.jump_replacement_block_serial = self.jump_original_block_serial
else:
self.jump_replacement_block_serial = self.direct_block_serial
return True
class JnzRule8(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
PATTERN = AstNode(m_or,
AstLeaf("x_0"),
AstConstant("c_1"))
RIGHT_PATTERN = AstConstant("c_2")
REPLACEMENT_OPCODE = m_goto
def check_candidate(self, opcode, left_candidate, right_candidate):
tmp = left_candidate["c_1"].value & right_candidate["c_2"].value
if tmp == left_candidate["c_1"].value:
return False
if opcode == m_jnz:
self.jump_replacement_block_serial = self.jump_original_block_serial
else:
self.jump_replacement_block_serial = self.direct_block_serial
return True
class JbRule1(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jb]
PATTERN = AstNode(m_xdu,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("1", 1)))
RIGHT_PATTERN = AstConstant("2", 2)
REPLACEMENT_OPCODE = m_goto
def check_candidate(self, opcode, left_candidate, right_candidate):
self.jump_replacement_block_serial = self.jump_original_block_serial
return True
class JaeRule1(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jae]
PATTERN = AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_1"))
RIGHT_PATTERN = AstConstant("c_2")
REPLACEMENT_OPCODE = m_goto
def check_candidate(self, opcode, left_candidate, right_candidate):
if left_candidate["c_1"].value >= right_candidate["c_2"].value:
return False
self.jump_replacement_block_serial = self.direct_block_serial
return True

View File

@@ -0,0 +1,98 @@
from ida_hexrays import *
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.hexrays_helpers import equal_mops_bypass_xdu, equal_bnot_mop
from d810.optimizers.flow.jumps.handler import JumpOptimizationRule
class CompareConstantRule1(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jge]
LEFT_PATTERN = AstNode(m_and,
AstNode(m_or, AstLeaf("xdu_x_0"), AstConstant("c_2")),
AstNode(m_or,
AstNode(m_xor, AstLeaf("x_0"), AstConstant("c_1")),
AstNode(m_bnot, AstNode(m_sub, AstLeaf("x_0"), AstConstant("c_1")))))
RIGHT_PATTERN = AstConstant("0", 0)
REPLACEMENT_OPCODE = m_jl
REPLACEMENT_LEFT_PATTERN = AstLeaf("x_0")
REPLACEMENT_RIGHT_PATTERN = AstLeaf("c_1")
def check_candidate(self, opcode, left_candidate, right_candidate):
if not equal_mops_bypass_xdu(left_candidate["xdu_x_0"].mop, left_candidate["x_0"].mop):
return False
if not equal_bnot_mop(left_candidate["c_2"].mop, left_candidate["c_1"].mop):
return False
self.jump_replacement_block_serial = self.jump_original_block_serial
return True
class CompareConstantRule2(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jge]
LEFT_PATTERN = AstNode(m_or,
AstNode(m_xdu,
AstNode(m_and,
AstNode(m_bnot, AstLeaf("x_0")), AstConstant("c_1"))),
AstNode(m_and,
AstNode(m_sub, AstLeaf('xdu_x_0'), AstConstant('xdu_c_1')),
AstNode(m_bnot, AstNode(m_xdu, AstNode(m_xor, AstLeaf('xdu1_x_0'), AstConstant('xdu_c_1'))))))
RIGHT_PATTERN = AstConstant("0", 0)
REPLACEMENT_OPCODE = m_jge
REPLACEMENT_LEFT_PATTERN = AstLeaf("x_0")
REPLACEMENT_RIGHT_PATTERN = AstLeaf("c_1")
def check_candidate(self, opcode, left_candidate, right_candidate):
if not equal_mops_bypass_xdu(left_candidate["xdu_x_0"].mop, left_candidate["x_0"].mop):
return False
if not equal_mops_bypass_xdu(left_candidate["xdu1_x_0"].mop, left_candidate["x_0"].mop):
return False
self.jump_replacement_block_serial = self.jump_original_block_serial
return True
class CompareConstantRule3(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jge]
LEFT_PATTERN = AstNode(m_and,
AstNode(m_sub, AstLeaf('x_0'), AstConstant('c_1')),
AstNode(m_bnot, AstLeaf("x_0")))
RIGHT_PATTERN = AstConstant("0", 0)
REPLACEMENT_OPCODE = m_jg
REPLACEMENT_LEFT_PATTERN = AstLeaf("x_0")
REPLACEMENT_RIGHT_PATTERN = AstLeaf("c_1")
def check_candidate(self, opcode, left_candidate, right_candidate):
self.jump_replacement_block_serial = self.jump_original_block_serial
return True
class CompareConstantRule4(JumpOptimizationRule):
ORIGINAL_JUMP_OPCODES = [m_jl, m_jge]
LEFT_PATTERN = AstNode(m_and,
AstNode(m_or,
AstNode(m_bnot,
AstNode(m_sub,
AstLeaf('x_0'),
AstConstant('c_1'))),
AstNode(m_xor,
AstLeaf('x_0'),
AstConstant('c_1'))),
AstNode(m_or,
AstLeaf("xdu_x_0"),
AstConstant('bnot_c_1')))
RIGHT_PATTERN = AstConstant("0", 0)
REPLACEMENT_OPCODE = m_jge
REPLACEMENT_LEFT_PATTERN = AstLeaf("x_0")
REPLACEMENT_RIGHT_PATTERN = AstLeaf("c_1")
def check_candidate(self, opcode, left_candidate, right_candidate):
print("dflighdrth")
if not equal_mops_bypass_xdu(left_candidate["xdu_x_0"].mop, left_candidate["x_0"].mop):
return False
if not equal_bnot_mop(left_candidate["c_1"].mop, left_candidate["bnot_c_1"].mop):
return False
self.jump_replacement_block_serial = self.jump_original_block_serial
return True

View File

@@ -0,0 +1,36 @@
from ida_hexrays import *
from d810.hexrays_formatters import string_to_maturity
DEFAULT_INSTRUCTION_MATURITIES = [MMAT_LOCOPT, MMAT_CALLS, MMAT_GLBOPT1]
DEFAULT_FLOW_MATURITIES = [MMAT_CALLS, MMAT_GLBOPT1]
class OptimizationRule(object):
NAME = None
DESCRIPTION = None
def __init__(self):
self.maturities = []
self.config = {}
self.log_dir = None
def set_log_dir(self, log_dir):
self.log_dir = log_dir
def configure(self, kwargs):
self.config = kwargs if kwargs is not None else {}
if "maturities" in self.config.keys():
self.maturities = [string_to_maturity(x) for x in self.config["maturities"]]
@property
def name(self):
if self.NAME is not None:
return self.NAME
return self.__class__.__name__
@property
def description(self):
if self.DESCRIPTION is not None:
return self.DESCRIPTION
return "No description available"

View File

@@ -0,0 +1,7 @@
from d810.optimizers.instructions.chain import CHAIN_RULES, ChainOptimizer
from d810.optimizers.instructions.pattern_matching import PATTERN_MATCHING_RULES, PatternOptimizer
from d810.optimizers.instructions.z3 import Z3_RULES, Z3Optimizer
from d810.optimizers.instructions.analysis import INSTRUCTION_ANALYSIS_RULES, InstructionAnalyzer
from d810.optimizers.instructions.early import EARLY_RULES, EarlyOptimizer
KNOWN_INS_RULES = PATTERN_MATCHING_RULES + CHAIN_RULES + Z3_RULES + EARLY_RULES + INSTRUCTION_ANALYSIS_RULES

View File

@@ -0,0 +1,5 @@
from d810.utils import get_all_subclasses
from d810.optimizers.instructions.analysis.handler import InstructionAnalyzer, InstructionAnalysisRule
from d810.optimizers.instructions.analysis.pattern_guess import *
INSTRUCTION_ANALYSIS_RULES = CHAIN_RULES = [x() for x in get_all_subclasses(InstructionAnalysisRule)]

View File

@@ -0,0 +1,42 @@
import logging
from ida_hexrays import *
from d810.hexrays_formatters import format_minsn_t
from d810.optimizers.instructions.handler import InstructionOptimizer, InstructionOptimizationRule
optimizer_logger = logging.getLogger('D810.optimizer')
class InstructionAnalysisRule(InstructionOptimizationRule):
def analyze_instruction(self, blk, ins):
raise NotImplementedError
class InstructionAnalyzer(InstructionOptimizer):
RULE_CLASSES = [InstructionAnalysisRule]
def set_maturity(self, maturity: int):
self.cur_maturity = maturity
for rule in self.rules:
rule.set_maturity(self.cur_maturity)
def analyze(self, blk: mblock_t, ins: minsn_t):
if blk is not None:
self.cur_maturity = blk.mba.maturity
if self.cur_maturity not in self.maturities:
return None
for rule in self.rules:
try:
rule.analyze_instruction(blk, ins)
except RuntimeError:
optimizer_logger.error("error during rule {0} for instruction {1}".format(rule, format_minsn_t(ins)))
return None
@property
def name(self):
if self.NAME is not None:
return self.NAME
return self.__class__.__name__

View File

@@ -0,0 +1,89 @@
import os
from d810.ast import minsn_to_ast
from d810.hexrays_formatters import format_minsn_t, format_mop_t, maturity_to_string
from d810.optimizers.instructions.analysis.handler import InstructionAnalysisRule
from d810.optimizers.instructions.analysis.utils import get_possible_patterns
class ExampleGuessingRule(InstructionAnalysisRule):
DESCRIPTION = "Detect pattern with variable used multiple times and with multiple different opcodes"
def __init__(self):
super().__init__()
self.cur_maturity = None
self.min_nb_var = 1
self.max_nb_var = 3
self.min_nb_diff_opcodes = 3
self.max_nb_diff_opcodes = -1
self.cur_index = 0
self.max_index = 1000
self.cur_ins_guessed = [""] * self.max_index
self.pattern_filename_path = None
def log_info(self, message):
with open(self.pattern_filename_path, "a") as f:
f.write('{0}\n'.format(message))
def set_maturity(self, maturity):
self.log_info("Patterns guessed at maturity {0}".format(maturity_to_string(maturity)))
self.cur_maturity = maturity
def set_log_dir(self, log_dir):
super().set_log_dir(log_dir)
self.pattern_filename_path = os.path.join(self.log_dir, "pattern_guess.log")
f = open(self.pattern_filename_path, "w")
f.close()
def configure(self, kwargs):
super().configure(kwargs)
if "min_nb_var" in kwargs.keys():
self.min_nb_var = kwargs["min_nb_var"]
if "max_nb_var" in kwargs.keys():
self.max_nb_var = kwargs["max_nb_var"]
if "min_nb_diff_opcodes" in kwargs.keys():
self.min_nb_diff_opcodes = kwargs["min_nb_diff_opcodes"]
if "max_nb_diff_opcodes" in kwargs.keys():
self.max_nb_diff_opcodes = kwargs["max_nb_diff_opcodes"]
if self.max_nb_var == -1:
self.max_nb_var = 0xff
if self.max_nb_diff_opcodes == -1:
self.max_nb_diff_opcodes = 0xff
def analyze_instruction(self, blk, ins):
if self.cur_maturity not in self.maturities:
return None
formatted_ins = str(format_minsn_t(ins))
if formatted_ins in self.cur_ins_guessed:
return False
tmp = minsn_to_ast(ins)
if tmp is None:
return False
is_good_candidate = self.check_if_possible_pattern(tmp)
if is_good_candidate:
self.cur_ins_guessed[self.cur_index] = formatted_ins
self.cur_index = (self.cur_index + 1) % self.max_index
return is_good_candidate
def check_if_possible_pattern(self, test_ast):
patterns = get_possible_patterns(test_ast, min_nb_use=2, ref_ast_info_by_index=None, max_nb_pattern=64)
for pattern in patterns:
leaf_info_list, cst_leaf_values, opcodes = pattern.get_information()
leaf_nb_use = [leaf_info.number_of_use for leaf_info in leaf_info_list]
if not(self.min_nb_var <= len(leaf_info_list) <= self.max_nb_var):
continue
if not(self.min_nb_diff_opcodes <= len(set(opcodes)) <= self.max_nb_diff_opcodes):
continue
if not(min(leaf_nb_use) >= 2):
continue
ins = pattern.mop.d
self.log_info("IR: 0x{0:x} - {1}".format(ins.ea, format_minsn_t(ins)))
for leaf_info in leaf_info_list:
self.log_info(" {0} -> {1}".format(leaf_info.ast, format_mop_t(leaf_info.ast.mop)))
self.log_info("Pattern: {0}".format(pattern))
self.log_info("AstNode: {0}\n".format(pattern.get_pattern()))
return True
return False

View File

@@ -0,0 +1,39 @@
from d810.ast import AstNode, AstLeaf
def get_possible_patterns(ast, min_nb_use=2, ref_ast_info_by_index=None, max_nb_pattern=64):
# max_nb_pattern is used to prevent memory explosion when very large patterns are parsed
if ast.is_leaf():
return [ast]
if ref_ast_info_by_index is None:
if ast.ast_index not in ast.sub_ast_info_by_index.keys():
ast.compute_sub_ast()
ref_ast_info_by_index = ast.sub_ast_info_by_index
possible_patterns = []
if ref_ast_info_by_index[ast.ast_index].number_of_use >= min_nb_use:
node_as_leaf = AstLeaf("x_{0}".format(ast.ast_index))
node_as_leaf.mop = ast.mop
node_as_leaf.ast_index = ast.ast_index
possible_patterns.append(node_as_leaf)
left_patterns = []
right_patterns = []
if ast.left is not None:
left_patterns = get_possible_patterns(ast.left, min_nb_use, ref_ast_info_by_index, max_nb_pattern)
if ast.right is not None:
right_patterns = get_possible_patterns(ast.right, min_nb_use, ref_ast_info_by_index, max_nb_pattern)
for left_pattern in left_patterns:
if ast.right is not None:
for right_pattern in right_patterns:
node = AstNode(ast.opcode, left_pattern, right_pattern)
node.mop = ast.mop
node.ast_index = ast.ast_index
if len(possible_patterns) < max_nb_pattern:
possible_patterns.append(node)
else:
node = AstNode(ast.opcode, left_pattern)
node.mop = ast.mop
node.ast_index = ast.ast_index
if len(possible_patterns) < max_nb_pattern:
possible_patterns.append(node)
return possible_patterns

View File

@@ -0,0 +1,5 @@
from d810.utils import get_all_subclasses
from d810.optimizers.instructions.chain.handler import ChainSimplificationRule, ChainOptimizer
from d810.optimizers.instructions.chain.chain_rules import *
CHAIN_RULES = [x() for x in get_all_subclasses(ChainSimplificationRule)]

View File

@@ -0,0 +1,375 @@
import logging
from functools import reduce
from ida_hexrays import *
from d810.optimizers.instructions.chain.handler import ChainSimplificationRule
from d810.hexrays_helpers import equal_bnot_mop, equal_mops_ignore_size, \
SUB_TABLE, AND_TABLE
from d810.hexrays_formatters import format_minsn_t
rules_chain_logger = logging.getLogger('D810.rules.chain')
class ChainSimplification(object):
def __init__(self, opcode):
self.opcode = opcode
self.formatted_ins = ""
self.non_cst_mop_list = []
self.cst_mop_list = []
self._is_instruction_simplified = False
def add_mop(self, mop):
if (mop.t == mop_d) and (mop.d.opcode == self.opcode):
self.add_mop(mop.d.l)
self.add_mop(mop.d.r)
else:
if mop.t == mop_n:
self.cst_mop_list.append(mop)
else:
self.non_cst_mop_list.append(mop)
def do_simplification(self):
final_mop_list = self.get_simplified_non_constant()
final_mop_list += self.get_simplified_constant()
return final_mop_list
def get_simplified_constant(self):
if len(self.cst_mop_list) == 0:
return []
elif len(self.cst_mop_list) == 1:
return self.cst_mop_list
else:
cst_size_list = [c.size for c in self.cst_mop_list]
cst_value_list = [c.nnn.value for c in self.cst_mop_list]
final_cst_size = max(cst_size_list)
rules_chain_logger.debug("Doing cst simplification: {0}".format(cst_value_list))
self._is_instruction_simplified = True
if self.opcode == m_xor:
final_cst = reduce(lambda x, y: x ^ y, cst_value_list)
elif self.opcode == m_and:
final_cst = reduce(lambda x, y: x & y, cst_value_list)
elif self.opcode == m_or:
final_cst = reduce(lambda x, y: x | y, cst_value_list)
elif self.opcode == m_add:
final_cst = reduce(lambda x, y: x + y, cst_value_list)
else:
raise NotImplementedError("Euh")
final_cst = final_cst & AND_TABLE[final_cst_size]
rules_chain_logger.debug("Final cst: {0}".format(final_cst))
final_cst_mop = mop_t()
final_cst_mop.make_number(final_cst, max(cst_size_list))
return [final_cst_mop]
def get_simplified_non_constant(self):
if len(self.non_cst_mop_list) == 0:
return []
elif len(self.non_cst_mop_list) == 1:
return self.non_cst_mop_list
else:
is_always_0 = False
index_removed = []
for i in range(len(self.non_cst_mop_list)):
for j in range(i + 1, len(self.non_cst_mop_list)):
if (i not in index_removed) and (j not in index_removed):
if equal_mops_ignore_size(self.non_cst_mop_list[i], self.non_cst_mop_list[j]):
if self.opcode == m_xor:
# x ^ x == 0
rules_chain_logger.debug("Doing non cst simplification (xor): {0}, {1} in {2}"
.format(i, j, self.formatted_ins))
index_removed += [i, j]
elif self.opcode == m_and:
# x & x == x
rules_chain_logger.debug("Doing non cst simplification (and): {0}, {1} in {2}"
.format(i, j, self.formatted_ins))
index_removed += [j]
elif self.opcode == m_or:
# x | x == x
rules_chain_logger.debug("Doing non cst simplification (or): {0}, {1} in {2}"
.format(i, j, self.formatted_ins))
index_removed += [j]
elif equal_bnot_mop(self.non_cst_mop_list[i], self.non_cst_mop_list[j]):
if self.opcode == m_and:
is_always_0 = True
if len(index_removed) == 0 and not is_always_0:
return self.non_cst_mop_list
final_mop_list = []
self._is_instruction_simplified = True
if is_always_0:
final_mop_list.append(self.create_cst_mop(0, self.res_mop_size))
return final_mop_list
for i in range(len(self.non_cst_mop_list)):
if i not in index_removed:
final_mop_list.append(self.non_cst_mop_list[i])
return final_mop_list
def simplify(self, ins):
self.res_mop_size = ins.d.size
if ins.opcode != self.opcode:
return None
self.formatted_ins = format_minsn_t(ins)
self.non_cst_mop_list = []
self.cst_mop_list = []
self.add_mop(ins.l)
self.add_mop(ins.r)
self._is_instruction_simplified = False
final_mop_list = self.do_simplification()
if not self._is_instruction_simplified:
return None
return self.create_new_chain(ins, final_mop_list)
def create_new_chain(self, original_ins, mop_list):
new_ins = minsn_t(original_ins.ea)
new_ins.opcode = self.opcode
if len(mop_list) == 0:
mop_list.append(self.create_cst_mop(0, original_ins.d.size))
if len(mop_list) == 1:
mop_list.append(self.create_cst_mop(0, original_ins.d.size))
new_ins.l = self._create_mop_chain(original_ins, mop_list[:-1], original_ins.d.size)
new_ins.r = mop_list[-1]
if new_ins.r.t == mop_n:
new_ins.r.size = original_ins.d.size
new_ins.d = original_ins.d
return new_ins
def create_cst_mop(self, value, size):
cst_mop = mop_t()
cst_mop.make_number(value, size)
return cst_mop
def _create_mop_chain(self, ea, mop_list, size):
if len(mop_list) == 1:
return mop_list[0]
new_ins = minsn_t(ea)
new_ins.opcode = self.opcode
new_ins.l = self._create_mop_chain(ea, mop_list[:-1], size)
new_ins.r = mop_list[-1]
new_ins.d = mop_t()
new_ins.d.size = size
mop = mop_t()
mop.create_from_insn(new_ins)
return mop
class ArithmeticChainSimplification(object):
def __init__(self):
self.formatted_ins = ""
self.add_non_cst_mop_list = []
self.add_cst_mop_list = []
self.sub_non_cst_mop_list = []
self.sub_cst_mop_list = []
self.add_other_cst_list = []
self.sub_other_cst_list = []
self._is_instruction_simplified = False
def add_mop(self, sign, mop):
# sign is 0 if +, 1 is minus => minus minus = 1 ^ 1 = 0 so add
if (mop.t == mop_d) and (mop.d.opcode in [m_add, m_sub]):
self.add_mop(sign, mop.d.l)
if mop.d.opcode == m_add:
self.add_mop(sign, mop.d.r)
else:
self.add_mop(sign ^ 1, mop.d.r)
elif (mop.t == mop_d) and (mop.d.opcode == m_neg):
self.add_mop(sign ^ 1, mop.d.l)
else:
if mop.t == mop_n:
if sign == 0:
self.add_cst_mop_list.append(mop)
else:
self.sub_cst_mop_list.append(mop)
else:
if sign == 0:
self.add_non_cst_mop_list.append(mop)
else:
self.sub_non_cst_mop_list.append(mop)
def do_simplification(self):
final_add_cst_list, final_sub_cst_list = self.get_simplified_constant()
final_add_list, final_sub_list, final_add_cst_mop = self.get_simplified_non_constant()
if final_add_cst_mop.nnn.value != 0:
final_add_cst_list.append(final_add_cst_mop)
return final_add_list, final_sub_list, final_add_cst_list, final_sub_cst_list
def get_simplified_constant(self):
if len(self.add_cst_mop_list) == 0 and len(self.sub_cst_mop_list) == 0:
return [[], []]
if len(self.add_cst_mop_list) == 1 and len(self.sub_cst_mop_list) == 0:
return self.add_cst_mop_list, []
if len(self.add_cst_mop_list) == 0 and len(self.sub_cst_mop_list) == 1:
return [], self.sub_cst_mop_list
add_cst_size_list = [c.size for c in self.add_cst_mop_list]
add_cst_value_list = [c.nnn.value for c in self.add_cst_mop_list]
sub_cst_size_list = [c.size for c in self.sub_cst_mop_list]
sub_cst_value_list = [SUB_TABLE[c.size] - c.nnn.value for c in self.sub_cst_mop_list]
self._is_instruction_simplified = True
final_cst_size = max(add_cst_size_list + sub_cst_size_list)
rules_chain_logger.debug("Doing arithmetic cst simplification: {0} {1}"
.format(add_cst_value_list, sub_cst_value_list))
final_cst = reduce(lambda x, y: x + y, add_cst_value_list + sub_cst_value_list)
final_cst = final_cst & AND_TABLE[final_cst_size]
rules_chain_logger.debug("Final cst: {0}".format(final_cst))
final_cst_mop = mop_t()
final_cst_mop.make_number(final_cst, final_cst_size)
return [final_cst_mop], []
def get_simplified_non_constant(self):
if len(self.add_non_cst_mop_list) == 0 and len(self.sub_non_cst_mop_list) == 0:
return [[], []]
final_add_list = self.add_non_cst_mop_list
final_sub_list = self.sub_non_cst_mop_list
index_add_removed = []
index_sub_removed = []
for (i, add_mop) in enumerate(self.add_non_cst_mop_list):
for (j, sub_mop) in enumerate(self.sub_non_cst_mop_list):
if (i not in index_add_removed) and (j not in index_sub_removed):
if equal_mops_ignore_size(add_mop, sub_mop):
index_add_removed.append(i)
index_sub_removed.append(j)
if len(index_add_removed) > 0:
self._is_instruction_simplified = True
final_add_list = []
for i in range(len(self.add_non_cst_mop_list)):
if i not in index_add_removed:
final_add_list.append(self.add_non_cst_mop_list[i])
final_sub_list = []
for i in range(len(self.sub_non_cst_mop_list)):
if i not in index_sub_removed:
final_sub_list.append(self.sub_non_cst_mop_list[i])
final_add_list, final_sub_list, final_add_cst_mop = self.check_bnot_mop(final_add_list, final_sub_list)
return final_add_list, final_sub_list, final_add_cst_mop
def check_bnot_mop(self, add_non_cst_mop_list, sub_non_cst_mop_list):
add_index_removed = []
sub_index_removed = []
cst_value = 0
final_add_non_cst_mop_list = add_non_cst_mop_list
final_sub_non_cst_mop_list = sub_non_cst_mop_list
add_size_list = [c.size for c in add_non_cst_mop_list]
sub_size_list = [c.size for c in sub_non_cst_mop_list]
final_cst_size = max(add_size_list + sub_size_list)
for i in range(len(add_non_cst_mop_list)):
for j in range(i + 1, len(add_non_cst_mop_list)):
if (i not in add_index_removed) and (j not in add_index_removed):
if equal_bnot_mop(add_non_cst_mop_list[i], add_non_cst_mop_list[j]):
cst_value += AND_TABLE[add_non_cst_mop_list[i].size]
add_index_removed += [i, j]
for i in range(len(sub_non_cst_mop_list)):
for j in range(i + 1, len(sub_non_cst_mop_list)):
if (i not in sub_index_removed) and (j not in sub_index_removed):
if equal_bnot_mop(sub_non_cst_mop_list[i], sub_non_cst_mop_list[j]):
cst_value += 1
sub_index_removed += [i, j]
final_add_cst_mop = mop_t()
final_add_cst_mop.make_number(cst_value & AND_TABLE[final_cst_size], final_cst_size)
if len(add_index_removed) > 0:
final_add_non_cst_mop_list = []
self._is_instruction_simplified = True
for i in range(len(add_non_cst_mop_list)):
if i not in add_index_removed:
final_add_non_cst_mop_list.append(add_non_cst_mop_list[i])
if len(sub_index_removed) > 0:
final_sub_non_cst_mop_list = []
self._is_instruction_simplified = True
for i in range(len(sub_non_cst_mop_list)):
if i not in sub_index_removed:
final_sub_non_cst_mop_list.append(sub_non_cst_mop_list[i])
return final_add_non_cst_mop_list, final_sub_non_cst_mop_list, final_add_cst_mop
def simplify(self, ins):
if ins.opcode not in [m_add, m_sub]:
return None
self.formatted_ins = format_minsn_t(ins)
self.add_non_cst_mop_list = []
self.add_cst_mop_list = []
self.sub_non_cst_mop_list = []
self.sub_cst_mop_list = []
self.add_mop(0, ins.l)
if ins.opcode == m_add:
self.add_mop(0, ins.r)
else:
self.add_mop(1, ins.r)
self._is_instruction_simplified = False
final_add_list, final_sub_list, final_add_cst_list, final_sub_cst_list = self.do_simplification()
if not self._is_instruction_simplified:
return None
simplified_ins = self.create_new_chain(ins, final_add_list, final_sub_list, final_add_cst_list, final_sub_cst_list)
return simplified_ins
def create_new_chain(self, original_ins, final_add_list, final_sub_list, final_add_cst_list, final_sub_cst_list):
mod_add = self._create_mop_add_chain(original_ins.ea, final_add_list + final_add_cst_list, original_ins.d.size)
mod_sub = self._create_mop_add_chain(original_ins.ea, final_sub_list + final_sub_cst_list, original_ins.d.size)
new_ins = minsn_t(original_ins.ea)
new_ins.opcode = m_sub
new_ins.l = mod_add
new_ins.r = mod_sub
new_ins.d = original_ins.d
return new_ins
def _create_mop_add_chain(self, ea, mop_list, size):
if len(mop_list) == 0:
res = mop_t()
res.make_number(0, size)
return res
elif len(mop_list) == 1:
return mop_list[0]
new_ins = minsn_t(ea)
new_ins.opcode = m_add
new_ins.l = self._create_mop_add_chain(ea, mop_list[:-1], size)
new_ins.r = mop_list[-1]
new_ins.d = mop_t()
new_ins.d.size = size
mop = mop_t()
mop.create_from_insn(new_ins)
return mop
class XorChain(ChainSimplificationRule):
DESCRIPTION = "Remove XOR chains with common terms. E.g. x ^ 4 ^ y ^ 6 ^ 5 ^ x ==> y ^ 7"
def check_and_replace(self, blk, ins):
xor_simplifier = ChainSimplification(m_xor)
new_ins = xor_simplifier.simplify(ins)
return new_ins
class AndChain(ChainSimplificationRule):
DESCRIPTION = "Remove AND chains with common terms. E.g. x & 4 & y & 6 & 5 & x ==> x & y & 4"
def check_and_replace(self, blk, ins):
and_simplifier = ChainSimplification(m_and)
new_ins = and_simplifier.simplify(ins)
return new_ins
class OrChain(ChainSimplificationRule):
DESCRIPTION = "Remove OR chains with common terms. E.g. x | 4 | y | 6 | 5 | x ==> x | y | 7"
def check_and_replace(self, blk, ins):
or_simplifier = ChainSimplification(m_or)
new_ins = or_simplifier.simplify(ins)
return new_ins
class ArithmeticChain(ChainSimplificationRule):
DESCRIPTION = "Remove arithmetic chains with common terms. E.g. x + 4 + y - (6 + x - 5) ==> y + 3"
def check_and_replace(self, blk, ins):
arithmetic_simplifier = ArithmeticChainSimplification()
new_ins = arithmetic_simplifier.simplify(ins)
return new_ins

View File

@@ -0,0 +1,9 @@
from d810.optimizers.instructions.handler import InstructionOptimizationRule, InstructionOptimizer
class ChainSimplificationRule(InstructionOptimizationRule):
pass
class ChainOptimizer(InstructionOptimizer):
RULE_CLASSES = [ChainSimplificationRule]

View File

@@ -0,0 +1,5 @@
from d810.utils import get_all_subclasses
from d810.optimizers.instructions.early.handler import EarlyRule, EarlyOptimizer
from d810.optimizers.instructions.early.mem_read import *
EARLY_RULES = [x() for x in get_all_subclasses(EarlyRule)]

View File

@@ -0,0 +1,9 @@
from d810.optimizers.instructions.handler import GenericPatternRule, InstructionOptimizer
class EarlyRule(GenericPatternRule):
pass
class EarlyOptimizer(InstructionOptimizer):
RULE_CLASSES = [EarlyRule]

View File

@@ -0,0 +1,73 @@
from ida_hexrays import *
from idaapi import SEGPERM_READ, SEGPERM_WRITE, xrefblk_t, getseg, segment_t, XREF_DATA, dr_W, is_loaded
from d810.optimizers.instructions.early.handler import EarlyRule
from d810.ast import AstLeaf, AstConstant, AstNode
class SetGlobalVariablesToZero(EarlyRule):
DESCRIPTION = "This rule can be used to patch memory read"
PATTERN = AstNode(m_mov, AstLeaf("ro_dword"))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res"))
def __init__(self):
super().__init__()
self.ro_dword_min_ea = None
self.ro_dword_max_ea = None
def configure(self, kwargs):
super().configure(kwargs)
self.ro_dword_min_ea = None
self.ro_dword_max_ea = None
if "ro_dword_min_ea" in kwargs.keys():
self.ro_dword_min_ea = int(kwargs["ro_dword_min_ea"], 16)
if "ro_dword_max_ea" in kwargs.keys():
self.ro_dword_max_ea = int(kwargs["ro_dword_max_ea"], 16)
def check_candidate(self, candidate):
if (self.ro_dword_min_ea is None) or (self.ro_dword_max_ea is None):
return False
if candidate["ro_dword"].mop.t != mop_v:
return False
mem_read_address = candidate["ro_dword"].mop.g
if not(self.ro_dword_min_ea <= mem_read_address <= self.ro_dword_max_ea):
return False
candidate.add_constant_leaf("val_res", 0, candidate["ro_dword"].mop.size)
return True
# This rule is from
# https://www.carbonblack.com/blog/defeating-compiler-level-obfuscations-used-in-apt10-malware/
class SetGlobalVariablesToZeroIfDetectedReadOnly(EarlyRule):
DESCRIPTION = "WARNING: Use it only if you know what you are doing as it may patch data not related to obfuscation"
PATTERN = AstNode(m_mov, AstLeaf("ro_dword"))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res"))
def is_read_only_inited_var(self, address):
s: segment_t = getseg(address)
if s is None:
return False
if s.perm != (SEGPERM_READ | SEGPERM_WRITE):
return False
if is_loaded(address):
return False
ref_finder = xrefblk_t()
is_ok = ref_finder.first_to(address, XREF_DATA)
while is_ok:
if ref_finder.type == dr_W:
return False
is_ok = ref_finder.next_to()
return True
def check_candidate(self, candidate):
if candidate["ro_dword"].mop.t != mop_v:
return False
mem_read_address = candidate["ro_dword"].mop.g
if not self.is_read_only_inited_var(mem_read_address):
return False
candidate.add_constant_leaf("val_res", 0, candidate["ro_dword"].mop.size)
return True

View File

@@ -0,0 +1,138 @@
from __future__ import annotations
import logging
from typing import List
from ida_hexrays import *
from d810.optimizers.handler import OptimizationRule, DEFAULT_INSTRUCTION_MATURITIES
from d810.hexrays_formatters import format_minsn_t
from d810.ast import minsn_to_ast, AstNode
from d810.errors import D810Exception
d810_logger = logging.getLogger('D810')
optimizer_logger = logging.getLogger('D810.optimizer')
class InstructionOptimizationRule(OptimizationRule):
def __init__(self):
super().__init__()
self.maturities = DEFAULT_INSTRUCTION_MATURITIES
def check_and_replace(self, blk, ins):
return None
class GenericPatternRule(InstructionOptimizationRule):
PATTERN = None
PATTERNS = None
REPLACEMENT_PATTERN = None
def __init__(self):
super().__init__()
self.pattern_candidates = [self.PATTERN]
if self.PATTERNS is not None:
self.pattern_candidates += self.PATTERNS
def check_candidate(self, candidate: AstNode):
# Perform rule specific checks
return False
def get_valid_candidates(self, instruction: minsn_t, stop_early=True):
valid_candidates = []
tmp = minsn_to_ast(instruction)
if tmp is None:
return []
for candidate_pattern in self.pattern_candidates:
if not candidate_pattern.check_pattern_and_copy_mops(tmp):
continue
if not self.check_candidate(candidate_pattern):
continue
valid_candidates.append(candidate_pattern)
if stop_early:
return valid_candidates
return []
def get_replacement(self, candidate: AstNode):
is_ok = self.REPLACEMENT_PATTERN.update_leafs_mop(candidate)
if not is_ok:
return None
new_ins = self.REPLACEMENT_PATTERN.create_minsn(candidate.ea, candidate.dst_mop)
return new_ins
def check_and_replace(self, blk: mblock_t, instruction: minsn_t):
valid_candidates = self.get_valid_candidates(instruction, stop_early=True)
if len(valid_candidates) == 0:
return None
new_instruction = self.get_replacement(valid_candidates[0])
return new_instruction
@property
def description(self):
if self.DESCRIPTION is not None:
return self.DESCRIPTION
if (self.PATTERN is None) or (self.REPLACEMENT_PATTERN is None):
return ""
self.PATTERN.reset_mops()
self.REPLACEMENT_PATTERN.reset_mops()
return "{0} => {1}".format(self.PATTERN, self.REPLACEMENT_PATTERN)
class InstructionOptimizer(object):
RULE_CLASSES = []
NAME = None
def __init__(self, maturities: List[int], log_dir=None):
self.rules = set()
self.rules_usage_info = {}
self.maturities = maturities
self.log_dir = log_dir
self.cur_maturity = MMAT_PREOPTIMIZED
def add_rule(self, rule: InstructionOptimizationRule):
is_valid_rule_class = False
for rule_class in self.RULE_CLASSES:
if isinstance(rule, rule_class):
is_valid_rule_class = True
break
if not is_valid_rule_class:
return False
optimizer_logger.debug("Adding rule {0}".format(rule))
self.rules.add(rule)
self.rules_usage_info[rule.name] = 0
return True
def reset_rule_usage_statistic(self):
self.rules_usage_info = {}
for rule in self.rules:
self.rules_usage_info[rule.name] = 0
def show_rule_usage_statistic(self):
for rule_name, rule_nb_match in self.rules_usage_info.items():
if rule_nb_match > 0:
d810_logger.info("Instruction Rule '{0}' has been used {1} times".format(rule_name, rule_nb_match))
def get_optimized_instruction(self, blk: mblock_t, ins: minsn_t):
if blk is not None:
self.cur_maturity = blk.mba.maturity
if self.cur_maturity not in self.maturities:
return None
for rule in self.rules:
try:
new_ins = rule.check_and_replace(blk, ins)
if new_ins is not None:
self.rules_usage_info[rule.name] += 1
optimizer_logger.info("Rule {0} matched:".format(rule.name))
optimizer_logger.info(" orig: {0}".format(format_minsn_t(ins)))
optimizer_logger.info(" new : {0}".format(format_minsn_t(new_ins)))
return new_ins
except RuntimeError as e:
optimizer_logger.error("Runtime error during rule {0} for instruction {1}: {2}".format(rule, format_minsn_t(ins), e))
except D810Exception as e:
optimizer_logger.error("D810Exception during rule {0} for instruction {1}: {2}".format(rule, format_minsn_t(ins), e))
return None
@property
def name(self):
if self.NAME is not None:
return self.NAME
return self.__class__.__name__

View File

@@ -0,0 +1,18 @@
from d810.utils import get_all_subclasses
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule, PatternOptimizer
from d810.optimizers.instructions.pattern_matching.rewrite_add import *
from d810.optimizers.instructions.pattern_matching.rewrite_and import *
from d810.optimizers.instructions.pattern_matching.rewrite_bnot import *
from d810.optimizers.instructions.pattern_matching.rewrite_cst import *
from d810.optimizers.instructions.pattern_matching.rewrite_mov import *
from d810.optimizers.instructions.pattern_matching.rewrite_mul import *
from d810.optimizers.instructions.pattern_matching.rewrite_neg import *
from d810.optimizers.instructions.pattern_matching.rewrite_predicates import *
from d810.optimizers.instructions.pattern_matching.rewrite_or import *
from d810.optimizers.instructions.pattern_matching.rewrite_sub import *
from d810.optimizers.instructions.pattern_matching.rewrite_xor import *
from d810.optimizers.instructions.pattern_matching.weird import *
PATTERN_MATCHING_RULES = [x() for x in get_all_subclasses(PatternMatchingRule)]

View File

@@ -0,0 +1,340 @@
import logging
import itertools
from ida_hexrays import *
from typing import List, Union
from d810.optimizers.instructions.handler import GenericPatternRule, InstructionOptimizer, InstructionOptimizationRule
from d810.ast import minsn_to_ast, AstNode, AstLeaf
from d810.hexrays_formatters import format_minsn_t, format_mop_t
optimizer_logger = logging.getLogger('D810.optimizer')
pattern_search_logger = logging.getLogger('D810.pattern_search')
class PatternMatchingRule(GenericPatternRule):
PATTERN = None
PATTERNS = None
FUZZ_PATTERN = True
REPLACEMENT_PATTERN = None
def __init__(self):
super().__init__()
self.fuzz_pattern = self.FUZZ_PATTERN
def configure(self, fuzz_pattern=None, **kwargs):
super().configure(kwargs)
if fuzz_pattern is not None:
self.fuzz_pattern = fuzz_pattern
self._generate_pattern_candidates()
pattern_search_logger.debug("Rule {0} configured with {1} patterns"
.format(self.__class__.__name__, len(self.pattern_candidates)))
def _generate_pattern_candidates(self):
self.fuzz_pattern = self.FUZZ_PATTERN
if self.PATTERN is not None:
self.PATTERN.reset_mops()
if not self.fuzz_pattern:
if self.PATTERN is not None:
self.pattern_candidates = [self.PATTERN]
if self.PATTERNS is not None:
self.pattern_candidates += [x for x in self.PATTERNS]
else:
self.pattern_candidates = [x for x in self.PATTERNS]
else:
self.pattern_candidates = ast_generator(self.PATTERN)
def check_candidate(self, candidate: AstNode):
return True
def check_pattern_and_replace(self, candidate_pattern: AstNode, test_ast: AstNode):
if not candidate_pattern.check_pattern_and_copy_mops(test_ast):
return None
if not self.check_candidate(candidate_pattern):
return None
new_instruction = self.get_replacement(candidate_pattern)
return new_instruction
class RulePatternInfo(object):
def __init__(self, rule, pattern):
self.rule = rule
self.pattern = pattern
def signature_generator(ref_sig):
for i, x in enumerate(ref_sig):
if x not in ["N", "L"]:
for sig_suffix in signature_generator(ref_sig[i + 1:]):
yield ref_sig[:i] + ["L"] + sig_suffix
yield ref_sig
class PatternStorage(object):
# The PatternStorage object is used to store patterns associated to rules
# A PatternStorage contains a dictionary (next_layer_patterns) where:
# - keys are the signature of a pattern at a specific depth (i.e. the opcodes, the variable and constant)
# - values are PatternStorage object for the next depth
# Additionally, it stores the rule objects which are resolved for the PatternStorage depth
def __init__(self, depth=1):
self.depth = depth
self.next_layer_patterns = {}
self.rule_resolved = []
def add_pattern_for_rule(self, pattern: AstNode, rule: InstructionOptimizationRule):
layer_signature = self.layer_signature_to_key(pattern.get_depth_signature(self.depth))
if len(layer_signature.replace(",", "")) == (layer_signature.count("N")):
self.rule_resolved.append(RulePatternInfo(rule, pattern))
else:
if layer_signature not in self.next_layer_patterns.keys():
self.next_layer_patterns[layer_signature] = PatternStorage(self.depth + 1)
self.next_layer_patterns[layer_signature].add_pattern_for_rule(pattern, rule)
@staticmethod
def layer_signature_to_key(sig: List[str]) -> str:
return ",".join(sig)
@staticmethod
def is_layer_signature_compatible(instruction_signature: str, pattern_signature: str) -> bool:
if instruction_signature == pattern_signature:
return True
instruction_node_list = instruction_signature.split(",")
pattern_node_list = pattern_signature.split(",")
for ins_node_sig, pattern_node_sig in zip(instruction_node_list, pattern_node_list):
if pattern_node_sig not in ["L", "C", "N"] and ins_node_sig != pattern_node_sig:
return False
return True
def get_matching_rule_pattern_info(self, pattern: AstNode):
pattern_search_logger.info("Searching : {0}".format(pattern))
return self.explore_one_level(pattern, 1)
def explore_one_level(self, searched_pattern: AstNode, cur_level: int):
# We need to check if searched_pattern is in self.next_layer_patterns
# Easy solution: try/except self.next_layer_patterns[searched_pattern]
# Problem is that known patterns may not exactly match the microcode instruction, e.g.
# -> Pattern layer 3 signature is ["L", "N", "15", "L"]
# -> Multiple instruction can match that: ["L", "N", "15", "L"], ["C", "N", "15", "L"], ["C", "N", "15", "13"]
# This piece of code tries to handles that in a (semi) efficient way
if len(self.next_layer_patterns) == 0:
return []
searched_layer_signature = searched_pattern.get_depth_signature(cur_level)
nb_possible_signature = 2 ** (len(searched_layer_signature) - searched_layer_signature.count("N") - \
searched_layer_signature.count("L"))
pattern_search_logger.debug(" Layer {0}: {1} -> {2} variations (storage has {3} signature)"
.format(cur_level, searched_layer_signature, nb_possible_signature,
len(self.next_layer_patterns)))
matched_rule_pattern_info = []
if nb_possible_signature < len(self.next_layer_patterns):
pattern_search_logger.debug(" => Using method 1")
for possible_sig in signature_generator(searched_layer_signature):
try:
test_sig = self.layer_signature_to_key(possible_sig)
pattern_storage = self.next_layer_patterns[test_sig]
pattern_search_logger.info(" Compatible signature: {0} -> resolved: {1}"
.format(test_sig, pattern_storage.rule_resolved))
matched_rule_pattern_info += pattern_storage.rule_resolved
matched_rule_pattern_info += pattern_storage.explore_one_level(searched_pattern, cur_level + 1)
except KeyError:
pass
else:
pattern_search_logger.debug(" => Using method 2")
searched_layer_signature_key = self.layer_signature_to_key(searched_layer_signature)
for test_sig, pattern_storage in self.next_layer_patterns.items():
if self.is_layer_signature_compatible(searched_layer_signature_key, test_sig):
pattern_search_logger.info(" Compatible signature: {0} -> resolved: {1}"
.format(test_sig, pattern_storage.rule_resolved))
matched_rule_pattern_info += pattern_storage.rule_resolved
matched_rule_pattern_info += pattern_storage.explore_one_level(searched_pattern, cur_level + 1)
return matched_rule_pattern_info
class PatternOptimizer(InstructionOptimizer):
# The main idea of PatternOptimizer is to generate/store all possible patterns associated to all known rules in a $
# dictionary-like object (PatternStorage) when the plugin is loaded.
# => it means that we generate a very large number of patterns
#
# At runtime, we transform the microcode instruction in a list of keys that we search in the PatternStorage object
# to speed up the checks
# => we don't want to test all patterns, so we use the PatternStorage object to (quickly) get the patterns
# which have the same shape as the microcode instruction
RULE_CLASSES = [PatternMatchingRule]
def __init__(self, maturities, log_dir=None):
super().__init__(maturities, log_dir=log_dir)
self.pattern_storage = PatternStorage(depth=1)
def add_rule(self, rule: InstructionOptimizationRule):
is_ok = super().add_rule(rule)
if not is_ok:
return False
for pattern in rule.pattern_candidates:
self.pattern_storage.add_pattern_for_rule(pattern, rule)
return True
def get_optimized_instruction(self, blk: mblock_t, ins: minsn_t) -> Union[None, minsn_t]:
if blk is not None:
self.cur_maturity = blk.mba.maturity
if self.cur_maturity not in self.maturities:
return None
tmp = minsn_to_ast(ins)
if tmp is None:
return None
all_matchs = self.pattern_storage.get_matching_rule_pattern_info(tmp)
for rule_pattern_info in all_matchs:
try:
new_ins = rule_pattern_info.rule.check_pattern_and_replace(rule_pattern_info.pattern, tmp)
if new_ins is not None:
self.rules_usage_info[rule_pattern_info.rule.name] += 1
optimizer_logger.info("Rule {0} matched:".format(rule_pattern_info.rule.name))
optimizer_logger.info(" orig: {0}".format(format_minsn_t(ins)))
optimizer_logger.info(" new : {0}".format(format_minsn_t(new_ins)))
return new_ins
except RuntimeError as e:
optimizer_logger.error("Error during rule {0} for instruction {1}: {2}"
.format(rule_pattern_info.rule, format_minsn_t(ins), e))
return None
# AST equivalent pattern generation stuff
# TODO: refactor/clean this
def rec_get_all_binary_subtree_representation(elt_list):
if len(elt_list) == 1:
return elt_list
if len(elt_list) == 2:
return [elt_list]
tmp_res = []
for i in range(1, len(elt_list)):
left_list = rec_get_all_binary_subtree_representation(elt_list[:i])
right_list = rec_get_all_binary_subtree_representation(elt_list[i:])
for l in left_list:
for r in right_list:
tmp_res.append([l, r])
return tmp_res
def rec_get_all_binary_tree_representation(elt_list):
if len(elt_list) <= 1:
return elt_list
tmp = list(itertools.permutations(elt_list))
tmp2 = []
for perm_tmp in tmp:
tmp2 += rec_get_all_binary_subtree_representation(perm_tmp)
return tmp2
def get_all_binary_tree_representation(all_elt):
tmp = rec_get_all_binary_tree_representation(all_elt)
return tmp
def generate_ast(opcode, leafs):
if isinstance(leafs, AstLeaf):
return leafs
if isinstance(leafs, AstNode):
return leafs
if len(leafs) == 1:
return leafs[0]
if len(leafs) == 2:
return AstNode(opcode, generate_ast(opcode, leafs[0]), generate_ast(opcode, leafs[1]))
def get_addition_operands(ast_node):
if not isinstance(ast_node, AstNode):
return [ast_node]
if ast_node.opcode == m_add:
return get_addition_operands(ast_node.left) + get_addition_operands(ast_node.right)
elif ast_node.opcode == m_sub:
tmp = get_addition_operands(ast_node.left)
for aaa in get_addition_operands(ast_node.right):
tmp.append(AstNode(m_neg, aaa))
return tmp
else:
return [ast_node]
def get_opcode_operands(ref_opcode, ast_node):
if not isinstance(ast_node, AstNode):
return [ast_node]
if ast_node.opcode == ref_opcode:
return get_opcode_operands(ref_opcode, ast_node.left) + get_opcode_operands(ref_opcode, ast_node.right)
else:
return [ast_node]
def get_similar_opcode_operands(ast_node):
if ast_node.opcode in [m_add, m_sub]:
add_elts = get_addition_operands(ast_node)
all_add_ordering = get_all_binary_tree_representation(add_elts)
ast_res = []
for leaf_ordering in all_add_ordering:
ast_res.append(generate_ast(m_add, leaf_ordering))
return ast_res
elif ast_node.opcode in [m_xor, m_or, m_and, m_mul]:
same_elts = get_opcode_operands(ast_node.opcode, ast_node)
all_same_ordering = get_all_binary_tree_representation(same_elts)
ast_res = []
for leaf_ordering in all_same_ordering:
ast_res.append(generate_ast(ast_node.opcode, leaf_ordering))
return ast_res
else:
return [ast_node]
def get_ast_variations_with_add_sub(opcode, left, right):
possible_ast = [AstNode(opcode, left, right)]
if opcode == m_add:
if isinstance(left, AstNode) and isinstance(right, AstNode):
if (left.opcode == m_neg) and (right.opcode == m_neg):
possible_ast.append(AstNode(m_neg, AstNode(m_add, left.left, right.left)))
if isinstance(right, AstNode) and (right.opcode == m_neg):
possible_ast.append(AstNode(m_sub, left, right.left))
return possible_ast
def ast_generator(ast_node, excluded_opcodes=None):
if not isinstance(ast_node, AstNode):
return [ast_node]
res_ast = []
excluded_opcodes = excluded_opcodes if excluded_opcodes is not None else []
if ast_node.opcode not in excluded_opcodes:
if ast_node.opcode in [m_add, m_sub]:
similar_ast_list = get_similar_opcode_operands(ast_node)
for similar_ast in similar_ast_list:
sub_ast_left_list = ast_generator(similar_ast.left, excluded_opcodes=[m_add, m_sub])
sub_ast_right_list = ast_generator(similar_ast.right, excluded_opcodes=[m_add, m_sub])
for sub_ast_left in sub_ast_left_list:
for sub_ast_right in sub_ast_right_list:
res_ast += get_ast_variations_with_add_sub(m_add, sub_ast_left, sub_ast_right)
return res_ast
if ast_node.opcode in [m_xor, m_or, m_and, m_mul]:
similar_ast_list = get_similar_opcode_operands(ast_node)
for similar_ast in similar_ast_list:
sub_ast_left_list = ast_generator(similar_ast.left, excluded_opcodes=[ast_node.opcode])
sub_ast_right_list = ast_generator(similar_ast.right, excluded_opcodes=[ast_node.opcode])
for sub_ast_left in sub_ast_left_list:
for sub_ast_right in sub_ast_right_list:
res_ast += get_ast_variations_with_add_sub(ast_node.opcode, sub_ast_left, sub_ast_right)
return res_ast
if ast_node.opcode not in [m_add, m_sub, m_or, m_and, m_mul]:
excluded_opcodes = []
nb_operands = 0
if ast_node.left is not None:
nb_operands += 1
if ast_node.right is not None:
nb_operands += 1
if nb_operands == 1:
sub_ast_list = ast_generator(ast_node.left, excluded_opcodes=excluded_opcodes)
for sub_ast in sub_ast_list:
res_ast.append(AstNode(ast_node.opcode, sub_ast))
return res_ast
if nb_operands == 2:
sub_ast_left_list = ast_generator(ast_node.left, excluded_opcodes=excluded_opcodes)
sub_ast_right_list = ast_generator(ast_node.right, excluded_opcodes=excluded_opcodes)
for sub_ast_left in sub_ast_left_list:
for sub_ast_right in sub_ast_right_list:
res_ast += get_ast_variations_with_add_sub(ast_node.opcode, sub_ast_left, sub_ast_right)
return res_ast
return []

View File

@@ -0,0 +1,243 @@
from ida_hexrays import *
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.hexrays_helpers import equal_ignore_msb_cst, equal_bnot_mop, AND_TABLE
class Add_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstLeaf("x_0"),
AstNode(m_sub,
AstNode(m_bnot,
AstLeaf("x_1")),
AstConstant("1", 1)))
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstLeaf("x_1"))
class Add_HackersDelightRule_2(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstLeaf("x_1"))
class Add_HackersDelightRule_3(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstLeaf("x_1"))
class Add_HackersDelightRule_4(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1"))),
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstLeaf("x_1"))
class Add_HackersDelightRule_5(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_or,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstLeaf("x_2"))),
AstNode(m_xor,
AstLeaf("x_0"),
AstNode(m_or,
AstLeaf("x_1"),
AstLeaf("x_2"))))
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstNode(m_or, AstLeaf("x_1"), AstLeaf("x_2")))
class Add_SpecialConstantRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_xor,
AstLeaf("x_0"),
AstConstant("c_1")),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_2"))))
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstConstant("c_1"))
def check_candidate(self, candidate):
return equal_ignore_msb_cst(candidate["c_1"].mop, candidate["c_2"].mop)
class Add_SpecialConstantRule_2(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_xor,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("val_ff", 0xff)),
AstConstant("c_1")),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_2"))))
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstConstant("c_1"))
def check_candidate(self, candidate):
return (candidate["c_1"].value & 0xff) == candidate["c_2"].value
class Add_SpecialConstantRule_3(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_xor,
AstLeaf("x_0"),
AstConstant("c_1")),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_or,
AstLeaf("x_0"),
AstConstant("c_2"))))
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstConstant("val_res"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["c_1"].mop, candidate["c_2"].mop):
return False
candidate.add_constant_leaf("val_res", candidate["c_2"].value - 1, candidate["x_0"].size)
return True
class Add_OllvmRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_bnot,
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_1'))),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_or,
AstLeaf('x_1'),
AstLeaf('x_0'))))
REPLACEMENT_PATTERN = AstNode(m_sub,
AstNode(m_add,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstConstant("val_1"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_1", 1, candidate.size)
return True
class Add_OllvmRule_2(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_bnot,
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_1'))),
AstNode(m_mul,
AstConstant("val_fe"),
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('x_1'))))
REPLACEMENT_PATTERN = AstNode(m_sub,
AstNode(m_add,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstConstant("val_1"))
def check_candidate(self, candidate):
if (candidate["val_fe"].value + 2) & AND_TABLE[candidate["val_fe"].size] != 0:
return False
candidate.add_constant_leaf("val_1", 1, candidate.size)
return True
class Add_OllvmRule_3(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1'))))
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf('x_0'), AstLeaf('x_1'))
class Add_OllvmRule_4(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_mul,
AstConstant("val_fe"),
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1'))))
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf('x_0'), AstLeaf('x_1'))
class AddXor_Rule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_sub,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("bnot_x_1"))))
REPLACEMENT_PATTERN = AstNode(m_add,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstConstant("val_2"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
candidate.add_constant_leaf("val_2", 2, candidate["x_0"].size)
return True
class AddXor_Rule_2(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_sub,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_bnot,
AstNode(m_and,
AstLeaf("bnot_x_0"),
AstLeaf("x_1")))))
REPLACEMENT_PATTERN = AstNode(m_add,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstLeaf("val_2"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
candidate.add_constant_leaf("val_2", 2, candidate["x_0"].size)
return True

View File

@@ -0,0 +1,253 @@
from ida_hexrays import *
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.hexrays_helpers import equal_bnot_mop, SUB_TABLE
class And_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_or,
AstNode(m_bnot,
AstLeaf("x_0")),
AstLeaf("x_1")),
AstNode(m_bnot, AstLeaf("x_0")))
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1"))
class And_HackersDelightRule_2(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_or,
AstLeaf("bnot_x_0"),
AstLeaf("x_1")),
AstNode(m_add,
AstLeaf("x_0"),
AstConstant("1", 1)))
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
return equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop)
class And_HackersDelightRule_3(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_add,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1"))
class And_HackersDelightRule_4(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_1')))
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf('x_0'), AstLeaf('x_1'))
class And_OllvmRule_1(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_bnot,
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_1'))))
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf('x_0'), AstLeaf('x_1'))
class And_OllvmRule_2(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('bnot_x_1')))
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf('x_0'), AstLeaf('x_1'))
def check_candidate(self, candidate):
return equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop)
class And_OllvmRule_3(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_bnot,
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_1'))))
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf('x_0'), AstLeaf('x_1'))
class And_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("bnot_x_1")),
AstLeaf("x_1"))
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
return equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop)
class And_FactorRule_2(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstLeaf('x_0'),
AstNode(m_bnot,
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_1'))))
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf('x_0'), AstLeaf('x_1'))
class AndBnot_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstLeaf("x_1"))
REPLACEMENT_PATTERN = AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_bnot, AstLeaf("x_1")))
class AndBnot_HackersDelightRule_2(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstLeaf("x_0"),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_bnot, AstLeaf("x_1")))
class AndBnot_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstLeaf("x_0"),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_bnot, AstLeaf("x_1")))
class AndBnot_FactorRule_2(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_bnot, AstLeaf("x_1")))
class AndBnot_FactorRule_3(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstLeaf("x_1"))
REPLACEMENT_PATTERN = AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_bnot, AstLeaf("x_1")))
class AndBnot_FactorRule_4(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_xor,
AstLeaf('x_1'),
AstLeaf('x_0')),
AstNode(m_bnot,
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('bnot_x_1'))))
REPLACEMENT_PATTERN = AstNode(m_and,
AstLeaf("x_1"),
AstNode(m_bnot, AstLeaf("x_0")))
def check_candidate(self, candidate):
return equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop)
class AndOr_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_2")),
AstNode(m_and,
AstLeaf("x_1"),
AstLeaf("x_2")))
REPLACEMENT_PATTERN = AstNode(m_and,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstLeaf("x_2"))
class AndXor_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_2")),
AstNode(m_and,
AstLeaf("x_1"),
AstLeaf("x_2")))
REPLACEMENT_PATTERN = AstNode(m_and,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstLeaf("x_2"))
class And1_MbaRule_1(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_mul, AstLeaf("x_0"), AstLeaf("x_0")),
AstConstant("3", 3))
REPLACEMENT_PATTERN = AstNode(m_and,
AstLeaf("x_0"),
AstConstant("val_1"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_1", 1, candidate.size)
return True
class AndGetUpperBits_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_mul,
AstConstant("c_1"),
AstNode(m_and,
AstNode(m_shr,
AstLeaf('x_0'),
AstConstant("c_2")),
AstConstant("c_3")))
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf('x_0'), AstConstant("c_res"))
def check_candidate(self, candidate):
if (2 ** candidate["c_2"].value) != candidate["c_1"].value:
return False
c_res = (SUB_TABLE[candidate["c_1"].size] - candidate["c_1"].value) & candidate["c_3"].value
candidate.add_constant_leaf("c_res", c_res, candidate["x_0"].size)
return True

View File

@@ -0,0 +1,272 @@
from ida_hexrays import *
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.hexrays_helpers import equal_bnot_mop, SUB_TABLE
class Bnot_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_neg,
AstLeaf("x_0")),
AstConstant("1", 1))
REPLACEMENT_PATTERN = AstNode(m_bnot, AstLeaf("x_0"))
class Bnot_HackersDelightRule_2(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_bnot,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1"))),
AstNode(m_bnot,
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_bnot, AstLeaf("x_1"))
class Bnot_MbaRule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_sub,
AstLeaf("x_0"),
AstConstant("1", 1)),
AstNode(m_mul,
AstConstant("2", 2),
AstLeaf("x_0")))
REPLACEMENT_PATTERN = AstNode(m_bnot, AstLeaf("x_0"))
class Bnot_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstNode(m_bnot,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1"))),
AstLeaf("x_1"))
REPLACEMENT_PATTERN = AstNode(m_bnot, AstLeaf("x_0"))
class Bnot_FactorRule_2(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstConstant("minus_1"),
AstLeaf("x_0"))
REPLACEMENT_PATTERN = AstNode(m_bnot, AstLeaf("x_0"))
def check_candidate(self, candidate):
if candidate["minus_1"].value != SUB_TABLE[candidate["minus_1"].size] - 1:
return False
return True
class Bnot_FactorRule_3(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('bnot_x_1')))
REPLACEMENT_PATTERN = AstNode(m_bnot, AstLeaf("x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class Bnot_FactorRule_4(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstNode(m_bnot, AstLeaf('x_0')),
AstNode(m_bnot, AstLeaf('x_1')))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf('x_0'), AstLeaf("x_1"))
class BnotXor_Rule_1(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_and,
AstLeaf("bnot_x_0"),
AstLeaf("bnot_x_1")))
REPLACEMENT_PATTERN = AstNode(m_bnot,
AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1")))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class BnotXor_Rule_2(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_or,
AstLeaf('bnot_x_0'),
AstLeaf('bnot_x_1')))
REPLACEMENT_PATTERN = AstNode(m_bnot,
AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1")))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class BnotXor_Rule_3(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('bnot_x_1')),
AstNode(m_or,
AstLeaf('bnot_x_0'),
AstLeaf('x_1')))
REPLACEMENT_PATTERN = AstNode(m_bnot,
AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1")))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class BnotXor_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstLeaf("x_0"),
AstNode(m_bnot,
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_bnot,
AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1")))
class BnotAnd_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_bnot,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_bnot,
AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1")))
class BnotAnd_FactorRule_2(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_or,
AstLeaf("bnot_x_0"),
AstLeaf("bnot_x_1")),
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_bnot,
AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1")))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class BnotAnd_FactorRule_3(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_bnot,
AstLeaf("x_0")),
AstNode(m_bnot,
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_bnot,
AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1")))
class BnotAnd_FactorRule_4(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstLeaf("bnot_x_0"),
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_bnot,
AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1")))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
return True
class BnotOr_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_bnot,
AstLeaf("x_0")),
AstNode(m_bnot,
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_bnot,
AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1")))
class BnotAdd_MbaRule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("bnot_x_1")),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_bnot,
AstNode(m_add, AstLeaf("x_0"), AstLeaf("x_1")))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class Bnot_Rule_1(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("bnot_x_1")),
AstNode(m_bnot,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("bnot_x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class Bnot_XorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_bnot,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_bnot, AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1")))

View File

@@ -0,0 +1,448 @@
from ida_hexrays import *
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
from d810.hexrays_helpers import equal_bnot_cst, SUB_TABLE, AND_TABLE, equal_bnot_mop
class CstSimplificationRule1(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_bnot,
AstLeaf("x_0")),
AstNode(m_xor,
AstNode(m_bnot,
AstLeaf("x_0")),
AstConstant("c_1")))
REPLACEMENT_PATTERN = AstNode(m_xor,
AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_bnot,
AstConstant("c_1"))),
AstNode(m_bnot, AstConstant("c_1")))
class CstSimplificationRule2(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstNode(m_xor,
AstLeaf("x_0"),
AstConstant("c_1_1")),
AstConstant("c_2_1")),
AstNode(m_and,
AstNode(m_xor,
AstLeaf("x_0"),
AstConstant("c_1_2")),
AstConstant("c_2_2")))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstConstant("c_res"))
def check_candidate(self, candidate):
if not equal_bnot_cst(candidate["c_2_1"].mop, candidate["c_2_2"].mop):
return False
c_res = ((candidate["c_1_1"].value ^ candidate["c_1_2"].value) & candidate["c_2_1"].value)
c_res ^= candidate["c_1_2"].value
candidate.add_constant_leaf("c_res", c_res, candidate["c_1_1"].size)
return True
class CstSimplificationRule3(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_sub,
AstLeaf("x_0"),
AstConstant("c_0")),
AstNode(m_mul,
AstConstant("c_1"),
AstNode(m_sub,
AstLeaf("x_0"),
AstConstant("c_2"))))
REPLACEMENT_PATTERN = AstNode(m_sub,
AstNode(m_mul,
AstConstant("c_coeff"),
AstLeaf("x_0")),
AstConstant("c_sub"))
def check_candidate(self, candidate):
c_coeff = candidate["c_1"].value + 1
c_sub = (candidate["c_1"].value * candidate["c_2"].value) + candidate["c_0"].value
candidate.add_constant_leaf("c_coeff", c_coeff, candidate["c_1"].size)
candidate.add_constant_leaf("c_sub", c_sub, candidate["c_2"].size)
return True
class CstSimplificationRule4(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstLeaf("x_0"),
AstNode(m_sub,
AstConstant("c_1"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_add,
AstLeaf("x_0"),
AstNode(m_add,
AstLeaf("x_1"),
AstConstant("c_res")))
def check_candidate(self, candidate):
c_res = SUB_TABLE[candidate["c_1"].size] - candidate["c_1"].value
candidate.add_constant_leaf("c_res", c_res, candidate["c_1"].size)
return True
class CstSimplificationRule5(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_1")),
AstNode(m_and,
AstLeaf("x_1"),
AstConstant("c_2")))
REPLACEMENT_PATTERN = AstNode(m_xor,
AstNode(m_and,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstConstant("c_1")),
AstLeaf("x_1"))
def check_candidate(self, candidate):
return equal_bnot_cst(candidate["c_1"].mop, candidate["c_2"].mop)
class CstSimplificationRule6(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_xor,
AstLeaf("x_0"),
AstConstant("c_1")),
AstConstant("c_2"))
REPLACEMENT_PATTERN = AstNode(m_xor,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_2")),
AstConstant("c_res"))
def check_candidate(self, candidate):
c_res = candidate["c_1"].value & candidate["c_2"].value
candidate.add_constant_leaf("c_res", c_res, candidate["c_2"].size)
return True
class CstSimplificationRule7(PatternMatchingRule):
PATTERN = AstNode(m_shr,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_1")),
AstConstant("c_2"))
REPLACEMENT_PATTERN = AstNode(m_and,
AstNode(m_shr,
AstLeaf("x_0"),
AstConstant("c_2")),
AstConstant("c_res"))
def check_candidate(self, candidate):
c_res = candidate["c_1"].value >> candidate["c_2"].value
candidate.add_constant_leaf("c_res", c_res, candidate["c_1"].size)
return True
class CstSimplificationRule8(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_1")),
AstConstant("c_2"))
REPLACEMENT_PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_res")),
AstConstant("c_2"))
def check_candidate(self, candidate):
c_res = candidate["c_1"].value & ~candidate["c_2"].value
if c_res == candidate["c_1"].value:
return False
candidate.add_constant_leaf("c_res", c_res, candidate["c_1"].size)
return True
class CstSimplificationRule9(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_or,
AstLeaf("x_0"),
AstConstant("c_1")),
AstConstant("c_2"))
REPLACEMENT_PATTERN = AstNode(m_xor,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_and")),
AstConstant("c_xor"))
def check_candidate(self, candidate):
c_and = (AND_TABLE[candidate["c_1"].size] ^ candidate["c_1"].value) & candidate["c_2"].value
c_xor = candidate["c_1"].value & candidate["c_2"].value
candidate.add_constant_leaf("c_and", c_and, candidate["x_0"].size)
candidate.add_constant_leaf("c_xor", c_xor, candidate["x_0"].size)
return True
class CstSimplificationRule10(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_1")),
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_2")))
REPLACEMENT_PATTERN = AstNode(m_neg,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_and")))
def check_candidate(self, candidate):
if (candidate["c_1"].value & candidate["c_2"].value) != candidate["c_1"].value:
return False
c_and = (AND_TABLE[candidate["c_1"].size] ^ candidate["c_1"].value) & candidate["c_2"].value
candidate.add_constant_leaf("c_and", c_and, candidate["x_0"].size)
return True
class CstSimplificationRule11(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_xor,
AstNode(m_bnot,
AstLeaf("x_0")),
AstConstant("c_1")),
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_2")))
REPLACEMENT_PATTERN = AstNode(m_xor,
AstNode(m_xor,
AstLeaf("x_0"),
AstConstant("c_1_bnot")),
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_and")))
def check_candidate(self, candidate):
c_1_bnot = (AND_TABLE[candidate["c_1"].size] ^ candidate["c_1"].value)
c_and = c_1_bnot & candidate["c_2"].value
candidate.add_constant_leaf("c_1_bnot", c_1_bnot, candidate["c_1"].size)
candidate.add_constant_leaf("c_and", c_and, candidate["c_1"].size)
return True
class CstSimplificationRule12(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_sub,
AstConstant("c_1"),
AstLeaf("x_0")),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_and,
AstNode(m_bnot,
AstLeaf("x_0")),
AstConstant("c_2"))))
REPLACEMENT_PATTERN = AstNode(m_sub,
AstNode(m_xor,
AstNode(m_bnot, AstLeaf("x_0")),
AstConstant("c_2")),
AstConstant("c_diff"))
def check_candidate(self, candidate):
c_diff = candidate["c_2"].value - candidate["c_1"].value
candidate.add_constant_leaf("c_diff", c_diff, candidate["c_1"].size)
return True
class CstSimplificationRule13(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstNode(m_and,
AstConstant("cst_1"),
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1"))),
AstLeaf("x_1"))
REPLACEMENT_PATTERN = AstNode(m_xor,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("cst_1")),
AstNode(m_and, AstLeaf("x_1"),
AstConstant("not_cst_1")))
def check_candidate(self, candidate):
candidate.add_constant_leaf("not_cst_1", ~candidate["cst_1"].value, candidate["cst_1"].size)
return True
class CstSimplificationRule14(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_1")),
AstConstant("c_2"))
REPLACEMENT_PATTERN = AstNode(m_add,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("lnot_c_1")),
AstConstant("val_1"))
def check_candidate(self, candidate):
lnot_c_1_value = candidate["c_1"].value ^ AND_TABLE[candidate["c_1"].size]
tmp = lnot_c_1_value ^ candidate["c_2"].value
if tmp != 1:
return False
candidate.add_constant_leaf("val_1", 1, candidate["c_2"].size)
candidate.add_constant_leaf("lnot_c_1", lnot_c_1_value, candidate["c_1"].size)
class CstSimplificationRule15(PatternMatchingRule):
PATTERN = AstNode(m_shr,
AstNode(m_shr,
AstLeaf("x_0"),
AstConstant("c_1")),
AstConstant("c_2"))
REPLACEMENT_PATTERN = AstNode(m_shr, AstLeaf("x_0"), AstConstant("c_res"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("c_res", candidate["c_1"].value + candidate["c_2"].value, candidate["c_1"].size)
return True
class CstSimplificationRule16(PatternMatchingRule):
PATTERN = AstNode(m_bnot,
AstNode(m_xor,
AstLeaf("x_0"),
AstConstant("c_1")))
REPLACEMENT_PATTERN = AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("bnot_c_1"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("bnot_c_1", candidate["c_1"].value ^ AND_TABLE[candidate["c_1"].size],
candidate["c_1"].size)
return True
class CstSimplificationRule17(PatternMatchingRule):
PATTERN = AstNode(m_bnot,
AstNode(m_or,
AstLeaf("x_0"),
AstConstant("c_1")))
REPLACEMENT_PATTERN = AstNode(m_and,
AstNode(m_bnot, AstLeaf("x_0")),
AstLeaf("bnot_c_1"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("bnot_c_1", candidate["c_1"].value ^ AND_TABLE[candidate["c_1"].size],
candidate["c_1"].size)
return True
class CstSimplificationRule18(PatternMatchingRule):
PATTERN = AstNode(m_bnot,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_1")))
REPLACEMENT_PATTERN = AstNode(m_or,
AstNode(m_bnot, AstLeaf("x_0")),
AstLeaf("bnot_c_1"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("bnot_c_1", candidate["c_1"].value ^ AND_TABLE[candidate["c_1"].size],
candidate["c_1"].size)
return True
class CstSimplificationRule19(PatternMatchingRule):
PATTERN = AstNode(m_sar,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_1")),
AstConstant("c_2"))
REPLACEMENT_PATTERN = AstNode(m_and, AstNode(m_shr, AstLeaf("x_0"), AstConstant("c_2")), AstConstant("c_res"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("c_res", candidate["c_1"].value >> candidate["c_2"].value,
candidate["c_1"].size)
return True
# Found sometimes with OLLVM
class CstSimplificationRule20(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf('bnot_x_0'),
AstConstant('c_and_1')),
AstNode(m_xor,
AstNode(m_and,
AstLeaf('x_0'),
AstConstant('c_and_2')),
AstConstant('c_xor')))
REPLACEMENT_PATTERN = AstNode(m_xor,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_and_res")),
AstConstant("c_xor_res"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
if candidate["c_and_1"].value & candidate["c_and_2"].value != 0:
return False
candidate.add_constant_leaf("c_and_res", candidate["c_and_1"].value ^ candidate["c_and_2"].value,
candidate["c_and_1"].size)
candidate.add_constant_leaf("c_xor_res", candidate["c_and_1"].value ^ candidate["c_xor"].value,
candidate["c_and_1"].size)
return True
# Found sometimes with OLLVM
class CstSimplificationRule21(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_xor,
AstNode(m_and,
AstLeaf('x_0'),
AstConstant('c_and')),
AstConstant('c_xor_1')),
AstNode(m_xor,
AstNode(m_and,
AstLeaf('x_0'),
AstConstant('bnot_c_and')),
AstConstant('c_xor_2')))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstConstant("c_xor_res"))
def check_candidate(self, candidate):
if not equal_bnot_cst(candidate["c_and"].mop, candidate["bnot_c_and"].mop):
return False
if candidate["c_xor_1"].mop.nnn.value & candidate["c_xor_2"].mop.nnn.value != 0:
return False
candidate.add_constant_leaf("c_xor_res", candidate["c_xor_1"].value ^ candidate["c_xor_2"].value,
candidate["c_xor_1"].size)
return True
# Found sometimes with OLLVM
class CstSimplificationRule22(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_xor,
AstNode(m_and,
AstLeaf('x_0'),
AstConstant('c_and')),
AstConstant('c_xor_1')),
AstNode(m_xor,
AstNode(m_and,
AstLeaf('bnot_x_0'),
AstConstant('bnot_c_and')),
AstConstant('c_xor_2')))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstConstant("c_xor_res"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
if not equal_bnot_cst(candidate["c_and"].mop, candidate["bnot_c_and"].mop):
return False
if candidate["c_xor_1"].mop.nnn.value & candidate["c_xor_2"].mop.nnn.value != 0:
return False
if candidate["c_xor_1"].mop.nnn.value & candidate["bnot_c_and"].mop.nnn.value != 0:
return False
candidate.add_constant_leaf("c_xor_res", candidate["c_xor_1"].value ^ candidate["c_xor_2"].value ^ candidate["bnot_c_and"].value,
candidate["c_xor_1"].size)
return True

View File

@@ -0,0 +1,50 @@
from ida_hexrays import *
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.hexrays_helpers import equal_bnot_mop, AND_TABLE
# GetIdentRule1: ((x_0 & x_1) + (x_0 & ~x_1)) == x_0
class GetIdentRule1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('bnot_x_1')))
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("x_0"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
# GetIdentRule2: ((x_0 & x_1) ^ (x_0 & ~x_1)) == x_0 i
class GetIdentRule2(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('bnot_x_1')))
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("x_0"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class GetIdentRule3(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("x_0"))

View File

@@ -0,0 +1,153 @@
from ida_hexrays import *
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.hexrays_helpers import equal_bnot_mop, is_check_mop, SUB_TABLE
class Mul_MbaRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_mul,
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1'))),
AstNode(m_mul,
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('bnot_x_1')),
AstNode(m_and,
AstLeaf('x_1'),
AstLeaf('bnot_x_0'))))
REPLACEMENT_PATTERN = AstNode(m_mul, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class Mul_MbaRule_2(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_mul,
AstNode(m_or,
AstLeaf('x_0'),
AstConstant('c_1')),
AstLeaf('x_0')),
AstNode(m_mul,
AstNode(m_and,
AstLeaf('x_0'),
AstConstant('bnot_c_1')),
AstNode(m_and,
AstConstant('c_1'),
AstLeaf('bnot_x_0'))))
REPLACEMENT_PATTERN = AstNode(m_mul, AstLeaf("x_0"), AstConstant('c_1'))
def check_candidate(self, candidate):
if not is_check_mop(candidate["x_0"].mop):
return False
if candidate["c_1"].value & 0x1 != 1:
return False
if not equal_bnot_mop(candidate["c_1"].mop, candidate["bnot_c_1"].mop):
return False
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
return True
class Mul_MbaRule_3(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_mul,
AstNode(m_or,
AstLeaf('x_0'),
AstConstant('c_1')),
AstNode(m_and,
AstLeaf('x_0'),
AstConstant('c_1'))),
AstNode(m_mul,
AstLeaf('x_0'),
AstNode(m_and,
AstConstant('c_1'),
AstLeaf('bnot_x_0'))))
REPLACEMENT_PATTERN = AstNode(m_mul, AstLeaf("x_0"), AstConstant('c_1'))
def check_candidate(self, candidate):
if not is_check_mop(candidate["x_0"].mop):
return False
if candidate["c_1"].value & 0x1 == 1:
return False
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
return True
class Mul_MbaRule_4(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_mul,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1"))),
AstNode(m_mul,
AstNode(m_bnot,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("bnot_x_1"))),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("bnot_x_1"))))
REPLACEMENT_PATTERN = AstNode(m_mul, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class Mul_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstConstant("2", 2),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_add,
AstLeaf("x_1"),
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("bnot_x_1")))))
REPLACEMENT_PATTERN = AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class Mul_FactorRule_2(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_neg,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1"))),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_mul,
AstConstant("val_fe"),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_fe", SUB_TABLE[candidate.size] - 2, candidate.size)
return True

View File

@@ -0,0 +1,126 @@
from ida_hexrays import *
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
from d810.hexrays_helpers import AND_TABLE
from d810.ast import AstLeaf, AstConstant, AstNode
class Neg_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_bnot,
AstLeaf("x_0")),
AstConstant("1", 1))
REPLACEMENT_PATTERN = AstNode(m_neg, AstLeaf("x_0"))
class Neg_HackersDelightRule_2(PatternMatchingRule):
PATTERN = AstNode(m_bnot,
AstNode(m_sub,
AstLeaf("x_0"),
AstConstant("1", 1)))
REPLACEMENT_PATTERN = AstNode(m_neg, AstLeaf("x_0"))
class NegAdd_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_neg,
AstNode(m_add,
AstLeaf("x_0"),
AstLeaf("x_1")))
class NegAdd_HackersDelightRule_2(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_xor,
AstLeaf("x_0"),
AstNode(m_or,
AstLeaf("x_1"),
AstLeaf("x_2"))),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_or,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstLeaf("x_2"))))
REPLACEMENT_PATTERN = AstNode(m_neg,
AstNode(m_add,
AstLeaf("x_0"),
AstNode(m_or,
AstLeaf("x_1"),
AstLeaf("x_2"))))
class NegAdd_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_mul,
AstConstant('val_fe'),
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('x_1'))),
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_1')))
REPLACEMENT_PATTERN = AstNode(m_neg,
AstNode(m_add,
AstLeaf("x_0"),
AstLeaf("x_1")))
def check_candidate(self, candidate):
if (candidate["val_fe"].value + 2) & AND_TABLE[candidate["val_fe"].size] != 0:
return False
return True
class NegOr_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_add,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_neg,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")))
class NegXor_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('x_1')))
REPLACEMENT_PATTERN = AstNode(m_neg,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")))
class NegXor_HackersDelightRule_2(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_add,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_mul,
AstConstant('2', 2),
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('x_1'))))
REPLACEMENT_PATTERN = AstNode(m_neg,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")))

View File

@@ -0,0 +1,259 @@
from ida_hexrays import *
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.hexrays_helpers import equal_bnot_mop
class Or_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("bnot_x_1")),
AstLeaf("x_1"))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class Or_HackersDelightRule_2(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_add,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
class Or_HackersDelightRule_2_variant_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_sub,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_neg, AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_or,
AstLeaf("x_0"),
AstNode(m_neg, AstLeaf("x_1")))
class Or_MbaRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
class Or_MbaRule_2(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_add,
AstNode(m_add,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstConstant('1', 1)),
AstNode(m_bnot,
AstNode(m_and,
AstLeaf('x_1'),
AstLeaf('x_0'))))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
class Or_MbaRule_3(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_add,
AstLeaf('x_0'),
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_1'))),
AstNode(m_and,
AstLeaf('x_0'),
AstNode(m_bnot,
AstLeaf('x_1'))))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
class Or_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
class Or_FactorRule_2(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_xor,
AstLeaf("x_1"),
AstLeaf("x_2"))),
AstNode(m_xor,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstLeaf("x_2")))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstNode(m_xor, AstLeaf("x_1"), AstLeaf("x_2")))
class Or_FactorRule_3(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_xor,
AstLeaf("bnot_x_0"),
AstLeaf("bnot_x_1")))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class Or_OllvmRule_1(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_bnot,
AstNode(m_xor,
AstLeaf("bnot_x_0"),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
return True
class Or_Rule_1(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf("bnot_x_0"),
AstLeaf("x_1")),
AstLeaf("x_0"))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
return True
class Or_Rule_2(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstLeaf("x_1"))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
class Or_Rule_3(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_bnot,
AstNode(m_or,
AstLeaf('bnot_x_0'),
AstLeaf('bnot_x_1'))),
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_1')))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class Or_Rule_4(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
class OrBnot_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstNode(m_bnot,
AstLeaf("x_0")),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_or,
AstNode(m_bnot,
AstLeaf("x_0")),
AstLeaf("x_1"))
class OrBnot_FactorRule_2(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstLeaf("x_0"),
AstNode(m_and,
AstNode(m_bnot,
AstLeaf("x_0")),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
class OrBnot_FactorRule_3(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_sub,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_or,
AstLeaf("bnot_x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstNode(m_bnot, AstLeaf("x_1")))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
return True
class OrBnot_FactorRule_4(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstNode(m_or,
AstLeaf("bnot_x_0"),
AstLeaf("x_1")),
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstNode(m_bnot, AstLeaf("x_1")))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
return True

View File

@@ -0,0 +1,403 @@
from ida_hexrays import *
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.hexrays_helpers import equal_bnot_mop, SUB_TABLE, AND_TABLE
# PredSetnzRule1: (x_0 | c_1) != c_2 ==> 1 if c_1 | c_2 != c_2
class PredSetnzRule1(PatternMatchingRule):
PATTERN = AstNode(m_setnz,
AstNode(m_or,
AstLeaf("x_0"),
AstConstant("c_1")),
AstConstant("c_2"))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
def check_candidate(self, candidate):
if (candidate["c_1"].value | candidate["c_2"].value) == candidate["c_2"].value:
return False
candidate.add_constant_leaf("val_1", 1, candidate.size)
return True
# PredSetnzRule2: (x_0 & c_1) != c_2 ==> 1 if c_1 & c_2 != c_2
class PredSetnzRule2(PatternMatchingRule):
PATTERN = AstNode(m_setnz,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_1")),
AstConstant("c_2"))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
def check_candidate(self, candidate):
if (candidate["c_1"].value & candidate["c_2"].value) == candidate["c_2"].value:
return False
candidate.add_constant_leaf("val_1", 1, candidate.size)
return True
# PredSetnzRule3: (x_0 | 2) + (x_0 ^ 2) != 0 ==> 1 (because math)
class PredSetnzRule3(PatternMatchingRule):
PATTERN = AstNode(m_setnz,
AstNode(m_add,
AstNode(m_or,
AstLeaf("x_0"),
AstConstant("2", 2)),
AstNode(m_xor,
AstLeaf("x_0"),
AstConstant("2", 2))),
AstConstant("0", 0))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_1", 1, candidate.size)
return True
# PredSetnzRule4: (cst_1 - x_0) ^ x_0 != 0 ==> 1 if cst_1 % 2 == 1 (because math)
class PredSetnzRule4(PatternMatchingRule):
PATTERN = AstNode(m_setnz,
AstNode(m_xor,
AstNode(m_sub,
AstConstant("cst_1"),
AstLeaf("x_0")),
AstLeaf("x_0")),
AstConstant("0", 0))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
def check_candidate(self, candidate):
if (candidate["cst_1"].value % 2) == 0:
return False
candidate.add_constant_leaf("val_1", 1, candidate.size)
return True
# PredSetnzRule5: (-(~x_0 & 1)) != x_0 ==> 1 (because math)
class PredSetnzRule5(PatternMatchingRule):
PATTERN = AstNode(m_setnz,
AstNode(m_neg,
AstNode(m_and,
AstNode(m_bnot,
AstLeaf("x_0")),
AstConstant("1", 1))),
AstLeaf("x_0"))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_1", 1, candidate.size)
return True
# PredSetnzRule6: ((x_0 + c_1) + ((x_0 + c_2) & 1)) != 0 ==> 1 (if (c_2 - c_1) & 1 == 1)
class PredSetnzRule6(PatternMatchingRule):
PATTERN = AstNode(m_setnz,
AstNode(m_add,
AstNode(m_add,
AstLeaf("x_0"),
AstConstant("c_1")),
AstNode(m_and,
AstNode(m_add,
AstLeaf("x_0"),
AstConstant("c_2")),
AstConstant("1", 1))),
AstConstant("0", 0))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
def check_candidate(self, candidate):
if (candidate["c_2"].value - candidate["c_1"].value) & 0x1 != 1:
return False
candidate.add_constant_leaf("val_1", 1, candidate.size)
return True
# PredSetnzRule8: bnot((3 - x_0)) ^ bnot(x_0) != 0 ==> 1
class PredSetnzRule8(PatternMatchingRule):
PATTERN = AstNode(m_setnz,
AstNode(m_xor,
AstNode(m_bnot,
AstNode(m_sub,
AstConstant("3", 3),
AstLeaf("x_0"))),
AstNode(m_bnot,
AstLeaf("x_0"))),
AstConstant("0", 0))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_1", 1, candidate.size)
return True
# PredSetzRule1: (x_0 | c_1) == c_2 ==> 0 if c_1 | c_2 != c_2
class PredSetzRule1(PatternMatchingRule):
PATTERN = AstNode(m_setz,
AstNode(m_or,
AstLeaf("x_0"),
AstConstant("c_1")),
AstConstant("c_2"))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_0"))
def check_candidate(self, candidate):
if (candidate["c_1"].value | candidate["c_2"].value) == candidate["c_2"].value:
return False
candidate.add_constant_leaf("val_0", 0, candidate.size)
return True
# PredSetzRule2: (x_0 & c_1) == c_2 ==> 0 if c_1 & c_2 != c_2
class PredSetzRule2(PatternMatchingRule):
PATTERN = AstNode(m_setz,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_1")),
AstConstant("c_2"))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_0"))
def check_candidate(self, candidate):
if (candidate["c_1"].value & candidate["c_2"].value) == candidate["c_2"].value:
return False
candidate.add_constant_leaf("val_0", 0, candidate.size)
return True
# PredSetzRule3: (x_0 | 2) + (x_0 ^ 2) == 0 ==> 0 (because math)
class PredSetzRule3(PatternMatchingRule):
PATTERN = AstNode(m_setz,
AstNode(m_add,
AstNode(m_or,
AstLeaf("x_0"),
AstConstant("2", 2)),
AstNode(m_xor,
AstLeaf("x_0"),
AstConstant("2", 2))),
AstConstant("0", 0))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_0"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_0", 0, candidate.size)
return True
# PredSetbRule1: (x_0 & c_1) <u c_2 ==> 0 if c_1 <u c_2
class PredSetbRule1(PatternMatchingRule):
PATTERN = AstNode(m_setb,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_1")),
AstConstant("c_2"))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_0"))
def check_candidate(self, candidate):
if candidate["c_1"].value >= candidate["c_2"].value:
return False
candidate.add_constant_leaf("val_0", 0, candidate.size)
return True
class PredOdd1(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_mul,
AstLeaf('x_0'),
AstNode(m_sub,
AstLeaf('x_0'),
AstConstant('1', 1))),
AstConstant('1', 1))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant('val_0'))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_0", 0, candidate.size)
return True
class PredOdd2(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_mul,
AstLeaf('x_0'),
AstNode(m_add,
AstLeaf('x_0'),
AstConstant('1', 1))),
AstConstant('1', 1))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant('val_0'))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_0", 0, candidate.size)
return True
# Pred0Rule1: (x_0 & ~x_0) ==> 0
class Pred0Rule1(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_bnot,
AstLeaf("x_0")))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_0"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_0", 0, candidate.size)
return True
# Pred0Rule2: (xdu(x_0 & 1) == 2) ==> 0
class Pred0Rule2(PatternMatchingRule):
PATTERN = AstNode(m_setz,
AstNode(m_xdu,
AstNode(m_and,
AstLeaf("x_0"),
AstConstant("c_1", 1))),
AstConstant("c_2", 2))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_0"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_0", 0, candidate.size)
return True
class Pred0Rule3(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_bnot,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("val_0"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_0", 0, candidate.size)
return True
class Pred0Rule4(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_bnot,
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('x_1'))))
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("val_0"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_0", 0, candidate.size)
return True
class Pred0Rule5(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_1')))
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("val_0"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_0", 0, candidate.size)
return True
class PredFFRule1(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstLeaf("x_0"),
AstNode(m_bnot,
AstLeaf("x_0")))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_ff"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_ff", AND_TABLE[candidate.size], candidate.size)
return True
# Pred1Rule2: (x_0 ^ x_1) | (~x_0 | x_1) ==> 0xff
class PredFFRule2(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_or,
AstLeaf("bnot_x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_ff"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
candidate.add_constant_leaf("val_ff", AND_TABLE[candidate.size], candidate.size)
return True
class PredFFRule3(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstLeaf("x_0"),
AstNode(m_bnot,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("val_ff"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_ff", AND_TABLE[candidate.size], candidate.size)
return True
class PredFFRule4(PatternMatchingRule):
DESCRIPTION = "(x_0 | x_1) | (~(x_0 & x_1)) ==> 0xff"
PATTERN = AstNode(m_or,
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_bnot,
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1'))))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_ff"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_ff", AND_TABLE[candidate.size], candidate.size)
return True
class PredOr2_Rule_1(PatternMatchingRule):
PATTERN = AstNode(m_and,
AstNode(m_bnot,
AstNode(m_mul,
AstLeaf('x_0'),
AstLeaf('x_0'))),
AstConstant('3', 3))
REPLACEMENT_PATTERN = AstNode(m_or,
AstNode(m_and,
AstNode(m_bnot, AstLeaf('x_0')),
AstConstant('val_1')),
AstConstant('val_2'))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_1", 1, candidate["x_0"].mop.size)
candidate.add_constant_leaf("val_2", 2, candidate["x_0"].mop.size)
return True
class PredOr1_Rule_1(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstLeaf('x_0'),
AstNode(m_add,
AstNode(m_and,
AstLeaf('x_0'),
AstConstant('1', 1)),
AstConstant('1', 1)))
REPLACEMENT_PATTERN = AstNode(m_or,
AstNode(m_xor,
AstLeaf('x_0'),
AstNode(m_mul,
AstConstant('val_2'),
AstNode(m_and,
AstLeaf('x_0'),
AstConstant('val_1')))),
AstConstant('val_1'))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_1", 1, candidate["x_0"].mop.size)
candidate.add_constant_leaf("val_2", 2, candidate["x_0"].mop.size)
return True

View File

@@ -0,0 +1,177 @@
from ida_hexrays import *
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.hexrays_helpers import equal_bnot_mop, SUB_TABLE
class Sub_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstLeaf("x_0"),
AstNode(m_add,
AstNode(m_bnot,
AstLeaf("x_1")),
AstConstant("1", 1)))
REPLACEMENT_PATTERN = AstNode(m_sub, AstLeaf("x_0"), AstLeaf("x_1"))
class Sub_HackersDelightRule_2(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_and,
AstNode(m_bnot,
AstLeaf("x_0")),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_sub, AstLeaf("x_0"), AstLeaf("x_1"))
class Sub_HackersDelightRule_3(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("bnot_x_1")),
AstNode(m_and,
AstLeaf("bnot_x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_sub, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class Sub_HackersDelightRule_4(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("bnot_x_1"))),
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_sub, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class Sub1_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_sub,
AstNode(m_neg,
AstLeaf('x_0')),
AstConstant('1', 1)),
AstNode(m_mul,
AstConstant('c_minus_2'),
AstLeaf('x_0')))
REPLACEMENT_PATTERN = AstNode(m_sub, AstLeaf("x_0"), AstConstant("val_1"))
def check_candidate(self, candidate):
if candidate["c_minus_2"].value != SUB_TABLE[candidate["c_minus_2"].size] - 2:
return False
candidate.add_constant_leaf("val_1", 1, candidate["x_0"].size)
return True
class Sub1_FactorRule_2(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_mul,
AstConstant("2", 2),
AstLeaf("x_0")),
AstNode(m_bnot,
AstLeaf("x_0")))
REPLACEMENT_PATTERN = AstNode(m_sub, AstLeaf("x_0"), AstConstant("1", 1))
class Sub1Add_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1"))),
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("bnot_x_1")))
REPLACEMENT_PATTERN = AstNode(m_sub,
AstNode(m_add,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstConstant("val_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
candidate.add_constant_leaf("val_1", 1, candidate["x_1"].size)
return True
class Sub1And_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("bnot_x_1")),
AstLeaf("x_1"))
REPLACEMENT_PATTERN = AstNode(m_sub,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstConstant("val_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
candidate.add_constant_leaf("val_1", 1, candidate["x_0"].size)
return True
class Sub1Or_MbaRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_add,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_bnot,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_sub,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstConstant("val_1"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_1", 1, candidate.size)
return True
class Sub1And1_MbaRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_or,
AstNode(m_bnot,
AstLeaf('x_0')),
AstConstant("1", 1)),
AstLeaf('x_0'))
REPLACEMENT_PATTERN = AstNode(m_sub,
AstNode(m_and,
AstLeaf('x_0'),
AstConstant("val_1_1")),
AstConstant("val_1_2"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_1_1", 1, candidate["x_0"].size)
candidate.add_constant_leaf("val_1_2", 1, candidate["x_0"].size)
return True

View File

@@ -0,0 +1,325 @@
from ida_hexrays import *
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.hexrays_helpers import equal_bnot_mop, SUB_TABLE
class Xor_HackersDelightRule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
class Xor_HackersDelightRule_2(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1"))),
AstNode(m_add,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
class Xor_HackersDelightRule_3(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_add,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
class Xor_HackersDelightRule_4(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_sub,
AstNode(m_sub,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_mul,
AstConstant('2', 2),
AstNode(m_or,
AstLeaf('x_0'),
AstNode(m_bnot, AstLeaf('x_1'))))),
AstConstant('2', 2))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
class Xor_HackersDelightRule_5(PatternMatchingRule):
FUZZ_PATTERN = False
PATTERN = AstNode(m_sub,
AstLeaf("x_0"),
AstNode(m_sub,
AstNode(m_mul,
AstConstant('2', 2),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1"))),
AstLeaf("x_1")))
PATTERNS = [
AstNode(m_sub, AstLeaf("x_0"), AstNode(m_sub, AstNode(m_mul, AstConstant('2', 2), AstNode(m_and, AstLeaf("x_1"), AstLeaf("x_0"))), AstLeaf("x_1")))
]
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
class Xor_MbaRule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstLeaf('x_0'),
AstNode(m_sub,
AstNode(m_mul,
AstConstant('2', 2),
AstNode(m_and,
AstLeaf('x_1'),
AstNode(m_bnot,
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_1'))))),
AstLeaf('x_1')))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
class Xor_MbaRule_2(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstLeaf('x_0'),
AstNode(m_sub,
AstNode(m_mul,
AstConstant('2', 2),
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1'))),
AstLeaf('x_1')))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
class Xor_MbaRule_3(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstLeaf('x_0'),
AstNode(m_mul,
AstConstant('2', 2),
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1'))))
REPLACEMENT_PATTERN = AstNode(m_sub,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstLeaf("x_1"))
class Xor_FactorRule_1(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("bnot_x_1")),
AstNode(m_and,
AstLeaf("bnot_x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class Xor_FactorRule_2(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstNode(m_and,
AstLeaf('bnot_x_0'),
AstLeaf('x_1')),
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('bnot_x_1')))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class Xor_FactorRule_3(PatternMatchingRule):
PATTERN = AstNode(m_xor,
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('x_1')))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
class Xor_SpecialConstantRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_sub,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_and,
AstNode(m_bnot,
AstLeaf("x_0")),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
class Xor_SpecialConstantRule_2(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstLeaf('x_0'),
AstNode(m_add,
AstNode(m_mul,
AstConstant('0xfe'),
AstNode(m_and,
AstLeaf('x_0'),
AstLeaf('x_1'))),
AstLeaf('x_1')))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
def check_candidate(self, candidate):
return candidate["0xfe"].value == SUB_TABLE[candidate["0xfe"].size] - 2
class Xor1_MbaRule_1(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_bnot,
AstLeaf('x_0')),
AstNode(m_or,
AstNode(m_mul,
AstConstant('2', 2),
AstLeaf('x_0')),
AstConstant('2', 2)))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf('x_0'), AstConstant("val_1"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_1", 1, candidate.size)
return True
class Xor_Rule_1(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_bnot,
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_xor,
AstLeaf('x_0'),
AstNode(m_bnot, AstLeaf("x_1")))
# Found sometimes with OLLVM
class Xor_Rule_2(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_2')),
AstNode(m_xor,
AstLeaf('x_1'),
AstLeaf('bnot_x2'))),
AstNode(m_and,
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('bnot_x2')),
AstNode(m_xor,
AstLeaf('x_1'),
AstLeaf('x_2'))))
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf('x_0'), AstLeaf('x_1'))
# Found sometimes with OLLVM
class Xor_Rule_3(PatternMatchingRule):
PATTERN = AstNode(m_or,
AstNode(m_and,
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('x_2')),
AstNode(m_xor,
AstLeaf('x_1'),
AstLeaf('x_2'))),
AstNode(m_and,
AstNode(m_xor,
AstLeaf('x_0'),
AstLeaf('bnot_x2')),
AstNode(m_xor,
AstLeaf('x_1'),
AstLeaf('bnot_x2'))))
REPLACEMENT_PATTERN = AstNode(m_xor, AstNode(m_bnot, AstLeaf('x_0')), AstLeaf('x_1'))
class XorAlmost_Rule_1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_add,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstNode(m_mul,
AstConstant("2", 2),
AstNode(m_or,
AstLeaf("x_0"),
AstNode(m_sub,
AstLeaf("x_1"),
AstConstant("1", 1)))))
REPLACEMENT_PATTERN = AstNode(m_add,
AstNode(m_xor,
AstLeaf("x_0"),
AstNode(m_neg, AstLeaf("x_1"))),
AstLeaf("val_2"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_2", 2, candidate.size)
return True
class Xor_NestedStuff(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_add,
AstNode(m_add,
AstLeaf('x_9'),
AstLeaf('x_10')),
AstLeaf("x_11")),
AstNode(m_add,
AstLeaf("x_14"),
AstNode(m_mul,
AstConstant('2', 2),
AstNode(m_and,
AstLeaf('x_10'),
AstNode(m_sub,
AstNode(m_add,
AstLeaf('x_9'),
AstLeaf("x_11")),
AstLeaf("x_14"))))))
REPLACEMENT_PATTERN = AstNode(m_xor,
AstLeaf("x_10"),
AstNode(m_sub,
AstNode(m_add,
AstLeaf('x_9'),
AstLeaf("x_11")),
AstLeaf("x_14")))
FUZZ_PATTERN = False

View File

@@ -0,0 +1,117 @@
from ida_hexrays import *
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.hexrays_helpers import equal_bnot_mop
class WeirdRule1(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstLeaf("x_0"),
AstNode(m_or,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_add,
AstNode(m_or,
AstLeaf("x_0"),
AstNode(m_bnot, AstLeaf("x_1"))),
AstConstant("val_1"))
def check_candidate(self, candidate):
candidate.add_constant_leaf("val_1", 1, candidate.size)
return True
class WeirdRule2(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_mul,
AstConstant("2", 2),
AstLeaf("x_0")),
AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_bnot, AstLeaf("x_1"))))
REPLACEMENT_PATTERN = AstNode(m_add,
AstLeaf("x_0"),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")))
class WeirdRule3(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_and,
AstLeaf("x_0"),
AstNode(m_bnot, AstLeaf("x_1"))),
AstNode(m_mul,
AstConstant("2", 2),
AstLeaf("x_0")))
REPLACEMENT_PATTERN = AstNode(m_neg,
AstNode(m_add,
AstLeaf("x_0"),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1"))))
class WeirdRule4(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("bnot_x_1")),
AstNode(m_and,
AstLeaf("x_0"),
AstLeaf("x_1")))
REPLACEMENT_PATTERN = AstNode(m_sub,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstLeaf("x_1"))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class WeirdRule5(PatternMatchingRule):
PATTERN = AstNode(m_sub,
AstNode(m_add,
AstNode(m_or,
AstLeaf("bnot_x_0"),
AstNode(m_and,
AstLeaf("bnot_x_1"),
AstLeaf("x_2"))),
AstNode(m_add,
AstLeaf("x_0"),
AstNode(m_and,
AstLeaf("x_1"),
AstLeaf("x_2")))),
AstLeaf("x_2"))
REPLACEMENT_PATTERN = AstNode(m_or,
AstLeaf("x_0"),
AstNode(m_or,
AstLeaf("x_1"),
AstNode(m_bnot,
AstLeaf("x_2"))))
def check_candidate(self, candidate):
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
return False
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
return False
return True
class WeirdRule6(PatternMatchingRule):
PATTERN = AstNode(m_add,
AstNode(m_or,
AstLeaf('x_0'),
AstLeaf('x_1')),
AstNode(m_and,
AstLeaf('x_0'),
AstNode(m_bnot,
AstLeaf('x_1'))))
REPLACEMENT_PATTERN = AstNode(m_add,
AstNode(m_xor,
AstLeaf("x_0"),
AstLeaf("x_1")),
AstLeaf('x_0'))

View File

@@ -0,0 +1,7 @@
from d810.utils import get_all_subclasses
from d810.optimizers.instructions.z3.handler import Z3Rule, Z3Optimizer
from d810.optimizers.instructions.z3.cst import *
from d810.optimizers.instructions.z3.predicates import *
Z3_RULES = [x() for x in get_all_subclasses(Z3Rule)]

View File

@@ -0,0 +1,51 @@
from ida_hexrays import *
from d810.optimizers.instructions.z3.handler import Z3Rule
from d810.ast import AstConstant, AstNode
from d810.ast import minsn_to_ast
from d810.errors import AstEvaluationException
from d810.z3_utils import z3_check_mop_equality
class Z3ConstantOptimization(Z3Rule):
DESCRIPTION = "Detect and replace obfuscated constants"
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("c_res"))
def __init__(self):
super().__init__()
self.min_nb_opcode = 3
self.min_nb_constant = 3
def configure(self, kwargs):
super().configure(kwargs)
if "min_nb_opcode" in kwargs.keys():
self.min_nb_opcode = kwargs["min_nb_opcode"]
if "min_nb_constant" in kwargs.keys():
self.min_nb_constant = kwargs["min_nb_constant"]
def check_and_replace(self, blk, instruction):
tmp = minsn_to_ast(instruction)
if tmp is None:
return None
leaf_info_list, cst_leaf_values, opcodes = tmp.get_information()
if len(leaf_info_list) == 1 and \
len(opcodes) >= self.min_nb_opcode and \
(len(cst_leaf_values) >= self.min_nb_constant):
try:
val_0 = tmp.evaluate_with_leaf_info(leaf_info_list, [0])
val_1 = tmp.evaluate_with_leaf_info(leaf_info_list, [0xffffffff])
if val_0 == val_1:
c_res_mop = mop_t()
c_res_mop.make_number(val_0, tmp.mop.size)
is_ok = z3_check_mop_equality(tmp.mop, c_res_mop)
if is_ok:
tmp.add_leaf("c_res", c_res_mop)
new_instruction = self.get_replacement(tmp)
return new_instruction
return None
except ZeroDivisionError:
pass
except AstEvaluationException as e:
print("Error while evaluating {0}: {1}".format(tmp, e))
pass
return None

View File

@@ -0,0 +1,9 @@
from d810.optimizers.instructions.handler import GenericPatternRule, InstructionOptimizer
class Z3Rule(GenericPatternRule):
pass
class Z3Optimizer(InstructionOptimizer):
RULE_CLASSES = [Z3Rule]

View File

@@ -0,0 +1,78 @@
from ida_hexrays import *
from d810.optimizers.instructions.z3.handler import Z3Rule
from d810.ast import AstLeaf, AstConstant, AstNode
from d810.z3_utils import z3_check_mop_equality, z3_check_mop_inequality
class Z3setzRuleGeneric(Z3Rule):
DESCRIPTION = "Check with Z3 if a m_setz check is always True or False"
PATTERN = AstNode(m_setz,
AstLeaf("x_0"),
AstLeaf("x_1"))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res"))
def check_candidate(self, candidate):
if z3_check_mop_equality(candidate["x_0"].mop, candidate["x_1"].mop):
candidate.add_constant_leaf("val_res", 1, candidate.size)
return True
if z3_check_mop_inequality(candidate["x_0"].mop, candidate["x_1"].mop):
candidate.add_constant_leaf("val_res", 0, candidate.size)
return True
return False
class Z3setnzRuleGeneric(Z3Rule):
DESCRIPTION = "Check with Z3 if a m_setnz check is always True or False"
PATTERN = AstNode(m_setnz,
AstLeaf("x_0"),
AstLeaf("x_1"))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res"))
def check_candidate(self, candidate):
if z3_check_mop_equality(candidate["x_0"].mop, candidate["x_1"].mop):
candidate.add_constant_leaf("val_res", 0, candidate.size)
return True
if z3_check_mop_inequality(candidate["x_0"].mop, candidate["x_1"].mop):
candidate.add_constant_leaf("val_res", 1, candidate.size)
return True
return False
class Z3lnotRuleGeneric(Z3Rule):
DESCRIPTION = "Check with Z3 if a m_lnot check is always True or False"
PATTERN = AstNode(m_lnot,
AstLeaf("x_0"))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res"))
def check_candidate(self, candidate):
val_0_mop = mop_t()
val_0_mop.make_number(0, candidate["x_0"].size)
if z3_check_mop_equality(candidate["x_0"].mop, val_0_mop):
candidate.add_constant_leaf("val_res", 1, candidate.size)
return True
if z3_check_mop_inequality(candidate["x_0"].mop, val_0_mop):
candidate.add_constant_leaf("val_res", 0, candidate.size)
return True
return False
class Z3SmodRuleGeneric(Z3Rule):
DESCRIPTION = "Check with Z3 if a m_setz check is always True or False"
PATTERN = AstNode(m_smod,
AstLeaf("x_0"),
AstConstant("2", 2))
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res"))
def check_candidate(self, candidate):
cst_0_mop = mop_t()
cst_0_mop.make_number(0, candidate.size)
if z3_check_mop_equality(candidate.mop, cst_0_mop):
candidate.add_leaf("val_res", cst_0_mop)
return True
cst_1_mop = mop_t()
cst_1_mop.make_number(1, candidate.size)
if z3_check_mop_equality(candidate.mop, cst_1_mop):
candidate.add_leaf("val_res", cst_1_mop)
return True
return False

477
d810/tracker.py Normal file
View File

@@ -0,0 +1,477 @@
from __future__ import annotations
import logging
from typing import List, Union, Tuple, Dict
from ida_hexrays import *
from d810.emulator import MicroCodeEnvironment, MicroCodeInterpreter
from d810.cfg_utils import change_1way_block_successor, change_2way_block_conditional_successor, duplicate_block
from d810.hexrays_hooks import InstructionDefUseCollector
from d810.hexrays_helpers import equal_mops_ignore_size, get_mop_index, get_blk_index
from d810.hexrays_formatters import format_minsn_t, format_mop_t
# This module can be use to find the instruction that define the value of a mop. Basically, you:
# 1 - Create a MopTracker object with the list of mops to search
# 2 - Call search_backward while specifying the instruction where the search should start
# It will return a list if MopHistory, each MopHistory object of this list:
# * Represents one possible path to compute the searched mops
# * Stores all instructions used to compute the searched mops
#
# You can get the value of one of the searched mop by calling the get_mop_constant_value API of a MopHistory object.
# Behind the scene, it will emulate all microcode instructions on the MopHistory path.
#
# Finally the duplicate_histories API can be used to duplicate microcode blocks so that for each microcode block,
# the searched mops have only one possible values. For instance, this is a preliminary step used in code unflattening.
logger = logging.getLogger('D810.tracker')
class BlockInfo(object):
def __init__(self, blk: mblock_t, ins=None):
self.blk = blk
self.ins_list = []
if ins is not None:
self.ins_list.append(ins)
def get_copy(self) -> BlockInfo:
new_block_info = BlockInfo(self.blk)
new_block_info.ins_list = [x for x in self.ins_list]
return new_block_info
class MopHistory(object):
def __init__(self, searched_mop_list: List[mop_t]):
self.searched_mop_list = [mop_t(x) for x in searched_mop_list]
self.history = []
self.unresolved_mop_list = []
self._mc_interpreter = MicroCodeInterpreter()
self._mc_initial_environment = MicroCodeEnvironment()
self._mc_current_environment = self._mc_initial_environment.get_copy()
self._is_dirty = True
def add_mop_initial_value(self, mop: mop_t, value: int):
self._is_dirty = True
self._mc_initial_environment.define(mop, value)
def get_copy(self) -> MopHistory:
new_mop_history = MopHistory(self.searched_mop_list)
new_mop_history.history = [x.get_copy() for x in self.history]
new_mop_history.unresolved_mop_list = [x for x in self.unresolved_mop_list]
new_mop_history._mc_initial_environment = self._mc_initial_environment.get_copy()
new_mop_history._mc_current_environment = new_mop_history._mc_initial_environment.get_copy()
return new_mop_history
def is_resolved(self) -> bool:
if len(self.unresolved_mop_list) == 0:
return True
for x in self.unresolved_mop_list:
x_value = self._mc_initial_environment.lookup(x, raise_exception=False)
if x_value is None:
return False
return True
@property
def block_path(self) -> List[mblock_t]:
return [blk_info.blk for blk_info in self.history]
@property
def block_serial_path(self) -> List[int]:
return [blk.serial for blk in self.block_path]
def replace_block_in_path(self, old_blk: mblock_t, new_blk: mblock_t) -> bool:
blk_index = get_blk_index(old_blk, self.block_path)
if blk_index > 0:
self.history[blk_index].blk = new_blk
self._is_dirty = True
return True
else:
logger.error("replace_block_in_path: should not happen")
return False
def insert_block_in_path(self, blk: mblock_t, where_index: int):
self.history = self.history[:where_index] + [BlockInfo(blk)] + self.history[where_index:]
self._is_dirty = True
def insert_ins_in_block(self, blk: mblock_t, ins: minsn_t, before=True):
blk_index = get_blk_index(blk, self.block_path)
if blk_index < 0:
return False
blk_info = self.history[blk_index]
if before:
blk_info.ins_list = [ins] + blk_info.ins_list
else:
blk_info.ins_list = blk_info.ins_list + [ins]
self._is_dirty = True
def _execute_microcode(self) -> bool:
if not self._is_dirty:
return True
formatted_mop_searched_list = "['" + "', '".join([format_mop_t(x) for x in self.searched_mop_list]) + "']"
logger.debug("Computing: {0} for path {1}".format(formatted_mop_searched_list, self.block_serial_path))
self._mc_current_environment = self._mc_initial_environment.get_copy()
for blk_info in self.history:
for blk_ins in blk_info.ins_list:
logger.debug("Executing: {0}.{1}".format(blk_info.blk.serial, format_minsn_t(blk_ins)))
if not self._mc_interpreter.eval_instruction(blk_info.blk, blk_ins, self._mc_current_environment):
self._is_dirty = False
return False
self._is_dirty = False
return True
def get_mop_constant_value(self, searched_mop: mop_t) -> Union[None, int]:
if not self._execute_microcode():
return None
return self._mc_interpreter.eval_mop(searched_mop, self._mc_current_environment)
def print_info(self, detailed_info=False):
formatted_mop_searched_list = [format_mop_t(x) for x in self.searched_mop_list]
tmp = ", ".join(["{0}={1}".format(formatted_mop, self.get_mop_constant_value(mop))
for formatted_mop, mop in zip(formatted_mop_searched_list, self.searched_mop_list)])
logger.info("MopHistory: resolved={0}, path={1}, mops={2}"
.format(self.is_resolved(), self.block_serial_path, tmp))
if detailed_info:
str_mop_list = "['" + "', '".join(formatted_mop_searched_list) + "']"
if len(self.block_path) == 0:
logger.info("MopHistory for {0} => nothing".format(str_mop_list))
return
end_blk = self.block_path[-1]
end_ins = end_blk.tail
if self.history[-1].ins_list:
end_ins = self.history[-1].ins_list[-1]
if end_ins:
logger.info("MopHistory for {0} {1}.{2}".format(str_mop_list, end_blk.serial, format_minsn_t(end_ins)))
else:
logger.info("MopHistory for '{0}' {1}.tail".format(str_mop_list, end_blk.serial))
logger.info(" path {0}".format(self.block_serial_path))
for blk_info in self.history:
for blk_ins in blk_info.ins_list:
logger.info(" {0}.{1}".format(blk_info.blk.serial, format_minsn_t(blk_ins)))
def get_standard_and_memory_mop_lists(mop_in: mop_t) -> Tuple[List[mop_t], List[mop_t]]:
if mop_in.t in [mop_r, mop_S]:
return [mop_in], []
elif mop_in.t == mop_v:
return [], [mop_in]
elif mop_in.t == mop_d:
ins_mop_info = InstructionDefUseCollector()
mop_in.d.for_all_ops(ins_mop_info)
return remove_segment_registers(ins_mop_info.unresolved_ins_mops), ins_mop_info.memory_unresolved_ins_mops
else:
logger.warning("Calling get_standard_and_memory_mop_lists with unsupported mop type {0}: '{1}'"
.format(mop_in.t, format_mop_t(mop_in)))
return [], []
# A MopTracker will create new MopTracker to recursively track variable when multiple paths are possible,
# The cur_mop_tracker_nb_path global variable is used to limit the number of MopTracker created
cur_mop_tracker_nb_path = 0
class MopTracker(object):
def __init__(self, searched_mop_list: List[mop_t], max_nb_block=-1, max_path=-1):
self.mba = None
self._unresolved_mops = []
self._memory_unresolved_mops = []
for searched_mop in searched_mop_list:
a, b = get_standard_and_memory_mop_lists(searched_mop)
self._unresolved_mops += a
self._memory_unresolved_mops += b
self.history = MopHistory(searched_mop_list)
self.max_nb_block = max_nb_block
self.max_path = max_path
self.avoid_list = []
self.call_detected = False
self.constant_mops = []
@staticmethod
def reset():
global cur_mop_tracker_nb_path
cur_mop_tracker_nb_path = 0
def add_mop_definition(self, mop: mop_t, cst_value: int):
self.constant_mops.append([mop, cst_value])
self.history.add_mop_initial_value(mop, cst_value)
def get_copy(self) -> MopTracker:
global cur_mop_tracker_nb_path
new_mop_tracker = MopTracker(self._unresolved_mops, self.max_nb_block, self.max_path)
new_mop_tracker._memory_unresolved_mops = [x for x in self._memory_unresolved_mops]
new_mop_tracker.constant_mops = [[x[0], x[1]] for x in self.constant_mops]
new_mop_tracker.history = self.history.get_copy()
cur_mop_tracker_nb_path += 1
return new_mop_tracker
def search_backward(self, blk: mblock_t, ins: minsn_t, avoid_list=None, must_use_pred=None,
stop_at_first_duplication=False) -> List[MopHistory]:
logger.debug("Searching backward (reg): {0}".format([format_mop_t(x) for x in self._unresolved_mops]))
logger.debug("Searching backward (mem): {0}".format([format_mop_t(x) for x in self._memory_unresolved_mops]))
logger.debug("Searching backward (cst): {0}"
.format(["{0}: {1:x}".format(format_mop_t(x[0]), x[1]) for x in self.constant_mops]))
self.mba = blk.mba
self.avoid_list = avoid_list if avoid_list else []
blk_with_multiple_pred = self.search_until_multiple_predecessor(blk, ins)
if self.is_resolved():
logger.debug("MopTracker is resolved: {0}".format(self.history.block_serial_path))
self.history.unresolved_mop_list = [x for x in self._unresolved_mops]
return [self.history]
elif blk_with_multiple_pred is None:
logger.debug("MopTracker unresolved: (blk_with_multiple_pred): {0}".format(self.history.block_serial_path))
self.history.unresolved_mop_list = [x for x in self._unresolved_mops]
return [self.history]
elif self.max_nb_block != -1 and len(self.history.block_serial_path) > self.max_nb_block:
logger.debug("MopTracker unresolved: (max_nb_block): {0}".format(self.history.block_serial_path))
self.history.unresolved_mop_list = [x for x in self._unresolved_mops]
return [self.history]
elif self.max_path != -1 and cur_mop_tracker_nb_path > self.max_path:
logger.debug("MopTracker unresolved: (max_path: {0}".format(cur_mop_tracker_nb_path))
self.history.unresolved_mop_list = [x for x in self._unresolved_mops]
return [self.history]
elif self.call_detected:
logger.debug("MopTracker unresolved: (call): {0}".format(self.history.block_serial_path))
self.history.unresolved_mop_list = [x for x in self._unresolved_mops]
return [self.history]
if stop_at_first_duplication:
self.history.unresolved_mop_list = [x for x in self._unresolved_mops]
return [self.history]
logger.debug("MopTracker creating child because multiple pred: {0}".format(self.history.block_serial_path))
possible_histories = []
if must_use_pred is not None and must_use_pred.serial in blk_with_multiple_pred.predset:
new_tracker = self.get_copy()
possible_histories += new_tracker.search_backward(must_use_pred, None, self.avoid_list, must_use_pred)
else:
for blk_pred_serial in blk_with_multiple_pred.predset:
new_tracker = self.get_copy()
possible_histories += new_tracker.search_backward(self.mba.get_mblock(blk_pred_serial), None,
self.avoid_list, must_use_pred)
return possible_histories
def search_until_multiple_predecessor(self, blk: mblock_t, ins: Union[None, minsn_t] = None) -> Union[None, mblock_t]:
# By default, we start searching from block tail
cur_ins = ins if ins else blk.tail
cur_blk = blk
while not self.is_resolved():
# Explore one block
if cur_blk.serial in self.history.block_serial_path:
self.history.insert_block_in_path(cur_blk, 0)
return None
if cur_blk.serial in self.avoid_list:
self.history.insert_block_in_path(cur_blk, 0)
return None
self.history.insert_block_in_path(cur_blk, 0)
cur_ins = self.blk_find_def_backward(cur_blk, cur_ins)
while cur_ins:
cur_ins = self.blk_find_def_backward(cur_blk, cur_ins)
if cur_blk.npred() > 1:
return cur_blk
elif cur_blk.npred() == 0:
return None
else:
cur_blk = self.mba.get_mblock(cur_blk.predset[0])
cur_ins = cur_blk.tail
# We want to handle cases where the self.is_resolved() is True without doing anything
if len(self.history.block_serial_path) == 0:
self.history.insert_block_in_path(cur_blk, 0)
return None
def is_resolved(self) -> bool:
if (len(self._unresolved_mops) == 0) and (len(self._memory_unresolved_mops) == 0):
return True
for x in self._unresolved_mops:
x_index = get_mop_index(x, [y[0] for y in self.constant_mops])
if x_index == -1:
return False
return True
def _build_ml_list(self, blk: mblock_t) -> Union[None, mlist_t]:
ml = mlist_t()
for unresolved_mop in self._unresolved_mops:
if unresolved_mop.t not in [mop_r, mop_S]:
logger.warning("_build_ml_list: Not supported mop type '{0}'".format(unresolved_mop.t))
return None
blk.append_use_list(ml, unresolved_mop, MUST_ACCESS)
return ml
def blk_find_def_backward(self, blk: mblock_t, ins_start: minsn_t) -> Union[None, minsn_t]:
if self.is_resolved():
return None
ml = self._build_ml_list(blk)
if not ml:
logger.warning("blk_find_def_backward: _build_ml_list failed")
return None
ins_def = self._blk_find_ins_def_backward(blk, ins_start, ml)
if ins_def:
is_ok = self.update_history(blk, ins_def)
if not is_ok:
return None
ins_def = ins_def.prev
return ins_def
def update_history(self, blk: mblock_t, ins_def: minsn_t) -> bool:
logger.debug("Updating history with {0}.{1}".format(blk.serial, format_minsn_t(ins_def)))
self.history.insert_ins_in_block(blk, ins_def, before=True)
if ins_def.opcode == m_call:
self.call_detected = True
return False
ins_mop_info = InstructionDefUseCollector()
ins_def.for_all_ops(ins_mop_info)
for target_mop in ins_mop_info.target_mops:
resolved_mop_index = get_mop_index(target_mop, self._unresolved_mops)
if resolved_mop_index != -1:
logger.debug("Removing {0} from unresolved mop".format(format_mop_t(target_mop)))
self._unresolved_mops.pop(resolved_mop_index)
cleaned_unresolved_ins_mops = remove_segment_registers(ins_mop_info.unresolved_ins_mops)
for ins_def_mop in cleaned_unresolved_ins_mops:
ins_def_mop_index = get_mop_index(ins_def_mop, self._unresolved_mops)
if ins_def_mop_index == -1:
logger.debug("Adding {0} in unresolved mop".format(format_mop_t(ins_def_mop)))
self._unresolved_mops.append(ins_def_mop)
for target_mop in ins_mop_info.target_mops:
resolved_mop_index = get_mop_index(target_mop, self._memory_unresolved_mops)
if resolved_mop_index != -1:
logger.debug("Removing {0} from memory unresolved mop".format(format_mop_t(target_mop)))
self._memory_unresolved_mops.pop(resolved_mop_index)
for ins_def_mem_mop in ins_mop_info.memory_unresolved_ins_mops:
ins_def_mop_index = get_mop_index(ins_def_mem_mop, self._memory_unresolved_mops)
if ins_def_mop_index == -1:
logger.debug("Adding {0} in memory unresolved mop".format(format_mop_t(ins_def_mem_mop)))
self._memory_unresolved_mops.append(ins_def_mem_mop)
return True
def _blk_find_ins_def_backward(self, blk: mblock_t, ins_start: minsn_t, ml: mlist_t) -> Union[None, minsn_t]:
cur_ins = ins_start
while cur_ins is not None:
def_list = blk.build_def_list(cur_ins, MAY_ACCESS | FULL_XDSU)
if ml.has_common(def_list):
return cur_ins
for mem_mop in self._memory_unresolved_mops:
if equal_mops_ignore_size(cur_ins.d, mem_mop):
return cur_ins
cur_ins = cur_ins.prev
return None
def get_block_with_multiple_predecessors(var_histories: List[MopHistory]) -> Tuple[Union[None, mblock_t],
Union[None, Dict[int, List[MopHistory]]]]:
for i, var_history in enumerate(var_histories):
pred_blk = var_history.block_path[0]
for block in var_history.block_path[1:]:
tmp_dict = {pred_blk.serial: [var_history]}
for j in range(i + 1, len(var_histories)):
blk_index = get_blk_index(block, var_histories[j].block_path)
if (blk_index - 1) >= 0:
other_pred = var_histories[j].block_path[blk_index - 1]
if other_pred.serial not in tmp_dict.keys():
tmp_dict[other_pred.serial] = []
tmp_dict[other_pred.serial].append(var_histories[j])
if len(tmp_dict) > 1:
return block, tmp_dict
pred_blk = block
return None, None
def try_to_duplicate_one_block(var_histories: List[MopHistory]) -> Tuple[int, int]:
nb_duplication = 0
nb_change = 0
if (len(var_histories) == 0) or (len(var_histories[0].block_path) == 0):
return nb_duplication, nb_change
mba = var_histories[0].block_path[0].mba
block_to_duplicate, pred_dict = get_block_with_multiple_predecessors(var_histories)
if block_to_duplicate is None:
return nb_duplication, nb_change
logger.debug("Block to duplicate found: {0} with {1} successors"
.format(block_to_duplicate.serial, block_to_duplicate.nsucc()))
i = 0
for pred_serial, pred_history_group in pred_dict.items():
# We do not duplicate first group
if i >= 1:
logger.debug(" Before {0}: {1}"
.format(pred_serial, [var_history.block_serial_path for var_history in pred_history_group]))
pred_block = mba.get_mblock(pred_serial)
duplicated_blk_jmp, duplicated_blk_default = duplicate_block(block_to_duplicate)
nb_duplication += 1 if duplicated_blk_jmp is not None else 0
nb_duplication += 1 if duplicated_blk_default is not None else 0
logger.debug(" Making {0} goto {1}".format(pred_block.serial, duplicated_blk_jmp.serial))
if (pred_block.tail is None) or (not is_mcode_jcond(pred_block.tail.opcode)):
change_1way_block_successor(pred_block, duplicated_blk_jmp.serial)
nb_change += 1
else:
if block_to_duplicate.serial == pred_block.tail.d.b:
change_2way_block_conditional_successor(pred_block, duplicated_blk_jmp.serial)
nb_change += 1
else:
logger.warning(" not sure this is suppose to happen")
change_1way_block_successor(pred_block.mba.get_mblock(pred_block.serial + 1),
duplicated_blk_jmp.serial)
nb_change += 1
block_to_duplicate_default_successor = mba.get_mblock(block_to_duplicate.serial + 1)
logger.debug(" Now, we fix var histories...")
for var_history in pred_history_group:
var_history.replace_block_in_path(block_to_duplicate, duplicated_blk_jmp)
if block_to_duplicate.tail is not None and is_mcode_jcond(block_to_duplicate.tail.opcode):
index_jump_block = get_blk_index(duplicated_blk_jmp, var_history.block_path)
if index_jump_block + 1 < len(var_history.block_path):
original_jump_block_successor = var_history.block_path[index_jump_block + 1]
if original_jump_block_successor.serial == block_to_duplicate_default_successor.serial:
var_history.insert_block_in_path(duplicated_blk_default, index_jump_block + 1)
i += 1
logger.debug(" After {0}: {1}"
.format(pred_serial, [var_history.block_serial_path for var_history in pred_history_group]))
for i, var_history in enumerate(var_histories):
logger.debug(" internal_pass_end.{0}: {1}".format(i, var_history.block_serial_path))
return nb_duplication, nb_change
def duplicate_histories(var_histories: List[MopHistory], max_nb_pass: int = 10) -> Tuple[int, int]:
cur_pass = 0
total_nb_duplication = 0
total_nb_change = 0
logger.info("Trying to fix new var_history...")
for i, var_history in enumerate(var_histories):
logger.info(" start.{0}: {1}".format(i, var_history.block_serial_path))
while cur_pass < max_nb_pass:
logger.debug("Current path {0}".format(cur_pass))
nb_duplication, nb_change = try_to_duplicate_one_block(var_histories)
if nb_change == 0 and nb_duplication == 0:
break
total_nb_duplication += nb_duplication
total_nb_change += nb_change
cur_pass += 1
for i, var_history in enumerate(var_histories):
logger.info(" end.{0}: {1}".format(i, var_history.block_serial_path))
return total_nb_duplication, total_nb_change
def get_segment_register_indexes(mop_list: List[mop_t]) -> List[int]:
# This is a very dirty and probably buggy
segment_register_indexes = []
for i, mop in enumerate(mop_list):
if mop.t == mop_r:
formatted_mop = format_mop_t(mop)
if formatted_mop in ["ds.2", "cs.2", "es.2", "ss.2"]:
segment_register_indexes.append(i)
return segment_register_indexes
def remove_segment_registers(mop_list: List[mop_t]) -> List[mop_t]:
# TODO: instead of doing that, we should add the segment registers to the (global?) emulation environment
segment_register_indexes = get_segment_register_indexes(mop_list)
if len(segment_register_indexes) == 0:
return mop_list
new_mop_list = []
for i, mop in enumerate(mop_list):
if i in segment_register_indexes:
pass
else:
new_mop_list.append(mop)
return new_mop_list

69
d810/utils.py Normal file
View File

@@ -0,0 +1,69 @@
import ctypes
from d810.hexrays_helpers import MSB_TABLE
CTYPE_SIGNED_TABLE = {1: ctypes.c_int8, 2: ctypes.c_int16, 4: ctypes.c_int32, 8: ctypes.c_int64}
CTYPE_UNSIGNED_TABLE = {1: ctypes.c_uint8, 2: ctypes.c_uint16, 4: ctypes.c_uint32, 8: ctypes.c_uint64}
def get_all_subclasses(python_class):
python_class.__subclasses__()
subclasses = set()
check_these = [python_class]
while check_these:
parent = check_these.pop()
for child in parent.__subclasses__():
if child not in subclasses:
subclasses.add(child)
check_these.append(child)
return sorted(subclasses, key=lambda x: x.__name__)
def unsigned_to_signed(unsigned_value, nb_bytes):
return CTYPE_SIGNED_TABLE[nb_bytes](unsigned_value).value
def signed_to_unsigned(signed_value, nb_bytes):
return CTYPE_UNSIGNED_TABLE[nb_bytes](signed_value).value
def get_msb(value, nb_bytes):
return (value & MSB_TABLE[nb_bytes]) >> (nb_bytes * 8 - 1)
def get_add_cf(op1, op2, nb_bytes):
res = op1 + op2
return get_msb((((op1 ^ op2) ^ res) ^ ((op1 ^ res) & (~(op1 ^ op2)))), nb_bytes)
def get_add_of(op1, op2, nb_bytes):
res = op1 + op2
return get_msb(((op1 ^ res) & (~(op1 ^ op2))), nb_bytes)
def get_sub_cf(op1, op2, nb_bytes):
res = op1 - op2
return get_msb((((op1 ^ op2) ^ res) ^ ((op1 ^ res) & (op1 ^ op2))), nb_bytes)
def get_sub_of(op1, op2, nb_bytes):
res = op1 - op2
return get_msb(((op1 ^ res) & (op1 ^ op2)), nb_bytes)
def get_parity_flag(op1, op2, nb_bytes):
tmp = CTYPE_UNSIGNED_TABLE[nb_bytes](op1 - op2).value
return (bin(tmp).count("1") + 1) % 2
def ror(x, n, nb_bits=32):
mask = (2 ** n) - 1
mask_bits = x & mask
return (x >> n) | (mask_bits << (nb_bits - n))
def rol(x, n, nb_bits=32):
return ror(x, nb_bits - n, nb_bits)

158
d810/z3_utils.py Normal file
View File

@@ -0,0 +1,158 @@
import logging
from typing import List, Union
from ida_hexrays import *
from d810.hexrays_helpers import get_mop_index
from d810.hexrays_formatters import format_minsn_t, opcode_to_string
from d810.ast import mop_to_ast, minsn_to_ast, AstLeaf, AstNode
from d810.errors import D810Z3Exception
logger = logging.getLogger('D810.plugin')
z3_file_logger = logging.getLogger('D810.z3_test')
try:
import z3
Z3_INSTALLED = True
except ImportError:
logger.info("Z3 features disabled. Install Z3 to enable them")
Z3_INSTALLED = False
def create_z3_vars(leaf_list: List[AstLeaf]):
if not Z3_INSTALLED:
raise D810Z3Exception("Z3 is not installed")
known_leaf_list = []
known_leaf_z3_var_list = []
for leaf in leaf_list:
if not leaf.is_constant():
leaf_index = get_mop_index(leaf.mop, known_leaf_list)
if leaf_index == -1:
known_leaf_list.append(leaf.mop)
leaf_index = len(known_leaf_list) - 1
if leaf.mop.size in [1, 2, 4, 8]:
# Normally, we should create variable based on their size
# but for now it can cause issue when instructions like XDU are used, hence this ugly fix
# known_leaf_z3_var_list.append(z3.BitVec("x_{0}".format(leaf_index), 8 * leaf.mop.size))
known_leaf_z3_var_list.append(z3.BitVec("x_{0}".format(leaf_index), 32))
pass
else:
known_leaf_z3_var_list.append(z3.BitVec("x_{0}".format(leaf_index), 32))
leaf.z3_var = known_leaf_z3_var_list[leaf_index]
leaf.z3_var_name = "x_{0}".format(leaf_index)
return known_leaf_z3_var_list
def ast_to_z3_expression(ast: Union[AstNode, AstLeaf], use_bitvecval=False):
if not Z3_INSTALLED:
raise D810Z3Exception("Z3 is not installed")
if isinstance(ast, AstLeaf):
if ast.is_constant():
return z3.BitVecVal(ast.value, 32)
return ast.z3_var
if ast.opcode == m_neg:
return -(ast_to_z3_expression(ast.left, use_bitvecval))
elif ast.opcode == m_lnot:
return not (ast_to_z3_expression(ast.left, use_bitvecval))
elif ast.opcode == m_bnot:
return ~(ast_to_z3_expression(ast.left, use_bitvecval))
elif ast.opcode == m_add:
return (ast_to_z3_expression(ast.left, use_bitvecval)) + (ast_to_z3_expression(ast.right, use_bitvecval))
elif ast.opcode == m_sub:
return (ast_to_z3_expression(ast.left, use_bitvecval)) - (ast_to_z3_expression(ast.right, use_bitvecval))
elif ast.opcode == m_mul:
return (ast_to_z3_expression(ast.left, use_bitvecval)) * (ast_to_z3_expression(ast.right, use_bitvecval))
elif ast.opcode == m_udiv:
return z3.UDiv(ast_to_z3_expression(ast.left, use_bitvecval=True),
ast_to_z3_expression(ast.right, use_bitvecval=True))
elif ast.opcode == m_sdiv:
return (ast_to_z3_expression(ast.left, use_bitvecval)) / (ast_to_z3_expression(ast.right, use_bitvecval))
elif ast.opcode == m_umod:
return z3.URem(ast_to_z3_expression(ast.left, use_bitvecval), ast_to_z3_expression(ast.right, use_bitvecval))
elif ast.opcode == m_smod:
return (ast_to_z3_expression(ast.left, use_bitvecval)) % (ast_to_z3_expression(ast.right, use_bitvecval))
elif ast.opcode == m_or:
return (ast_to_z3_expression(ast.left, use_bitvecval)) | (ast_to_z3_expression(ast.right, use_bitvecval))
elif ast.opcode == m_and:
return (ast_to_z3_expression(ast.left, use_bitvecval)) & (ast_to_z3_expression(ast.right, use_bitvecval))
elif ast.opcode == m_xor:
return (ast_to_z3_expression(ast.left, use_bitvecval)) ^ (ast_to_z3_expression(ast.right, use_bitvecval))
elif ast.opcode == m_shl:
return (ast_to_z3_expression(ast.left, use_bitvecval)) << (ast_to_z3_expression(ast.right, use_bitvecval))
elif ast.opcode == m_shr:
return z3.LShR(ast_to_z3_expression(ast.left, use_bitvecval), ast_to_z3_expression(ast.right, use_bitvecval))
elif ast.opcode == m_sar:
return (ast_to_z3_expression(ast.left, use_bitvecval)) >> (ast_to_z3_expression(ast.right, use_bitvecval))
elif ast.opcode in [m_xdu, m_xds, m_low, m_high]:
return ast_to_z3_expression(ast.left, use_bitvecval)
raise D810Z3Exception("Z3 evaluation: Unknown opcode {0} for {1}".format(opcode_to_string(ast.opcode), ast))
def mop_list_to_z3_expression_list(mop_list: List[mop_t]):
if not Z3_INSTALLED:
raise D810Z3Exception("Z3 is not installed")
ast_list = [mop_to_ast(mop) for mop in mop_list]
ast_leaf_list = []
for ast in ast_list:
ast_leaf_list += ast.get_leaf_list()
_ = create_z3_vars(ast_leaf_list)
return [ast_to_z3_expression(ast) for ast in ast_list]
def z3_check_mop_equality(mop1: mop_t, mop2: mop_t) -> bool:
if not Z3_INSTALLED:
raise D810Z3Exception("Z3 is not installed")
z3_mop1, z3_mop2 = mop_list_to_z3_expression_list([mop1, mop2])
s = z3.Solver()
s.add(z3.Not(z3_mop1 == z3_mop2))
if s.check().r == -1:
return True
return False
def z3_check_mop_inequality(mop1: mop_t, mop2: mop_t) -> bool:
if not Z3_INSTALLED:
raise D810Z3Exception("Z3 is not installed")
z3_mop1, z3_mop2 = mop_list_to_z3_expression_list([mop1, mop2])
s = z3.Solver()
s.add(z3_mop1 == z3_mop2)
if s.check().r == -1:
return True
return False
def rename_leafs(leaf_list: List[AstLeaf]) -> List[str]:
if not Z3_INSTALLED:
raise D810Z3Exception("Z3 is not installed")
known_leaf_list = []
for leaf in leaf_list:
if not leaf.is_constant() and leaf.mop.t != mop_z:
leaf_index = get_mop_index(leaf.mop, known_leaf_list)
if leaf_index == -1:
known_leaf_list.append(leaf.mop)
leaf_index = len(known_leaf_list) - 1
leaf.z3_var_name = "x_{0}".format(leaf_index)
return ["x_{0} = BitVec('x_{0}', {1})".format(i, 8 * leaf.size) for i, leaf in enumerate(known_leaf_list)]
def log_z3_instructions(original_ins: minsn_t, new_ins: minsn_t):
if not Z3_INSTALLED:
raise D810Z3Exception("Z3 is not installed")
orig_mba_tree = minsn_to_ast(original_ins)
new_mba_tree = minsn_to_ast(new_ins)
if orig_mba_tree is None or new_mba_tree is None:
return None
orig_leaf_list = orig_mba_tree.get_leaf_list()
new_leaf_list = new_mba_tree.get_leaf_list()
var_def_list = rename_leafs(orig_leaf_list + new_leaf_list)
z3_file_logger.info("print('Testing: {0} == {1}')".format(format_minsn_t(original_ins), format_minsn_t(new_ins)))
for var_def in var_def_list:
z3_file_logger.info("{0}".format(var_def))
removed_xdu = "{0}".format(orig_mba_tree).replace("xdu","")
z3_file_logger.info("original_expr = {0}".format(removed_xdu))
removed_xdu = "{0}".format(new_mba_tree).replace("xdu","")
z3_file_logger.info("new_expr = {0}".format(removed_xdu))
z3_file_logger.info("prove(original_expr == new_expr)\n")