diff options
| author | serpilliere <serpilliere@users.noreply.github.com> | 2021-12-02 08:39:18 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-12-02 08:39:18 +0100 |
| commit | 0cd2cad02dd1b300d61bdc985b09de65f92261b4 (patch) | |
| tree | 7b2f0f0d5d933d807be7b57f331de5a0431cbc67 | |
| parent | ea5abb5090ef0c8fac2ddde6f99f6f3c4fff432c (diff) | |
| parent | b9c8746116de2b223d441a90f783f8c16ab876c3 (diff) | |
| download | miasm-0cd2cad02dd1b300d61bdc985b09de65f92261b4.tar.gz miasm-0cd2cad02dd1b300d61bdc985b09de65f92261b4.zip | |
Merge pull request #1400 from serpilliere/color_ircfg
Colorize ir/asm
| -rw-r--r-- | miasm/arch/aarch64/arch.py | 69 | ||||
| -rw-r--r-- | miasm/arch/arm/arch.py | 96 | ||||
| -rw-r--r-- | miasm/arch/arm/lifter_model_call.py | 2 | ||||
| -rw-r--r-- | miasm/arch/mep/arch.py | 33 | ||||
| -rw-r--r-- | miasm/arch/mips32/arch.py | 15 | ||||
| -rw-r--r-- | miasm/arch/msp430/arch.py | 24 | ||||
| -rw-r--r-- | miasm/arch/ppc/arch.py | 23 | ||||
| -rw-r--r-- | miasm/arch/x86/arch.py | 59 | ||||
| -rw-r--r-- | miasm/core/asmblock.py | 10 | ||||
| -rw-r--r-- | miasm/core/cpu.py | 14 | ||||
| -rw-r--r-- | miasm/core/graph.py | 12 | ||||
| -rw-r--r-- | miasm/core/utils.py | 24 | ||||
| -rw-r--r-- | miasm/ir/ir.py | 117 |
13 files changed, 485 insertions, 13 deletions
diff --git a/miasm/arch/aarch64/arch.py b/miasm/arch/aarch64/arch.py index 4c0bcb09..050aa638 100644 --- a/miasm/arch/aarch64/arch.py +++ b/miasm/arch/aarch64/arch.py @@ -14,6 +14,8 @@ from miasm.arch.aarch64.regs import * from miasm.core.cpu import log as log_cpu from miasm.core.modint import mod_size2int from miasm.core.asm_ast import AstInt, AstId, AstMem, AstOp +from miasm.ir.ir import color_expr_html +from miasm.core import utils log = logging.getLogger("aarch64dis") console_handler = logging.StreamHandler() @@ -367,6 +369,73 @@ class instruction_aarch64(instruction): else: raise NotImplementedError("bad op") + @staticmethod + def arg2html(expr, index=None, loc_db=None): + wb = False + if expr.is_id() or expr.is_int() or expr.is_loc(): + return color_expr_html(expr, loc_db) + elif isinstance(expr, m2_expr.ExprOp) and expr.op in shift_expr: + op_str = shift_str[shift_expr.index(expr.op)] + return "%s %s %s" % ( + color_expr_html(expr.args[0], loc_db), + utils.set_html_text_color(op_str, utils.COLOR_OP), + color_expr_html(expr.args[1], loc_db) + ) + elif isinstance(expr, m2_expr.ExprOp) and expr.op == "slice_at": + return "%s LSL %s" % ( + color_expr_html(expr.args[0], loc_db), + color_expr_html(expr.args[1], loc_db) + ) + elif isinstance(expr, m2_expr.ExprOp) and expr.op in extend_lst: + op_str = expr.op + return "%s %s %s" % ( + color_expr_html(expr.args[0], loc_db), + op_str, + color_expr_html(expr.args[1], loc_db) + ) + elif isinstance(expr, m2_expr.ExprOp) and expr.op == "postinc": + if int(expr.args[1]) != 0: + return "[%s], %s" % ( + color_expr_html(expr.args[0], loc_db), + color_expr_html(expr.args[1], loc_db) + ) + else: + return "[%s]" % (color_expr_html(expr.args[0], loc_db)) + elif isinstance(expr, m2_expr.ExprOp) and expr.op == "preinc_wb": + if int(expr.args[1]) != 0: + return "[%s, %s]!" % ( + color_expr_html(expr.args[0], loc_db), + color_expr_html(expr.args[1], loc_db) + ) + else: + return "[%s]" % (color_expr_html(expr.args[0], loc_db)) + elif isinstance(expr, m2_expr.ExprOp) and expr.op == "preinc": + if len(expr.args) == 1: + return "[%s]" % (color_expr_html(expr.args[0], loc_db)) + elif not isinstance(expr.args[1], m2_expr.ExprInt) or int(expr.args[1]) != 0: + return "[%s, %s]" % ( + color_expr_html(expr.args[0], loc_db), + color_expr_html(expr.args[1], loc_db) + ) + else: + return "[%s]" % color_expr_html(expr.args[0], loc_db) + elif isinstance(expr, m2_expr.ExprOp) and expr.op == 'segm': + arg = color_expr_html(expr.args[1], loc_db) + if isinstance(arg, m2_expr.ExprId): + arg = str(arg) + elif arg.op == 'LSL' and int(arg.args[1]) == 0: + arg = str(arg.args[0]) + else: + arg = "%s %s %s" % ( + color_expr_html(arg.args[0], loc_db), + utils.set_html_text_color(arg.op, utils.COLOR_OP), + color_expr_html(arg.args[1], loc_db) + ) + return '[%s, %s]' % (color_expr_html(expr.args[0], loc_db), arg) + + else: + raise NotImplementedError("bad op") + def dstflow(self): return self.name in BRCOND + ["B", "BL", "BR", "BLR"] diff --git a/miasm/arch/arm/arch.py b/miasm/arch/arm/arch.py index 6c5b0ce2..affa9866 100644 --- a/miasm/arch/arm/arch.py +++ b/miasm/arch/arm/arch.py @@ -12,6 +12,8 @@ from miasm.core.bin_stream import bin_stream import miasm.arch.arm.regs as regs_module from miasm.arch.arm.regs import * from miasm.core.asm_ast import AstInt, AstId, AstMem, AstOp +from miasm.ir.ir import color_expr_html +from miasm.core import utils # A1 encoding @@ -426,6 +428,83 @@ class instruction_arm(instruction): o += "!" return o + @staticmethod + def arg2html(expr, index=None, loc_db=None): + wb = False + if expr.is_id() or expr.is_int() or expr.is_loc(): + return color_expr_html(expr, loc_db) + if isinstance(expr, ExprOp) and expr.op in expr2shift_dct: + if len(expr.args) == 1: + return '%s %s' % (color_expr_html(expr.args[0], loc_db), expr2shift_dct[expr.op]) + elif len(expr.args) == 2: + return '%s %s %s' % (color_expr_html(expr.args[0], loc_db), expr2shift_dct[expr.op], expr.args[1]) + else: + raise NotImplementedError('zarb arg2str') + + + sb = False + if isinstance(expr, ExprOp) and expr.op == "sbit": + sb = True + expr = expr.args[0] + if isinstance(expr, ExprOp) and expr.op == "reglist": + o = [gpregs.expr.index(x) for x in expr.args] + out = reglist2html(o) + if sb: + out += "^" + return out + + + if isinstance(expr, ExprOp) and expr.op == 'wback': + wb = True + expr = expr.args[0] + if isinstance(expr, ExprId): + out = color_expr_html(expr, loc_db) + if wb: + out += "!" + return out + + if not isinstance(expr, ExprMem): + return color_expr_html(expr, loc_db) + + expr = expr.ptr + if isinstance(expr, ExprOp) and expr.op == 'wback': + wb = True + expr = expr.args[0] + + + if isinstance(expr, ExprId): + r, s = expr, None + elif len(expr.args) == 1 and isinstance(expr.args[0], ExprId): + r, s = expr.args[0], None + elif isinstance(expr.args[0], ExprId): + r, s = expr.args[0], expr.args[1] + else: + r, s = expr.args[0].args + if isinstance(s, ExprOp) and s.op in expr2shift_dct: + s = ' '.join( + str(x) + for x in ( + color_expr_html(s.args[0], loc_db), + utils.set_html_text_color(expr2shift_dct[s.op], utils.COLOR_OP), + color_expr_html(s.args[1], loc_db) + ) + ) + + if isinstance(expr, ExprOp) and expr.op == 'postinc': + o = '[%s]' % color_expr_html(r, loc_db) + if s and not (isinstance(s, ExprInt) and int(s) == 0): + o += ', %s' % color_expr_html(s, loc_db) + else: + if s and not (isinstance(s, ExprInt) and int(s) == 0): + o = '[%s, %s]' % (color_expr_html(r, loc_db), color_expr_html(s, loc_db)) + else: + o = '[%s]' % color_expr_html(r, loc_db) + + + if wb: + o += "!" + return o + def dstflow(self): if self.is_subcall(): @@ -1258,6 +1337,23 @@ def reglist2str(rlist): i = j + 1 return "{" + ", ".join(out) + '}' +def reglist2html(rlist): + out = [] + i = 0 + while i < len(rlist): + j = i + 1 + while j < len(rlist) and rlist[j] < 13 and rlist[j] == rlist[j - 1] + 1: + j += 1 + j -= 1 + if j < i + 2: + out.append(color_expr_html(regs_expr[rlist[i]], None)) + i += 1 + else: + out.append(color_expr_html(regs_expr[rlist[i]], None) + '-' + color_expr_html(regs_expr[rlist[j]], None)) + i = j + 1 + out = utils.fix_html_chars("{") + ", ".join(out) + utils.fix_html_chars("}") + return out + class arm_rlist(arm_arg): parser = gpreg_list diff --git a/miasm/arch/arm/lifter_model_call.py b/miasm/arch/arm/lifter_model_call.py index f9c39ab6..9aac74a4 100644 --- a/miasm/arch/arm/lifter_model_call.py +++ b/miasm/arch/arm/lifter_model_call.py @@ -63,7 +63,7 @@ class LifterModelCallArml(LifterModelCallArmlBase): call_assignblk, AssignBlock([ExprAssign(self.IRDst, loc_next_expr)], instr), ] - e_do = IRBlock(loc_do, call_assignblks) + e_do = IRBlock(self.loc_db, loc_do, call_assignblks) assignblks_out = [ AssignBlock([ExprAssign(self.IRDst, dst_cond)], instr) ] diff --git a/miasm/arch/mep/arch.py b/miasm/arch/mep/arch.py index a6b994ac..67dd5288 100644 --- a/miasm/arch/mep/arch.py +++ b/miasm/arch/mep/arch.py @@ -10,6 +10,7 @@ from miasm.core.asm_ast import AstId, AstMem from miasm.arch.mep.regs import * import miasm.arch.mep.regs as mep_regs_module # will be used to set mn_mep.regs +from miasm.ir.ir import color_expr_html # Note: pyparsing is used to alter the way special operands are parsed @@ -97,6 +98,38 @@ class instruction_mep(instruction): to do with a '%s' instance." % type(expr) raise Disasm_Exception(message) + @staticmethod + def arg2html(expr, pos=None, loc_db=None): + """Convert mnemonics arguments into readable html strings according to the + MeP-c4 architecture manual and their internal types + + Notes: + - it must be implemented ! However, a simple 'return str(expr)' + could do the trick. + - it is used to mimic objdump output + + Args: + expr: argument as a miasm expression + pos: position index in the arguments list + """ + + if isinstance(expr, ExprId) or isinstance(expr, ExprInt) or isinstance(expr, ExprLoc): + return color_expr_html(expr, loc_db) + + elif isinstance(expr, ExprMem) and (isinstance(expr.ptr, ExprId) or isinstance(expr.ptr, ExprInt)): + return "(%s)" % color_expr_html(expr.ptr, loc_db) + + elif isinstance(expr, ExprMem) and isinstance(expr.ptr, ExprOp): + return "%s(%s)" % ( + color_expr_html(expr.ptr.args[1], loc_db), + color_expr_html(expr.ptr.args[0], loc_db) + ) + + # Raise an exception if the expression type was not processed + message = "instruction_mep.arg2str(): don't know what \ + to do with a '%s' instance." % type(expr) + raise Disasm_Exception(message) + def __str__(self): """Return the mnemonic as a string. diff --git a/miasm/arch/mips32/arch.py b/miasm/arch/mips32/arch.py index 803f9eef..3d4aa356 100644 --- a/miasm/arch/mips32/arch.py +++ b/miasm/arch/mips32/arch.py @@ -9,6 +9,7 @@ from miasm.expression.expression import ExprMem, ExprInt, ExprId, ExprOp, ExprLo from miasm.core.bin_stream import bin_stream import miasm.arch.mips32.regs as regs import miasm.core.cpu as cpu +from miasm.ir.ir import color_expr_html from miasm.core.asm_ast import AstInt, AstId, AstMem, AstOp @@ -75,6 +76,20 @@ class instruction_mips32(cpu.instruction): assert(len(arg.args) == 2 and arg.op == '+') return "%s(%s)"%(arg.args[1], arg.args[0]) + @staticmethod + def arg2html(expr, index=None, loc_db=None): + if expr.is_id() or expr.is_int() or expr.is_loc(): + return color_expr_html(expr, loc_db) + assert(isinstance(expr, ExprMem)) + arg = expr.ptr + if isinstance(arg, ExprId): + return "(%s)"%color_expr_html(arg, loc_db) + assert(len(arg.args) == 2 and arg.op == '+') + return "%s(%s)"%( + color_expr_html(arg.args[1], loc_db), + color_expr_html(arg.args[0], loc_db) + ) + def dstflow(self): if self.name == 'BREAK': return False diff --git a/miasm/arch/msp430/arch.py b/miasm/arch/msp430/arch.py index 8d208d26..dbe93fd2 100644 --- a/miasm/arch/msp430/arch.py +++ b/miasm/arch/msp430/arch.py @@ -11,6 +11,7 @@ from miasm.core.bin_stream import bin_stream import miasm.arch.msp430.regs as regs_module from miasm.arch.msp430.regs import * from miasm.core.asm_ast import AstInt, AstId, AstMem, AstOp +from miasm.ir.ir import color_expr_html log = logging.getLogger("msp430dis") console_handler = logging.StreamHandler() @@ -130,6 +131,29 @@ class instruction_msp430(instruction): raise NotImplementedError('unknown instance expr = %s' % type(expr)) return o + @staticmethod + def arg2html(expr, index=None, loc_db=None): + if isinstance(expr, ExprId) or isinstance(expr, ExprInt) or expr.is_loc(): + return color_expr_html(expr, loc_db) + elif isinstance(expr, ExprOp) and expr.op == "autoinc": + o = "@%s+" % color_expr_html(expr.args[0], loc_db) + elif isinstance(expr, ExprMem): + if isinstance(expr.ptr, ExprId): + if index == 0: + o = "@%s" % color_expr_html(expr.ptr, loc_db) + else: + o = "0x0(%s)" % color_expr_html(expr.ptr, loc_db) + elif isinstance(expr.ptr, ExprInt): + o = "@%s" % color_expr_html(expr.ptr, loc_db) + elif isinstance(expr.ptr, ExprOp): + o = "%s(%s)" % ( + color_expr_html(expr.ptr.args[1], loc_db), + color_expr_html(expr.ptr.args[0], loc_db) + ) + else: + raise NotImplementedError('unknown instance expr = %s' % type(expr)) + return o + def dstflow2label(self, loc_db): expr = self.args[0] diff --git a/miasm/arch/ppc/arch.py b/miasm/arch/ppc/arch.py index 41259180..d4edc58e 100644 --- a/miasm/arch/ppc/arch.py +++ b/miasm/arch/ppc/arch.py @@ -9,6 +9,7 @@ from miasm.core.bin_stream import bin_stream import miasm.arch.ppc.regs as regs_module from miasm.arch.ppc.regs import * from miasm.core.asm_ast import AstInt, AstId, AstMem, AstOp +from miasm.ir.ir import color_expr_html log = logging.getLogger("ppcdis") console_handler = logging.StreamHandler() @@ -94,6 +95,28 @@ class instruction_ppc(instruction): return str(e) + + @staticmethod + def arg2html(e, pos = None, loc_db=None): + if isinstance(e, ExprId) or isinstance(e, ExprInt) or isinstance(e, ExprLoc): + return color_expr_html(e, loc_db) + elif isinstance(e, ExprMem): + addr = e.ptr + if isinstance(addr, ExprInt) or isinstance(addr, ExprId): + out = '(%s)'%color_expr_html(addr, loc_db) + elif isinstance(addr, ExprOp): + if len(addr.args) == 1: + out = '(%s)'%color_expr_html(addr, loc_db) + elif len(addr.args) == 2: + out = '%s(%s)'%(color_expr_html(addr.args[1], loc_db), color_expr_html(addr.args[0], loc_db)) + else: + raise NotImplementedError('More than two args to ExprOp of address') + else: + raise NotImplementedError('Invalid memory expression') + return out + + return color_expr_html(e, loc_db) + @staticmethod def is_conditional_jump(s): return (s[0] == 'B' and diff --git a/miasm/arch/x86/arch.py b/miasm/arch/x86/arch.py index a886e799..ae58e69b 100644 --- a/miasm/arch/x86/arch.py +++ b/miasm/arch/x86/arch.py @@ -6,7 +6,7 @@ import re from future.utils import viewitems -from miasm.core.utils import int_to_byte +from miasm.core import utils from miasm.expression.expression import * from pyparsing import * from miasm.core.cpu import * @@ -14,6 +14,7 @@ from collections import defaultdict import miasm.arch.x86.regs as regs_module from miasm.arch.x86.regs import * from miasm.core.asm_ast import AstNode, AstInt, AstId, AstMem, AstOp +from miasm.ir.ir import color_expr_html log = logging.getLogger("x86_arch") @@ -570,6 +571,26 @@ class instruction_x86(instruction): o = "REPE %s" % o return o + def to_html(self, loc_db=None): + o = super(instruction_x86, self).to_html(loc_db) + if self.additional_info.g1.value & 1: + text = utils.set_html_text_color("LOCK", utils.COLOR_MNEMO) + o = "%s %s" % (text, o) + if self.additional_info.g1.value & 2: + if getattr(self.additional_info.prefixed, 'default', b"") != b"\xF2": + text = utils.set_html_text_color("REPNE", utils.COLOR_MNEMO) + o = "%s %s" % (text, o) + if self.additional_info.g1.value & 8: + if getattr(self.additional_info.prefixed, 'default', b"") != b"\xF3": + text = utils.set_html_text_color("REP", utils.COLOR_MNEMO) + o = "%s %s" % (text, o) + elif self.additional_info.g1.value & 4: + if getattr(self.additional_info.prefixed, 'default', b"") != b"\xF3": + text = utils.set_html_text_color("REPE", utils.COLOR_MNEMO) + o = "%s %s" % (text, o) + return o + + def get_args_expr(self): args = [] for a in self.args: @@ -612,6 +633,40 @@ class instruction_x86(instruction): return "%s" % o + @staticmethod + def arg2html(expr, index=None, loc_db=None): + if expr.is_id() or expr.is_int() or expr.is_loc(): + o = color_expr_html(expr, loc_db) + elif ((isinstance(expr, ExprOp) and expr.op == 'far' and + isinstance(expr.args[0], ExprMem)) or + isinstance(expr, ExprMem)): + if isinstance(expr, ExprOp): + prefix, expr = "FAR ", expr.args[0] + else: + prefix = "" + sz = SIZE2MEMPREFIX[expr.size] + sz = '<font color="%s">%s</font>' % (utils.COLOR_MEM, sz) + segm = "" + if is_mem_segm(expr): + segm = "%s:" % expr.ptr.args[0] + expr = expr.ptr.args[1] + else: + expr = expr.ptr + if isinstance(expr, ExprOp): + s = color_expr_html(expr, loc_db)#.replace('(', '').replace(')', '') + else: + s = color_expr_html(expr, loc_db) + o = prefix + sz + ' PTR %s[%s]' % (segm, s) + elif isinstance(expr, ExprOp) and expr.op == 'segm': + o = "%s:%s" % ( + color_expr_html(expr.args[0], loc_db), + color_expr_html(expr.args[1], loc_db) + ) + else: + raise ValueError('check this %r' % expr) + return "%s" % o + + class mn_x86(cls_mn): name = "x86" @@ -864,7 +919,7 @@ class mn_x86(cls_mn): if self.rex_b.value: rex |= 0x1 if rex != 0x40 or self.rex_p.value == 1: - v = int_to_byte(rex) + v + v = utils.int_to_byte(rex) + v if hasattr(self, 'no_rex'): return None diff --git a/miasm/core/asmblock.py b/miasm/core/asmblock.py index 47bffb34..e92034fe 100644 --- a/miasm/core/asmblock.py +++ b/miasm/core/asmblock.py @@ -35,6 +35,9 @@ class AsmRaw(object): def to_string(self, loc_db): return str(self) + def to_html(self, loc_db): + return str(self) + class AsmConstraint(object): c_to = "c_to" @@ -439,6 +442,9 @@ class AsmCFG(DiGraph): # between the two graphs self.add_edge(*edge, constraint=graph.edges2constraint[edge]) + def escape_text(self, text): + return text + def node2lines(self, node): loc_key_name = self.loc_db.pretty_str(node) @@ -462,9 +468,9 @@ class AsmCFG(DiGraph): if self._dot_offset: yield [self.DotCellDescription(text="%.8X" % line.offset, attr={}), - self.DotCellDescription(text=line.to_string(self.loc_db), attr={})] + self.DotCellDescription(text=line.to_html(self.loc_db), attr={})] else: - yield self.DotCellDescription(text=line.to_string(self.loc_db), attr={}) + yield self.DotCellDescription(text=line.to_html(self.loc_db), attr={}) def node_attr(self, node): block = self._loc_key_to_block.get(node, None) diff --git a/miasm/core/cpu.py b/miasm/core/cpu.py index d9c1955b..fac03248 100644 --- a/miasm/core/cpu.py +++ b/miasm/core/cpu.py @@ -19,6 +19,7 @@ from miasm.expression.simplifications import expr_simp from miasm.core.asm_ast import AstNode, AstInt, AstId, AstOp +from miasm.core import utils from future.utils import with_metaclass log = logging.getLogger("cpuhelper") @@ -1005,6 +1006,19 @@ class instruction(object): o += self.gen_args(args) return o + def to_html(self, loc_db=None): + out = "%-10s " % self.name + out = '<font color="%s">%s</font>' % (utils.COLOR_MNEMO, out) + + args = [] + for i, arg in enumerate(self.args): + if not isinstance(arg, m2_expr.Expr): + raise ValueError('zarb arg type') + x = self.arg2html(arg, i, loc_db) + args.append(x) + out += self.gen_args(args) + return out + def get_asm_offset(self, expr): return m2_expr.ExprInt(self.offset, expr.size) diff --git a/miasm/core/graph.py b/miasm/core/graph.py index 1ee30c9f..0dfd7e6a 100644 --- a/miasm/core/graph.py +++ b/miasm/core/graph.py @@ -20,6 +20,9 @@ class DiGraph(object): # N -> Nodes N2 with a edge (N2 -> N) self._nodes_pred = {} + self.escape_chars = re.compile('[' + re.escape('{}') + '&|<>' + ']') + + def __repr__(self): out = [] for node in self._nodes: @@ -239,10 +242,12 @@ class DiGraph(object): **attr)) ) + def escape_text(self, text): + return self.escape_chars.sub(self._fix_chars, text) + def dot(self): """Render dot graph with HTML""" - escape_chars = re.compile('[' + re.escape('{}') + '&|<>' + ']') td_attr = {'align': 'left'} nodes_attr = {'shape': 'Mrecord', 'fontname': 'Courier New'} @@ -266,7 +271,7 @@ class DiGraph(object): for col in lineDesc: out_render += "<td %s>%s</td>" % ( self._attr2str(td_attr, col.attr), - escape_chars.sub(self._fix_chars, str(col.text))) + self.escape_text(str(col.text))) node_html_lines.append(out_render) node_html_lines = ('<tr>' + @@ -302,7 +307,6 @@ class DiGraph(object): self.gv = graphviz.Digraph('html_table') self._dot_offset = False - escape_chars = re.compile('[' + re.escape('{}') + '&|<>' + ']') td_attr = {'align': 'left'} nodes_attr = {'shape': 'Mrecord', 'fontname': 'Courier New'} @@ -320,7 +324,7 @@ class DiGraph(object): for col in lineDesc: out_render += "<td %s>%s</td>" % ( self._attr2str(td_attr, col.attr), - escape_chars.sub(self._fix_chars, str(col.text))) + self.escape_text(str(col.text))) node_html_lines.append(out_render) node_html_lines = ('<tr>' + diff --git a/miasm/core/utils.py b/miasm/core/utils.py index cfe96de4..41bf78c1 100644 --- a/miasm/core/utils.py +++ b/miasm/core/utils.py @@ -1,4 +1,5 @@ from __future__ import print_function +import re import sys from builtins import range import struct @@ -16,6 +17,28 @@ from future.utils import viewitems import collections +COLOR_INT = "azure4" +COLOR_ID = "forestgreen"#"chartreuse3" +COLOR_MEM = "deeppink4" +COLOR_OP_FUNC = "blue1" +COLOR_LOC = "darkslateblue" +COLOR_OP = "black" + +COLOR_MNEMO = "blue1" + +ESCAPE_CHARS = re.compile('[' + re.escape('{}') + '&|<>' + ']') + +def set_html_text_color(text, color): + return '<font color="%s">%s</font>' % (color, text) + + +def _fix_chars(token): + return "&#%04d;" % ord(token.group()) + + +def fix_html_chars(text): + return ESCAPE_CHARS.sub(_fix_chars, str(text)) + upck8 = lambda x: struct.unpack('B', x)[0] upck16 = lambda x: struct.unpack('H', x)[0] upck32 = lambda x: struct.unpack('I', x)[0] @@ -261,3 +284,4 @@ class BoundedDict(DictMixin): def __iter__(self): return iter(self._data) + diff --git a/miasm/ir/ir.py b/miasm/ir/ir.py index b712b0ee..e9b86899 100644 --- a/miasm/ir/ir.py +++ b/miasm/ir/ir.py @@ -27,7 +27,10 @@ import miasm.expression.expression as m2_expr from miasm.expression.expression_helper import get_missing_interval from miasm.core.asmblock import AsmBlock, AsmBlockBad, AsmConstraint from miasm.core.graph import DiGraph +from miasm.ir.translators import Translator from functools import reduce +from miasm.core import utils +import re def _expr_loc_to_symb(expr, loc_db): @@ -44,6 +47,108 @@ def _expr_loc_to_symb(expr, loc_db): name = sorted(names)[0] return m2_expr.ExprId(name, expr.size) + +ESCAPE_CHARS = re.compile('[' + re.escape('{}') + '&|<>' + ']') + +class TranslatorHtml(Translator): + __LANG__ = "custom_expr_color" + + @staticmethod + def _fix_chars(token): + return "&#%04d;" % ord(token.group()) + + def __init__(self, loc_db=None, **kwargs): + super(TranslatorHtml, self).__init__(**kwargs) + self.loc_db = loc_db + + def str_protected_child(self, child, parent): + return ("(%s)" % ( + self.from_expr(child)) if m2_expr.should_parenthesize_child(child, parent) + else self.from_expr(child) + ) + + def from_ExprInt(self, expr): + out = str(expr) + out = '<font color="%s">%s</font>' % (utils.COLOR_INT, out) + return out + + def from_ExprId(self, expr): + out = str(expr) + out = '<font color="%s">%s</font>' % (utils.COLOR_ID, out) + return out + + def from_ExprLoc(self, expr): + + if self.loc_db is None: + name = ESCAPE_CHARS.sub(self._fix_chars, str((expr))) + else: + names = self.loc_db.get_location_names(expr.loc_key) + if not names: + name = self.loc_db.pretty_str(expr.loc_key) + else: + # Use only one name for readability + name = sorted(names)[0] + name = ESCAPE_CHARS.sub(self._fix_chars, name) + out = '<font color="%s">%s</font>' % (utils.COLOR_LOC, name) + return out + + def from_ExprMem(self, expr): + ptr = self.from_expr(expr.ptr) + size = '@' + str(expr.size) + size = '<font color="%s">%s</font>' % (utils.COLOR_MEM, size) + bracket_left = ESCAPE_CHARS.sub(self._fix_chars, '[') + bracket_right = ESCAPE_CHARS.sub(self._fix_chars, ']') + out = '%s%s%s%s' % (size, bracket_left, ptr, bracket_right) + return out + + def from_ExprSlice(self, expr): + base = self.from_expr(expr.arg) + start = str(expr.start) + stop = str(expr.stop) + bracket_left = ESCAPE_CHARS.sub(self._fix_chars, '[') + bracket_right = ESCAPE_CHARS.sub(self._fix_chars, ']') + out = "(%s)%s%s:%s%s" % (base, bracket_left, start, stop, bracket_right) + return out + + def from_ExprCompose(self, expr): + out = ESCAPE_CHARS.sub(self._fix_chars, "{") + out += ", ".join(["%s, %s, %s" % (self.from_expr(subexpr), + str(idx), + str(idx + subexpr.size)) + for idx, subexpr in expr.iter_args()]) + out += ESCAPE_CHARS.sub(self._fix_chars, "}") + return out + + def from_ExprCond(self, expr): + cond = self.str_protected_child(expr.cond, expr) + src1 = self.from_expr(expr.src1) + src2 = self.from_expr(expr.src2) + out = "%s?(%s,%s)" % (cond, src1, src2) + return out + + def from_ExprOp(self, expr): + op = ESCAPE_CHARS.sub(self._fix_chars, expr._op) + if expr._op == '-': # Unary minus + return '-' + self.str_protected_child(expr._args[0], expr) + if expr.is_associative() or expr.is_infix(): + return (' ' + op + ' ').join([self.str_protected_child(arg, expr) + for arg in expr._args]) + + op = '<font color="%s">%s</font>' % (utils.COLOR_OP_FUNC, op) + return (op + '(' + + ', '.join( + self.from_expr(arg) + for arg in expr._args + ) + ')') + + def from_ExprAssign(self, expr): + return "%s = %s" % tuple(map(expr.from_expr, (expr.dst, expr.src))) + + +def color_expr_html(expr, loc_db): + translator = TranslatorHtml(loc_db=loc_db) + return translator.from_expr(expr) + def slice_rest(expr): "Return the completion of the current slice" size = expr.arg.size @@ -489,6 +594,7 @@ class irbloc(IRBlock): super(irbloc, self).__init__(loc_key, irs) + class IRCFG(DiGraph): """DiGraph for IR instances""" @@ -528,6 +634,9 @@ class IRCFG(DiGraph): if dst.is_loc(): self.add_uniq_edge(irblock.loc_key, dst.loc_key) + def escape_text(self, text): + return text + def node2lines(self, node): node_name = self.loc_db.pretty_str(node) yield self.DotCellDescription( @@ -543,10 +652,10 @@ class IRCFG(DiGraph): return for i, assignblk in enumerate(self._blocks[node]): for dst, src in viewitems(assignblk): - - new_src = src.visit(lambda expr:_expr_loc_to_symb(expr, self.loc_db)) - new_dst = dst.visit(lambda expr:_expr_loc_to_symb(expr, self.loc_db)) - line = "%s = %s" % (new_dst, new_src) + line = "%s = %s" % ( + color_expr_html(dst, self.loc_db), + color_expr_html(src, self.loc_db) + ) if self._dot_offset: yield [self.DotCellDescription(text="%-4d" % i, attr={}), self.DotCellDescription(text=line, attr={})] |