about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorserpilliere <serpilliere@users.noreply.github.com>2021-12-02 08:39:18 +0100
committerGitHub <noreply@github.com>2021-12-02 08:39:18 +0100
commit0cd2cad02dd1b300d61bdc985b09de65f92261b4 (patch)
tree7b2f0f0d5d933d807be7b57f331de5a0431cbc67
parentea5abb5090ef0c8fac2ddde6f99f6f3c4fff432c (diff)
parentb9c8746116de2b223d441a90f783f8c16ab876c3 (diff)
downloadmiasm-0cd2cad02dd1b300d61bdc985b09de65f92261b4.tar.gz
miasm-0cd2cad02dd1b300d61bdc985b09de65f92261b4.zip
Merge pull request #1400 from serpilliere/color_ircfg
Colorize ir/asm
-rw-r--r--miasm/arch/aarch64/arch.py69
-rw-r--r--miasm/arch/arm/arch.py96
-rw-r--r--miasm/arch/arm/lifter_model_call.py2
-rw-r--r--miasm/arch/mep/arch.py33
-rw-r--r--miasm/arch/mips32/arch.py15
-rw-r--r--miasm/arch/msp430/arch.py24
-rw-r--r--miasm/arch/ppc/arch.py23
-rw-r--r--miasm/arch/x86/arch.py59
-rw-r--r--miasm/core/asmblock.py10
-rw-r--r--miasm/core/cpu.py14
-rw-r--r--miasm/core/graph.py12
-rw-r--r--miasm/core/utils.py24
-rw-r--r--miasm/ir/ir.py117
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={})]