about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--example/disasm/full.py2
-rw-r--r--miasm2/analysis/depgraph.py14
-rw-r--r--miasm2/core/asmbloc.py109
-rw-r--r--miasm2/core/graph.py149
-rw-r--r--miasm2/core/sembuilder.py4
-rw-r--r--miasm2/core/utils.py6
-rw-r--r--miasm2/expression/expression.py8
-rw-r--r--miasm2/ir/ir.py84
-rw-r--r--miasm2/jitter/jitload.py2
-rw-r--r--miasm2/jitter/loader/pe.py4
-rw-r--r--test/analysis/depgraph.py2
-rw-r--r--test/core/utils.py2
12 files changed, 224 insertions, 162 deletions
diff --git a/example/disasm/full.py b/example/disasm/full.py
index 0b0069c6..ee0b88dd 100644
--- a/example/disasm/full.py
+++ b/example/disasm/full.py
@@ -148,7 +148,7 @@ for blocs in all_funcs_blocs.values():
 
 
 log.info('generate graph file')
-open('graph_execflow.dot', 'w').write(all_blocs.dot(label=True))
+open('graph_execflow.dot', 'w').write(all_blocs.dot(offset=True))
 
 log.info('generate intervals')
 
diff --git a/miasm2/analysis/depgraph.py b/miasm2/analysis/depgraph.py
index 0a5d38aa..a7c16a19 100644
--- a/miasm2/analysis/depgraph.py
+++ b/miasm2/analysis/depgraph.py
@@ -122,7 +122,7 @@ class DependencyNode(object):
 
 class CacheWrapper(IterableUserDict):
 
-    """Wrapper class for cache dictionnary"""
+    """Wrapper class for cache dictionary"""
 
     def __init__(self, dct=None):
         """Create a CacheWrapper with value @dct."""
@@ -148,9 +148,9 @@ class CacheWrapper(IterableUserDict):
 
     @property
     def nostep_cache(self):
-        """Dictionnary of DependencyNode and their dependencies,
+        """Dictionary of DependencyNode and their dependencies,
         without the step attribute.
-        The dictionnary is generated once when the method is called for the
+        The dictionary is generated once when the method is called for the
         first time and not updated afterward.
         """
         if self._nostep_cache is None:
@@ -239,12 +239,12 @@ class DependencyDict(object):
 
     @property
     def cache(self):
-        "Dictionnary of DependencyNode and their dependencies"
+        "Dictionary of DependencyNode and their dependencies"
         return self._cache
 
     @property
     def pending(self):
-        """Dictionnary of DependencyNode and their dependencies, waiting for
+        """Dictionary of DependencyNode and their dependencies, waiting for
         resolution"""
         return self._pending
 
@@ -530,7 +530,7 @@ class DependencyResult(object):
 
     @property
     def has_loop(self):
-        """True if current dictionnary has a loop"""
+        """True if current dictionary has a loop"""
         if self._has_loop is None:
             self._has_loop = (len(self.relevant_labels) !=
                               len(set(self.relevant_labels)))
@@ -539,7 +539,7 @@ class DependencyResult(object):
     def emul(self, ctx=None, step=False):
         """Symbolic execution of relevant nodes according to the history
         Return the values of input nodes' elements
-        @ctx: (optional) Initial context as dictionnary
+        @ctx: (optional) Initial context as dictionary
         @step: (optional) Verbose execution
 
         Warning: The emulation is not sound if the input nodes depend on loop
diff --git a/miasm2/core/asmbloc.py b/miasm2/core/asmbloc.py
index a4427206..38d3d17a 100644
--- a/miasm2/core/asmbloc.py
+++ b/miasm2/core/asmbloc.py
@@ -3,7 +3,6 @@
 
 import logging
 import inspect
-import re
 from collections import namedtuple
 
 import miasm2.expression.expression as m2_expr
@@ -254,6 +253,7 @@ class asm_bloc(object):
 
 
 class asm_block_bad(asm_bloc):
+
     """Stand for a *bad* ASM block (malformed, unreachable,
     not disassembled, ...)"""
 
@@ -261,7 +261,7 @@ class asm_block_bad(asm_bloc):
                    0: "Unable to disassemble",
                    1: "Null starting block",
                    2: "Address forbidden by dont_dis",
-    }
+                   }
 
     def __init__(self, label=None, alignment=1, errno=-1, *args, **kwargs):
         """Instanciate an asm_block_bad.
@@ -577,6 +577,7 @@ def dis_bloc_all(mnemo, pool_bin, offset, job_done, symbol_pool, dont_dis=[],
 
 
 class AsmCFG(DiGraph):
+
     """Directed graph standing for a ASM Control Flow Graph with:
      - nodes: asm_bloc
      - edges: constraints between blocks, synchronized with asm_bloc's "bto"
@@ -640,7 +641,7 @@ class AsmCFG(DiGraph):
     def add_uniq_edge(self, src, dst, constraint):
         """Add an edge from @src to @dst if it doesn't already exist"""
         if (src not in self._nodes_succ or
-            dst not in self._nodes_succ[src]):
+                dst not in self._nodes_succ[src]):
             self.add_edge(src, dst, constraint)
 
     def del_edge(self, src, dst):
@@ -704,81 +705,54 @@ class AsmCFG(DiGraph):
             # Use "_uniq_" beacause the edge can already exist due to add_node
             self.add_uniq_edge(*edge, constraint=graph.edges2constraint[edge])
 
-    def dot(self, label=False, lines=True):
-        """Render dot graph with HTML
-        @label: (optional) if set, add the corresponding label in each block
-        @lines: (optional) if set, includes assembly lines in the output
-        """
-
-        escape_chars = re.compile('[' + re.escape('{}') + ']')
-        label_attr = 'colspan="2" align="center" bgcolor="grey"'
-        edge_attr = 'label = "%s" color="%s" style="bold"'
-        td_attr = 'align="left"'
-        block_attr = 'shape="Mrecord" fontname="Courier New"'
-
-        out = ["digraph asm_graph {"]
-        fix_chars = lambda x: '\\' + x.group()
-
-        # Generate basic blocks
-        out_blocks = []
-        for block in self.nodes():
-            out_block = '%s [\n' % block.label.name
-            out_block += "%s " % block_attr
-            if isinstance(block, asm_block_bad):
-                out_block += 'style=filled fillcolor="red" '
-            out_block += 'label =<<table border="0" cellborder="0" cellpadding="3">'
-
-            block_label = '<tr><td %s>%s</td></tr>' % (
-                label_attr, block.label.name)
-            block_html_lines = []
-
-            if lines:
-                if isinstance(block, asm_block_bad):
-                    block_html_lines.append(block.ERROR_TYPES.get(block._errno,
-                                                                  block._errno))
-
-                for line in block.lines:
-                    if label:
-                        out_render = "%.8X</td><td %s> " % (line.offset,
-                                                            td_attr)
-                    else:
-                        out_render = ""
-                    out_render += escape_chars.sub(fix_chars, str(line))
-                    block_html_lines.append(out_render)
-
-            block_html_lines = ('<tr><td %s>' % td_attr +
-                                ('</td></tr><tr><td %s>' % td_attr).join(block_html_lines) +
-                                '</td></tr>')
-            out_block += "%s " % block_label
-            out_block += block_html_lines + "</table>> ];"
-            out_blocks.append(out_block)
+    def node2lines(self, node):
+        yield self.DotCellDescription(text=str(node.label.name),
+                                      attr={'align': 'center',
+                                            'colspan': 2,
+                                            'bgcolor': 'grey'})
+
+        if isinstance(node, asm_block_bad):
+            yield [self.DotCellDescription(
+                text=node.ERROR_TYPES.get(node._errno,
+                                          node._errno),
+                                           attr={})]
+            raise StopIteration
+        for line in node.lines:
+            if self._dot_offset:
+                yield [self.DotCellDescription(text="%.8X" % line.offset,
+                                               attr={}),
+                       self.DotCellDescription(text=str(line), attr={})]
+            else:
+                yield self.DotCellDescription(text=str(line), attr={})
 
-        out += out_blocks
+    def node_attr(self, node):
+        if isinstance(node, asm_block_bad):
+            return {'style': 'filled', 'fillcolor': 'red'}
+        return {}
 
-        # Generate links
-        for src, dst in self.edges():
-            exp_label = dst.label
-            cst = self.edges2constraint.get((src, dst), None)
+    def edge_attr(self, src, dst):
+        cst = self.edges2constraint.get((src, dst), None)
+        edge_color = "blue"
 
-            edge_color = "black"
+        if len(self.successors(src)) > 1:
             if cst == asm_constraint.c_next:
                 edge_color = "red"
-            elif cst == asm_constraint.c_to:
+            else:
                 edge_color = "limegreen"
-            # special case
-            if len(src.bto) == 1:
-                edge_color = "blue"
 
-            out.append('%s -> %s' % (src.label.name, dst.label.name) + \
-                       '[' + edge_attr % (cst, edge_color) + '];')
+        return {"color": edge_color}
 
-        out.append("}")
-        return '\n'.join(out)
+    def dot(self, offset=False):
+        """
+        @offset: (optional) if set, add the corresponding offsets in each node
+        """
+        self._dot_offset = offset
+        return super(AsmCFG, self).dot()
 
     # Helpers
     @property
     def pendings(self):
-        """Dictionnary of label -> set(AsmCFGPending instance) indicating
+        """Dictionary of label -> set(AsmCFGPending instance) indicating
         which label are missing in the current instance.
         A label is missing if a block which is already in nodes has constraints
         with him (thanks to its .bto) and the corresponding block is not yet in
@@ -935,7 +909,8 @@ class AsmCFG(DiGraph):
         new block destinations
         @kwargs: (optional) named arguments to pass to dis_block_callback
         """
-        # Get all possible destinations not yet resolved, with a resolved offset
+        # Get all possible destinations not yet resolved, with a resolved
+        # offset
         block_dst = [label.offset
                      for label in self.pendings
                      if label.offset is not None]
diff --git a/miasm2/core/graph.py b/miasm2/core/graph.py
index 310adc2e..f544757d 100644
--- a/miasm2/core/graph.py
+++ b/miasm2/core/graph.py
@@ -1,9 +1,15 @@
 from collections import defaultdict, namedtuple
+import re
 
 
 class DiGraph(object):
+
     """Implementation of directed graph"""
 
+    # Stand for a cell in a dot node rendering
+    DotCellDescription = namedtuple("DotCellDescription",
+                                    ["text", "attr"])
+
     def __init__(self):
         self._nodes = set()
         self._edges = []
@@ -86,7 +92,7 @@ class DiGraph(object):
     def add_uniq_edge(self, src, dst):
         """Add an edge from @src to @dst if it doesn't already exist"""
         if (src not in self._nodes_succ or
-            dst not in self._nodes_succ[src]):
+                dst not in self._nodes_succ[src]):
             self.add_edge(src, dst)
 
     def del_edge(self, src, dst):
@@ -144,35 +150,100 @@ class DiGraph(object):
                     out.append(path + [dst])
         return out
 
+    def nodeid(self, node):
+        """
+        Returns uniq id for a @node
+        @node: a node of the graph
+        """
+        return hash(node) & 0xFFFFFFFFFFFFFFFF
+
+    def node2lines(self, node):
+        """
+        Returns an iterator on cells of the dot @node.
+        A DotCellDescription or a list of DotCellDescription are accepted
+        @node: a node of the graph
+        """
+        yield self.DotCellDescription(text=str(node), attr={})
+
+    def node_attr(self, node):
+        """
+        Returns a dictionary of the @node's attributes
+        @node: a node of the graph
+        """
+        return {}
+
+    def edge_attr(self, src, dst):
+        """
+        Return a dictionary of attributes for the edge between @src and @dst
+        @src: the source node of the edge
+        @dst: the destination node of the edge
+        """
+        return {}
+
     @staticmethod
-    def node2str(node):
-        return str(node)
+    def _fix_chars(token):
+        return "&#%04d;" % ord(token.group())
 
     @staticmethod
-    def edge2str(src, dst):
-        return ""
+    def _attr2str(default_attr, attr):
+        return ' '.join('%s="%s"' % (name, value)
+                        for name, value in
+                        dict(default_attr,
+                             **attr).iteritems())
 
     def dot(self):
-        out = """
-digraph asm_graph {
-graph [
-splines=polyline,
-];
-node [
-fontsize = "16",
-shape = "box"
-];
-"""
+        """Render dot graph with HTML"""
+
+        escape_chars = re.compile('[' + re.escape('{}') + '&|<>' + ']')
+        label_attr = 'colspan="2" align="center" bgcolor="grey"'
+        edge_attr = 'label = "%s" color="%s" style="bold"'
+        td_attr = {'align': 'left'}
+        nodes_attr = {'shape': 'Mrecord',
+                      'fontname': 'Courier New'}
+
+        out = ["digraph asm_graph {"]
+
+        # Generate basic nodes
+        out_nodes = []
         for node in self.nodes():
-            out += '%s [label="%s"];\n' % (
-                hash(node) & 0xFFFFFFFFFFFFFFFF, self.node2str(node))
+            node_id = self.nodeid(node)
+            out_node = '%s [\n' % node_id
+            out_node += self._attr2str(nodes_attr, self.node_attr(node))
+            out_node += 'label =<<table border="0" cellborder="0" cellpadding="3">'
+
+            node_html_lines = []
+
+            for lineDesc in self.node2lines(node):
+                out_render = ""
+                if isinstance(lineDesc, self.DotCellDescription):
+                    lineDesc = [lineDesc]
+                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)))
+                node_html_lines.append(out_render)
 
+            node_html_lines = ('<tr>' +
+                               ('</tr><tr>').join(node_html_lines) +
+                               '</tr>')
+
+            out_node += node_html_lines + "</table>> ];"
+            out_nodes.append(out_node)
+
+        out += out_nodes
+
+        # Generate links
         for src, dst in self.edges():
-            out += '%s -> %s [label="%s"]\n' % (hash(src) & 0xFFFFFFFFFFFFFFFF,
-                                                hash(dst) & 0xFFFFFFFFFFFFFFFF,
-                                                self.edge2str(src, dst))
-        out += "}"
-        return out
+            attrs = self.edge_attr(src, dst)
+
+            attrs = ' '.join('%s="%s"' % (name, value)
+                             for name, value in attrs.iteritems())
+
+            out.append('%s -> %s' % (self.nodeid(src), self.nodeid(dst)) +
+                       '[' + attrs + '];')
+
+        out.append("}")
+        return '\n'.join(out)
 
     @staticmethod
     def _reachable_nodes(head, next_cb):
@@ -270,7 +341,7 @@ shape = "box"
 
         The function doesn't return the self reference in dominators.
         @node: The start node
-        @gen_dominators: The dictionnary containing at least node's
+        @gen_dominators: The dictionary containing at least node's
         dominators/post_dominators
         @succ_cb: return predecessors/succesors of a node
 
@@ -316,7 +387,7 @@ shape = "box"
         """Return an iterator of the ordered list of @node's dominators
         The function doesn't return the self reference in dominators.
         @node: The start node
-        @dominators: The dictionnary containing at least node's dominators
+        @dominators: The dictionary containing at least node's dominators
         """
         return self._walk_generic_dominator(node,
                                             dominators,
@@ -326,7 +397,7 @@ shape = "box"
         """Return an iterator of the ordered list of @node's postdominators
         The function doesn't return the self reference in postdominators.
         @node: The start node
-        @postdominators: The dictionnary containing at least node's
+        @postdominators: The dictionary containing at least node's
         postdominators
 
         """
@@ -541,6 +612,7 @@ shape = "box"
 
 
 class DiGraphSimplifier(object):
+
     """Wrapper on graph simplification passes.
 
     Instance handle passes lists.
@@ -575,6 +647,7 @@ class DiGraphSimplifier(object):
 
 
 class MatchGraphJoker(object):
+
     """MatchGraphJoker are joker nodes of MatchGraph, that is to say nodes which
     stand for any node. Restrictions can be added to jokers.
 
@@ -649,6 +722,7 @@ class MatchGraphJoker(object):
 
 
 class MatchGraph(DiGraph):
+
     """MatchGraph intends to be the counterpart of MatchExpr, but for DiGraph
 
     This class provides API to match a given DiGraph pattern, with addidionnal
@@ -701,7 +775,7 @@ class MatchGraph(DiGraph):
         @candidate: @graph's node
         @expected: MatchGraphJoker instance
         @graph: DiGraph instance
-        @partial_sol: (optional) dictionnary of MatchGraphJoker -> @graph's node
+        @partial_sol: (optional) dictionary of MatchGraphJoker -> @graph's node
         standing for a partial solution
         """
         # Avoid having 2 different joker for the same node
@@ -713,8 +787,8 @@ class MatchGraph(DiGraph):
             return False
 
         # Check arity
-        ## If filter_in/out, then arity must be the same
-        ## Otherwise, arity of the candidate must be at least equal
+        # If filter_in/out, then arity must be the same
+        # Otherwise, arity of the candidate must be at least equal
         if ((expected.restrict_in == True and
              len(self.predecessors(expected)) != len(graph.predecessors(candidate))) or
             (expected.restrict_in == False and
@@ -731,12 +805,12 @@ class MatchGraph(DiGraph):
             return True
         for pred in self.predecessors(expected):
             if (pred in partial_sol and
-                partial_sol[pred] not in graph.predecessors(candidate)):
+                    partial_sol[pred] not in graph.predecessors(candidate)):
                 return False
 
         for succ in self.successors(expected):
             if (succ in partial_sol and
-                partial_sol[succ] not in graph.successors(candidate)):
+                    partial_sol[succ] not in graph.successors(candidate)):
                 return False
 
         # All checks OK
@@ -750,11 +824,11 @@ class MatchGraph(DiGraph):
         """
         real_node = partial_sol[node]
         for candidate in propagator(self, node):
-            ## Edge already in the partial solution, skip it
+            # Edge already in the partial solution, skip it
             if candidate in partial_sol:
                 continue
 
-            ## Check candidate
+            # Check candidate
             for candidate_real in propagator(graph, real_node):
                 if self._check_node(candidate_real, candidate, graph,
                                     partial_sol):
@@ -775,15 +849,15 @@ class MatchGraph(DiGraph):
 
     def match(self, graph):
         """Naive subgraph matching between graph and self.
-        Iterator on matching solution, as dictionnary MatchGraphJoker -> @graph
+        Iterator on matching solution, as dictionary MatchGraphJoker -> @graph
         @graph: DiGraph instance
         In order to obtained correct and complete results, @graph must be
         connected.
         """
         # Partial solution: nodes corrects, edges between these nodes corrects
-        # A partial solution is a dictionnary MatchGraphJoker -> @graph's node
-        todo = list() # Dictionnaries containing partial solution
-        done = list() # Aleady computed partial solutions
+        # A partial solution is a dictionary MatchGraphJoker -> @graph's node
+        todo = list()  # Dictionnaries containing partial solution
+        done = list()  # Aleady computed partial solutions
 
         # Elect first candidates
         to_match = next(iter(self._nodes))
@@ -799,7 +873,8 @@ class MatchGraph(DiGraph):
             # -> using last entry of todo first performs a "depth first"
             # approach on solutions
             # -> the algorithm may converge faster to a solution, a desired
-            # behavior while doing graph simplification (stopping after one sol)
+            # behavior while doing graph simplification (stopping after one
+            # sol)
             partial_sol = todo.pop()
 
             # Avoid infinite loop and recurrent work
diff --git a/miasm2/core/sembuilder.py b/miasm2/core/sembuilder.py
index 83981919..ce327ce1 100644
--- a/miasm2/core/sembuilder.py
+++ b/miasm2/core/sembuilder.py
@@ -131,7 +131,7 @@ class SemBuilder(object):
 
     def __init__(self, ctx):
         """Create a SemBuilder
-        @ctx: context dictionnary used during parsing
+        @ctx: context dictionary used during parsing
         """
         # Init
         self.transformer = MiasmTransformer()
@@ -144,7 +144,7 @@ class SemBuilder(object):
 
     @property
     def functions(self):
-        """Return a dictionnary name -> func of parsed functions"""
+        """Return a dictionary name -> func of parsed functions"""
         return self._functions.copy()
 
     @staticmethod
diff --git a/miasm2/core/utils.py b/miasm2/core/utils.py
index 30aff7d2..70520c1b 100644
--- a/miasm2/core/utils.py
+++ b/miasm2/core/utils.py
@@ -53,7 +53,7 @@ def whoami():
 
 
 class BoundedDict(UserDict.DictMixin):
-    """Limited in size dictionnary.
+    """Limited in size dictionary.
 
     To reduce combinatory cost, once an upper limit @max_size is reached,
     @max_size - @min_size elements are suppressed.
@@ -65,7 +65,7 @@ class BoundedDict(UserDict.DictMixin):
     def __init__(self, max_size, min_size=None, initialdata=None,
                  delete_cb=None):
         """Create a BoundedDict
-        @max_size: maximum size of the dictionnary
+        @max_size: maximum size of the dictionary
         @min_size: (optional) number of most used element to keep when resizing
         @initialdata: (optional) dict instance with initial data
         @delete_cb: (optional) callback called when an element is removed
@@ -121,7 +121,7 @@ class BoundedDict(UserDict.DictMixin):
 
     @property
     def data(self):
-        "Return the current instance as a dictionnary"
+        "Return the current instance as a dictionary"
         return self._data
 
     def __getitem__(self, key):
diff --git a/miasm2/expression/expression.py b/miasm2/expression/expression.py
index 229f8a21..bda0dbc4 100644
--- a/miasm2/expression/expression.py
+++ b/miasm2/expression/expression.py
@@ -227,7 +227,7 @@ class Expr(object):
 
     def replace_expr(self, dct=None):
         """Find and replace sub expression using dct
-        @dct: dictionnary of Expr -> *
+        @dct: dictionary of Expr -> *
         """
         if dct is None:
             dct = {}
@@ -1214,7 +1214,7 @@ def test_set(e, v, tks, result):
     @e : Expr
     @v : Expr
     @tks : list of ExprId, available jokers
-    @result : dictionnary of ExprId -> Expr, current context
+    @result : dictionary of ExprId -> Expr, current context
     """
 
     if not v in tks:
@@ -1227,11 +1227,11 @@ def test_set(e, v, tks, result):
 
 def MatchExpr(e, m, tks, result=None):
     """Try to match m expression with e expression with tks jokers.
-    Result is output dictionnary with matching joker values.
+    Result is output dictionary with matching joker values.
     @e : Expr to test
     @m : Targetted Expr
     @tks : list of ExprId, available jokers
-    @result : dictionnary of ExprId -> Expr, output matching context
+    @result : dictionary of ExprId -> Expr, output matching context
     """
 
     if result is None:
diff --git a/miasm2/ir/ir.py b/miasm2/ir/ir.py
index 9d90f79a..122ce6d0 100644
--- a/miasm2/ir/ir.py
+++ b/miasm2/ir/ir.py
@@ -30,7 +30,7 @@ from miasm2.core.graph import DiGraph
 
 class irbloc(object):
 
-    def __init__(self, label, irs, lines = []):
+    def __init__(self, label, irs, lines=[]):
         assert(isinstance(label, asm_label))
         self.label = label
         self.irs = irs
@@ -39,7 +39,6 @@ class irbloc(object):
         self._dst = None
         self._dst_linenb = None
 
-
     def _get_dst(self):
         """Find the IRDst affectation and update dst, dst_linenb accordingly"""
         if self._dst is not None:
@@ -121,6 +120,7 @@ class irbloc(object):
 
 
 class DiGraphIR(DiGraph):
+
     """DiGraph for IR instances"""
 
     def __init__(self, blocks, *args, **kwargs):
@@ -130,35 +130,49 @@ class DiGraphIR(DiGraph):
         self._blocks = blocks
         super(DiGraphIR, self).__init__(*args, **kwargs)
 
-    def dot(self):
-        """Output the graphviz script"""
-        out = """
-    digraph asm_graph {
-    size="80,50";
-    node [
-    fontsize = "16",
-    shape = "box"
-    ];
+    def node2lines(self, node):
+        yield self.DotCellDescription(text=str(node.name),
+                                      attr={'align': 'center',
+                                      'colspan': 2,
+                                            'bgcolor': 'grey'})
+        if node not in self._blocks:
+            yield [self.DotCellDescription(text="NOT PRESENT", attr={})]
+            raise StopIteration
+        for i, exprs in enumerate(self._blocks[node].irs):
+            for expr in exprs:
+                if self._dot_offset:
+                    yield [self.DotCellDescription(text="%-4d" % i, attr={}),
+                           self.DotCellDescription(text=str(expr), attr={})]
+                else:
+                    yield self.DotCellDescription(text=str(expr), attr={})
+            yield self.DotCellDescription(text="", attr={})
+
+    def edge_attr(self, src, dst):
+        if src not in self._blocks or dst not in self._blocks:
+            return {}
+        src_irdst = self._blocks[src].dst
+        edge_color = "blue"
+        if isinstance(src_irdst, m2_expr.ExprCond):
+            if (expr_is_label(src_irdst.src1) and
+                    src_irdst.src1.name == dst):
+                edge_color = "limegreen"
+            elif (expr_is_label(src_irdst.src2) and
+                  src_irdst.src2.name == dst):
+                edge_color = "red"
+        return {"color": edge_color}
+
+    def node_attr(self, node):
+        if node not in self._blocks:
+            return {'style': 'filled', 'fillcolor': 'red'}
+        return {}
+
+    def dot(self, offset=False):
         """
-        all_lbls = {}
-        for lbl in self.nodes():
-            if lbl not in self._blocks:
-                continue
-            irb = self._blocks[lbl]
-            ir_txt = [str(lbl)]
-            for irs in irb.irs:
-                for l in irs:
-                    ir_txt.append(str(l))
-                ir_txt.append("")
-            ir_txt.append("")
-            all_lbls[hash(lbl)] = "\l\\\n".join(ir_txt)
-        for l, v in all_lbls.items():
-            out += '%s [label="%s"];\n' % (l, v)
-
-        for a, b in self.edges():
-            out += '%s -> %s;\n' % (hash(a), hash(b))
-        out += '}'
-        return out
+        @offset: (optional) if set, add the corresponding line number in each
+        node
+        """
+        self._dot_offset = offset
+        return super(DiGraphIR, self).dot()
 
 
 class ir(object):
@@ -184,7 +198,7 @@ class ir(object):
         @ad: an ExprId/ExprInt/label/int"""
 
         if (isinstance(ad, m2_expr.ExprId) and
-            isinstance(ad.name, asm_label)):
+                isinstance(ad.name, asm_label)):
             ad = ad.name
         if isinstance(ad, m2_expr.ExprInt):
             ad = int(ad.arg)
@@ -201,7 +215,7 @@ class ir(object):
         label = self.get_label(ad)
         return self.blocs.get(label, None)
 
-    def add_instr(self, l, ad=0, gen_pc_updt = False):
+    def add_instr(self, l, ad=0, gen_pc_updt=False):
         b = asm_bloc(self.gen_label())
         b.lines = [l]
         self.add_bloc(b, gen_pc_updt)
@@ -262,7 +276,6 @@ class ir(object):
             # Add the merged one
             affect_list.append(m2_expr.ExprAff(dst, final_dst))
 
-
     def getby_offset(self, offset):
         out = set()
         for irb in self.blocs.values():
@@ -276,7 +289,7 @@ class ir(object):
                                                                     l.offset))])
         c.lines.append(l)
 
-    def add_bloc(self, bloc, gen_pc_updt = False):
+    def add_bloc(self, bloc, gen_pc_updt=False):
         c = None
         ir_blocs_all = []
         for l in bloc.lines:
@@ -292,7 +305,6 @@ class ir(object):
             c.irs.append(ir_bloc_cur)
             c.lines.append(l)
 
-
             if ir_blocs_extra:
                 for b in ir_blocs_extra:
                     b.lines = [l] * len(b.irs)
@@ -372,7 +384,7 @@ class ir(object):
             for i, l in enumerate(irs):
                 irs[i] = l.replace_expr(rep)
 
-    def get_rw(self, regs_ids = []):
+    def get_rw(self, regs_ids=[]):
         """
         Calls get_rw(irb) for each bloc
         @regs_ids : ids of registers used in IR
diff --git a/miasm2/jitter/jitload.py b/miasm2/jitter/jitload.py
index 2335cc3c..1bb9a806 100644
--- a/miasm2/jitter/jitload.py
+++ b/miasm2/jitter/jitload.py
@@ -428,7 +428,7 @@ class jitter:
     def add_lib_handler(self, libs, user_globals=None):
         """Add a function to handle libs call with breakpoints
         @libs: libimp instance
-        @user_globals: dictionnary for defined user function
+        @user_globals: dictionary for defined user function
         """
         if user_globals is None:
             user_globals = {}
diff --git a/miasm2/jitter/loader/pe.py b/miasm2/jitter/loader/pe.py
index 1c811101..89876a92 100644
--- a/miasm2/jitter/loader/pe.py
+++ b/miasm2/jitter/loader/pe.py
@@ -228,7 +228,7 @@ def vm_load_pe_libs(vm, libs_name, libs, lib_path_base, **kargs):
     @libs_name: list of str
     @libs: libimp_pe instance
     @lib_path_base: (optional) DLLs relative path
-    Return a dictionnary Filename -> PE instances
+    Return a dictionary Filename -> PE instances
     Extra arguments are passed to vm_load_pe_lib
     """
     return {fname: vm_load_pe_lib(vm, fname, libs, lib_path_base, **kargs)
@@ -455,7 +455,7 @@ class libimp_pe(libimp):
 
 def vm_load_pe_and_dependencies(vm, fname, name2module, runtime_lib,
                                 lib_path_base, **kwargs):
-    """Load a binary and all its dependencies. Returns a dictionnary containing
+    """Load a binary and all its dependencies. Returns a dictionary containing
     the association between binaries names and it's pe object
 
     @vm: virtual memory manager instance
diff --git a/test/analysis/depgraph.py b/test/analysis/depgraph.py
index b532080b..211671a4 100644
--- a/test/analysis/depgraph.py
+++ b/test/analysis/depgraph.py
@@ -168,7 +168,7 @@ DD2.cache[DEPNODES_0[4]] = set(DEPNODES_0[5:9])
 assert DD2.cache != DD0.cache
 
 
-print "   [+] Test dictionnary equality"
+print "   [+] Test dictionary equality"
 DNA = DependencyNode(LBL2, A, 0, next(STEP_COUNTER))
 DNB = DependencyNode(LBL1, B, 1, next(STEP_COUNTER))
 DNC = DependencyNode(LBL1, C, 0, next(STEP_COUNTER), True)
diff --git a/test/core/utils.py b/test/core/utils.py
index bf14df68..f7de6565 100644
--- a/test/core/utils.py
+++ b/test/core/utils.py
@@ -13,7 +13,7 @@ class TestUtils(unittest.TestCase):
         def logger(key):
             print "DELETE", key
 
-        # Create a 5/2 dictionnary
+        # Create a 5/2 dictionary
         bd = BoundedDict(5, 2, initialdata={"element": "value"},
                          delete_cb=logger)
         bd["element2"] = "value2"