From 2f37ae22fcbd173a5554aa3fba922e7f9713cf83 Mon Sep 17 00:00:00 2001 From: Ajax Date: Thu, 23 Apr 2015 18:28:17 +0200 Subject: SemBuilder: Add `('X' % Y)(Z) -> ExprOp('X' % Y, Z)` and `('X')(Y) -> ExprOp('X')(Y)` --- miasm2/core/sembuilder.py | 50 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 15 deletions(-) (limited to 'miasm2/core/sembuilder.py') diff --git a/miasm2/core/sembuilder.py b/miasm2/core/sembuilder.py index 34271960..ba9d9d80 100644 --- a/miasm2/core/sembuilder.py +++ b/miasm2/core/sembuilder.py @@ -11,6 +11,8 @@ class MiasmTransformer(ast.NodeTransformer): memX[Y] -> ExprMem(Y, X) iX(Y) -> ExprIntX(Y) X if Y else Z -> ExprCond(Y, X, Z) + 'X'(Y) -> ExprOp('X', Y) + ('X' % Y)(Z) -> ExprOp('X' % Y, Z) """ # Parsers @@ -20,23 +22,40 @@ class MiasmTransformer(ast.NodeTransformer): # Visitors def visit_Call(self, node): - """iX(Y) -> ExprIntX(Y)""" - # Match the function name - if not isinstance(node.func, ast.Name): + """iX(Y) -> ExprIntX(Y), + 'X'(Y) -> ExprOp('X', Y), ('X' % Y)(Z) -> ExprOp('X' % Y, Z)""" + if isinstance(node.func, ast.Name): + # iX(Y) -> ExprIntX(Y) + fc_name = node.func.id + + # Match the function name + new_name = fc_name + integer = self.parse_integer.search(fc_name) + + # Do replacement + if integer is not None: + new_name = "ExprInt%s" % integer.groups()[0] + + # Replace in the node + node.func.id = new_name + + elif (isinstance(node.func, ast.Str) or + (isinstance(node.func, ast.BinOp) and + isinstance(node.func.op, ast.Mod) and + isinstance(node.func.left, ast.Str))): + # 'op'(args...) -> ExprOp('op', args...) + # ('op' % (fmt))(args...) -> ExprOp('op' % (fmt), args...) + op_name = node.func + + # Do replacement + node.func = ast.Name(id="ExprOp", ctx=ast.Load()) + node.args[0:0] = [op_name] + node.args = map(self.visit, node.args) + + else: # TODO: launch visitor on node - return node - - fc_name = node.func.id - new_name = fc_name - integer = self.parse_integer.search(fc_name) + pass - # Do replacement - if integer is not None: - new_name = "ExprInt%s" % integer.groups()[0] - - # Replace in the node - node.func.id = new_name - # TODO: launch visitor on node return node def visit_Subscript(self, node): @@ -104,6 +123,7 @@ class SemBuilder(object): if (isinstance(dst, ast.Name) and dst.id not in argument_names and dst.id not in self.ctx): + # Real variable declaration statement.value = src body.append(statement) -- cgit 1.4.1 From 568dd3641a10e0887700a3a3433e2e20e8e37825 Mon Sep 17 00:00:00 2001 From: Ajax Date: Fri, 24 Apr 2015 10:08:23 +0200 Subject: SemBuilder: PreInit context with needed values --- miasm2/arch/mips32/sem.py | 1 - miasm2/core/sembuilder.py | 11 ++++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'miasm2/core/sembuilder.py') diff --git a/miasm2/arch/mips32/sem.py b/miasm2/arch/mips32/sem.py index 9589eb1e..ca368883 100644 --- a/miasm2/arch/mips32/sem.py +++ b/miasm2/arch/mips32/sem.py @@ -10,7 +10,6 @@ ctx = {"R_LO": R_LO, "R_HI": R_HI, "PC": PC, "RA": RA} -ctx.update(m2_expr.__dict__) sbuild = SemBuilder(ctx) diff --git a/miasm2/core/sembuilder.py b/miasm2/core/sembuilder.py index ba9d9d80..ddf1cb32 100644 --- a/miasm2/core/sembuilder.py +++ b/miasm2/core/sembuilder.py @@ -4,6 +4,9 @@ import inspect import ast import re +import miasm2.expression.expression as m2_expr +from miasm2.ir.ir import irbloc + class MiasmTransformer(ast.NodeTransformer): """AST visitor translating DSL to Miasm expression @@ -99,8 +102,14 @@ class SemBuilder(object): """Create a SemBuilder @ctx: context dictionnary used during parsing """ - self.ctx = dict(ctx) + # Init self.transformer = MiasmTransformer() + self.ctx = dict(m2_expr.__dict__) + self.ctx["irbloc"] = irbloc + + # Update context + self.ctx.update(ctx) + def parse(self, func): """Function decorator, returning a correct method from a pseudo-Python -- cgit 1.4.1 From 9536200d7787797c34635dce16462164530f8eaa Mon Sep 17 00:00:00 2001 From: Ajax Date: Fri, 24 Apr 2015 10:23:06 +0200 Subject: SemBuilder: Get back functions parsed for mnemo_func --- miasm2/arch/mips32/sem.py | 110 ++++++++++++---------------------------------- miasm2/core/sembuilder.py | 16 ++++--- 2 files changed, 38 insertions(+), 88 deletions(-) (limited to 'miasm2/core/sembuilder.py') diff --git a/miasm2/arch/mips32/sem.py b/miasm2/arch/mips32/sem.py index ca368883..d634c208 100644 --- a/miasm2/arch/mips32/sem.py +++ b/miasm2/arch/mips32/sem.py @@ -408,89 +408,33 @@ def ei(a): def ehb(a): "NOP" -mnemo_func = { - "addiu": addiu, - "addu": addiu, - "lw" : lw, - "sw" : sw, - "sh" : sh, - "sb" : sb, - "jalr" : jalr, - "jal" : jal, - "bal" : bal, - "b" : l_b, - "lbu" : lbu, - "lhu" : lhu, - "lb" : lb, - "beq" : beq, - "bgez" : bgez, - "bltz" : bltz, - "bgtz" : bgtz, - "bne" : bne, - "lui" : lui, - "nop" : nop, - "j" : j, - "jr" : j, - "ori" : l_or, - "or" : l_or, - "nor" : nor, - "and" : l_and, - "andi" : l_and, - "ext" : ext, - "mul" : mul, - "sltu" : sltu, - "slt" : slt, - "slti" : slt, - "sltiu" : sltu, - "subu" : l_sub, - "movn" : movn, - "movz" : movz, - "srl" : srl, - "sra" : sra, - "srav" : srav, - "sll" : sll, - "srlv" : srlv, - "sllv" : sllv, - "xori" : l_xor, - "xor" : l_xor, - "seb" : seb, - "seh" : seh, - "bltz" : bltz, - "blez" : blez, - "wsbh" : wsbh, - "rotr" : rotr, - # "mfc0" : mfc0, - # "mfc1" : mfc1, - # "mtc0" : mtc0, - # "mtc1" : mtc1, - "tlbwi" : tlbwi, - "tlbp" : tlbp, - "ins" : ins, - - "add.d" : add_d, - "sub.d" : sub_d, - "div.d" : div_d, - "mul.d" : mul_d, - "mov.d" : mov_d, - "lwc1" : lwc1, - "swc1" : swc1, - "c.lt.d" : c_lt_d, - "c.eq.d" : c_eq_d, - "c.le.d" : c_le_d, - "bc1t" : bc1t, - "bc1f" : bc1f, - "cvt.d.w":cvt_d_w, - "mult" : mult, - "multu" : multu, - - "mfhi" : mfhi, - "mflo" : mflo, - - "di" : di, - "ei" : ei, - "ehb" : ehb, - - } +mnemo_func = sbuild.functions +mnemo_func.update({ + 'add.d': add_d, + 'addu': addiu, + 'and': l_and, + 'andi': l_and, + 'b': l_b, + 'c.eq.d': c_eq_d, + 'c.le.d': c_le_d, + 'c.lt.d': c_lt_d, + 'cvt.d.w': cvt_d_w, + 'div.d': div_d, + 'ins': ins, + 'jr': j, + 'mov.d': mov_d, + 'movn': movn, + 'movz': movz, + 'mul.d': mul_d, + 'or': l_or, + 'ori': l_or, + 'slti': slt, + 'sltiu': sltu, + 'sub.d': sub_d, + 'subu': l_sub, + 'xor': l_xor, + 'xori': l_xor, +}) def get_mnemo_expr(ir, instr, *args): instr, extra_ir = mnemo_func[instr.name.lower()](ir, instr, *args) diff --git a/miasm2/core/sembuilder.py b/miasm2/core/sembuilder.py index ddf1cb32..a62ecc38 100644 --- a/miasm2/core/sembuilder.py +++ b/miasm2/core/sembuilder.py @@ -104,12 +104,17 @@ class SemBuilder(object): """ # Init self.transformer = MiasmTransformer() - self.ctx = dict(m2_expr.__dict__) - self.ctx["irbloc"] = irbloc + self._ctx = dict(m2_expr.__dict__) + self._ctx["irbloc"] = irbloc + self._functions = {} # Update context - self.ctx.update(ctx) + self._ctx.update(ctx) + @property + def functions(self): + """Return a dictionnary name -> func of parsed functions""" + return self._functions.copy() def parse(self, func): """Function decorator, returning a correct method from a pseudo-Python @@ -131,7 +136,7 @@ class SemBuilder(object): if (isinstance(dst, ast.Name) and dst.id not in argument_names and - dst.id not in self.ctx): + dst.id not in self._ctx): # Real variable declaration statement.value = src @@ -177,8 +182,9 @@ class SemBuilder(object): # Compile according to the context fixed = ast.fix_missing_locations(ret) codeobj = compile(fixed, '', 'exec') - ctx = self.ctx.copy() + ctx = self._ctx.copy() eval(codeobj, ctx) # Get the function back + self._functions[fc_ast.name] = ctx[fc_ast.name] return ctx[fc_ast.name] -- cgit 1.4.1 From a300ef578432b16c4d7e6de04a638834b73f82c7 Mon Sep 17 00:00:00 2001 From: Ajax Date: Fri, 24 Apr 2015 13:11:24 +0200 Subject: SemBuilder: Introduce the 'if cond' statement, building a new IRBlock --- miasm2/core/sembuilder.py | 129 ++++++++++++++++++++++++++++++++++++++++------ test/core/sembuilder.py | 28 +++++++++- 2 files changed, 139 insertions(+), 18 deletions(-) (limited to 'miasm2/core/sembuilder.py') diff --git a/miasm2/core/sembuilder.py b/miasm2/core/sembuilder.py index a62ecc38..985a6a65 100644 --- a/miasm2/core/sembuilder.py +++ b/miasm2/core/sembuilder.py @@ -116,19 +116,26 @@ class SemBuilder(object): """Return a dictionnary name -> func of parsed functions""" return self._functions.copy() - def parse(self, func): - """Function decorator, returning a correct method from a pseudo-Python - one""" + @staticmethod + def _create_labels(): + """Return the AST standing for label creations""" + out = ast.parse("lbl_end = ExprId(ir.get_next_instr(instr))").body + out += ast.parse("lbl_if = ExprId(ir.gen_label())").body + return out + + def _parse_body(self, body, argument_names): + """Recursive function transforming a @body to a block expression + Return: + - AST to append to body (real python statements) + - a list of blocks, ie list of affblock, ie list of ExprAff (AST)""" - # Get the function AST - parsed = ast.parse(inspect.getsource(func)) - fc_ast = parsed.body[0] - argument_names = [name.id for name in fc_ast.args.args] - body = [] + # Init + ## Real instructions + real_body = [] + ## Final blocks + blocks = [[[]]] - # AffBlock of the current instruction - new_ast = [] - for statement in fc_ast.body: + for statement in body: if isinstance(statement, ast.Assign): src = self.transformer.visit(statement.value) @@ -140,7 +147,7 @@ class SemBuilder(object): # Real variable declaration statement.value = src - body.append(statement) + real_body.append(statement) continue dst.ctx = ast.Load() @@ -152,22 +159,112 @@ class SemBuilder(object): starargs=None, kwargs=None) - new_ast.append(res) + blocks[-1][-1].append(res) elif (isinstance(statement, ast.Expr) and isinstance(statement.value, ast.Str)): # String (docstring, comment, ...) -> keep it - body.append(statement) + real_body.append(statement) + + elif (isinstance(statement, ast.If) and + not statement.orelse): + # Create jumps : ir.IRDst = lbl_if if cond else lbl_end + cond = statement.test + real_body += self._create_labels() + + lbl_end = ast.Name(id='lbl_end', ctx=ast.Load()) + lbl_if = ast.Name(id='lbl_if', ctx=ast.Load()) + dst = ast.Call(func=ast.Name(id='ExprCond', + ctx=ast.Load()), + args=[cond, + lbl_if, + lbl_end], + keywords=[], + starargs=None, + kwargs=None) + + if (isinstance(cond, ast.UnaryOp) and + isinstance(cond.op, ast.Not)): + ## if not cond -> switch exprCond + dst.args[1:] = dst.args[1:][::-1] + dst.args[0] = cond.operand + + IRDst = ast.Attribute(value=ast.Name(id='ir', + ctx=ast.Load()), + attr='IRDst', ctx=ast.Load()) + blocks[-1][-1].append(ast.Call(func=ast.Name(id='ExprAff', + ctx=ast.Load()), + args=[IRDst, dst], + keywords=[], + starargs=None, + kwargs=None)) + + # Create the new blocks + sub_blocks, sub_body = self._parse_body(statement.body, + argument_names) + if len(sub_blocks) > 1: + raise RuntimeError("Imbricated if unimplemented") + + ## Close the last block + jmp_end = ast.Call(func=ast.Name(id='ExprAff', + ctx=ast.Load()), + args=[IRDst, lbl_end], + keywords=[], + starargs=None, + kwargs=None) + sub_blocks[-1][-1].append(jmp_end) + sub_blocks[-1][-1] = ast.List(elts=sub_blocks[-1][-1], + ctx=ast.Load()) + sub_blocks[-1] = ast.List(elts=sub_blocks[-1], + ctx=ast.Load()) + + ## Replace the block with a call to 'irbloc' + lbl_if_name = ast.Attribute(value=ast.Name(id='lbl_if', + ctx=ast.Load()), + attr='name', ctx=ast.Load()) + + sub_blocks[-1] = ast.Call(func=ast.Name(id='irbloc', + ctx=ast.Load()), + args=[lbl_if_name, + sub_blocks[-1]], + keywords=[], + starargs=None, + kwargs=None) + blocks += sub_blocks + real_body += sub_body + + # Prepare a new block for following statement + blocks.append([[]]) + else: # TODO: real var, +=, /=, -=, <<=, >>=, if/else, ... raise RuntimeError("Unimplemented %s" % statement) + return blocks, real_body + + def parse(self, func): + """Function decorator, returning a correct method from a pseudo-Python + one""" + + # Get the function AST + parsed = ast.parse(inspect.getsource(func)) + fc_ast = parsed.body[0] + argument_names = [name.id for name in fc_ast.args.args] + + # Translate (blocks[0][0] is the current instr) + blocks, body = self._parse_body(fc_ast.body, argument_names) + # Build the new function fc_ast.args.args[0:0] = [ast.Name(id='ir', ctx=ast.Param()), ast.Name(id='instr', ctx=ast.Param())] - body.append(ast.Return(value=ast.Tuple(elts=[ast.List(elts=new_ast, + cur_instr = blocks[0][0] + if len(blocks[-1][0]) == 0: + ## Last block can be empty + blocks.pop() + other_blocks = blocks[1:] + body.append(ast.Return(value=ast.Tuple(elts=[ast.List(elts=cur_instr, ctx=ast.Load()), - ast.List(elts=[], + ast.List(elts=other_blocks, ctx=ast.Load())], ctx=ast.Load()))) diff --git a/test/core/sembuilder.py b/test/core/sembuilder.py index 8d61c1b8..468e3ef5 100644 --- a/test/core/sembuilder.py +++ b/test/core/sembuilder.py @@ -3,7 +3,20 @@ from pdb import pm from miasm2.core.sembuilder import SemBuilder import miasm2.expression.expression as m2_expr +from miasm2.core.asmbloc import asm_label +# Test classes +class IR(object): + + IRDst = m2_expr.ExprId("IRDst") + + def get_next_instr(self, _): + return asm_label("NEXT") + + def gen_label(self): + return asm_label("GEN") + +# Test sb = SemBuilder(m2_expr.__dict__) @sb.parse @@ -16,13 +29,24 @@ def test(Arg1, Arg2, Arg3): tmpvar = 'myop'(i32(2)) Arg2 = ('myopsize%d' % Arg1.size)(tmpvar, Arg1) + if not Arg1: + Arg2 = Arg3 + a = m2_expr.ExprId('A') b = m2_expr.ExprId('B') c = m2_expr.ExprId('C') -ir = None +ir = IR() instr = None res = test(ir, instr, a, b, c) + +print "[+] Returned:" print res +print "[+] DocString:", test.__doc__ + +print "[+] Cur instr:" for statement in res[0]: print statement -print test.__doc__ + +print "[+] Blocks:" +for block in res[1]: + print block -- cgit 1.4.1