From ad022737ed50c1b206a34b4875bc04f0635bbe90 Mon Sep 17 00:00:00 2001 From: Ajax Date: Wed, 22 Apr 2015 14:01:23 +0200 Subject: Core: Introduce SemBuilder, a helper for building semantics side effects --- miasm2/core/sembuilder.py | 145 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 miasm2/core/sembuilder.py (limited to 'miasm2/core/sembuilder.py') diff --git a/miasm2/core/sembuilder.py b/miasm2/core/sembuilder.py new file mode 100644 index 00000000..65e951bd --- /dev/null +++ b/miasm2/core/sembuilder.py @@ -0,0 +1,145 @@ +"Helper to quickly build instruction's semantic side effects" + +import inspect +import ast +import re + + +class MiasmTransformer(ast.NodeTransformer): + """AST visitor translating DSL to Miasm expression + + memX[Y] -> ExprMem(Y, X) + iX(Y) -> ExprIntX(Y) + X if Y else Z -> ExprCond(Y, X, Z) + """ + + # Parsers + parse_integer = re.compile("^i([0-9]+)$") + parse_mem = re.compile("^mem([0-9]+)$") + + # Visitors + + def visit_Call(self, node): + """iX(Y) -> ExprIntX(Y)""" + # Match the function name + if not isinstance(node.func, ast.Name): + # TODO: launch visitor on node + return node + + fc_name = node.func.id + 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 + # TODO: launch visitor on node + return node + + def visit_Subscript(self, node): + """memX[Y] -> ExprMem(Y, X)""" + + # Detect the syntax + if not isinstance(node.value, ast.Name): + return node + name = node.value.id + mem = self.parse_mem.search(name) + if mem is None: + # TODO: launch visitor on node + return node + + # Do replacement + addr = self.visit(node.slice.value) + call = ast.Call(func=ast.Name(id='ExprMem', ctx=ast.Load()), + args=[addr, ast.Num(n=int(mem.groups()[0]))], + keywords=[], starargs=None, kwargs=None) + return call + + def visit_IfExp(self, node): + """X if Y else Z -> ExprCond(Y, X, Z)""" + call = ast.Call(func=ast.Name(id='ExprCond', ctx=ast.Load()), + args=[self.visit(node.test), + self.visit(node.body), + self.visit(node.orelse)], + keywords=[], starargs=None, kwargs=None) + return call + + +class SemBuilder(object): + """Helper for building instruction's semantic side effects method + + This class provides a decorator @parse to use on them. + The context in which the function will be parsed must be supplied on + instanciation + """ + + def __init__(self, ctx): + """Create a SemBuilder + @ctx: context dictionnary used during parsing + """ + self.ctx = dict(ctx) + self.transformer = MiasmTransformer() + + 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] + body = [] + + # AffBlock of the current instruction + new_ast = [] + for statement in fc_ast.body: + + if isinstance(statement, ast.Assign): + src = self.transformer.visit(statement.value) + dst = self.transformer.visit(statement.targets[0]) + dst.ctx = ast.Load() + + res = ast.Call(func=ast.Name(id='ExprAff', + ctx=ast.Load()), + args=[dst, src], + keywords=[], + starargs=None, + kwargs=None) + + new_ast.append(res) + + elif (isinstance(statement, ast.Expr) and + isinstance(statement.value, ast.Str)): + # String (docstring, comment, ...) -> keep it + body.append(statement) + else: + # TODO: real var, +=, /=, -=, <<=, >>=, if/else, ... + raise RuntimeError("Unimplemented %s" % statement) + + # 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, + ctx=ast.Load()), + ast.List(elts=[], + ctx=ast.Load())], + ctx=ast.Load()))) + + ret = ast.Module([ast.FunctionDef(name=fc_ast.name, + args=fc_ast.args, + body=body, + decorator_list=[])]) + + # To display the generated function, use codegen.to_source + # codegen: https://github.com/andreif/codegen + + # Compile according to the context + fixed = ast.fix_missing_locations(ret) + codeobj = compile(fixed, '', 'exec') + ctx = self.ctx.copy() + eval(codeobj, ctx) + + # Get the function back + return ctx[fc_ast.name] -- cgit 1.4.1 From 3805e82bfad5036c89db87fb83eb8812d658100d Mon Sep 17 00:00:00 2001 From: Ajax Date: Wed, 22 Apr 2015 15:05:00 +0200 Subject: SemBuilder: Handle real variable declaration --- miasm2/core/sembuilder.py | 10 ++++++++++ test/core/sembuilder.py | 3 +++ 2 files changed, 13 insertions(+) (limited to 'miasm2/core/sembuilder.py') diff --git a/miasm2/core/sembuilder.py b/miasm2/core/sembuilder.py index 65e951bd..34271960 100644 --- a/miasm2/core/sembuilder.py +++ b/miasm2/core/sembuilder.py @@ -90,6 +90,7 @@ class SemBuilder(object): # 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 = [] # AffBlock of the current instruction @@ -99,6 +100,15 @@ class SemBuilder(object): if isinstance(statement, ast.Assign): src = self.transformer.visit(statement.value) dst = self.transformer.visit(statement.targets[0]) + + 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) + continue + dst.ctx = ast.Load() res = ast.Call(func=ast.Name(id='ExprAff', diff --git a/test/core/sembuilder.py b/test/core/sembuilder.py index 1854fc58..3c1b7d3f 100644 --- a/test/core/sembuilder.py +++ b/test/core/sembuilder.py @@ -1,4 +1,5 @@ import inspect +from pdb import pm from miasm2.core.sembuilder import SemBuilder import miasm2.expression.expression as m2_expr @@ -12,6 +13,8 @@ def test(Arg1, Arg2, Arg3): mem32[Arg1] = Arg2 mem32[Arg2] = Arg3 + i32(4) - mem32[Arg1] Arg3 = Arg3 if Arg2 else i32(0) + tmpvar = i32(2) + Arg2 = tmpvar a = m2_expr.ExprId('A') b = m2_expr.ExprId('B') -- cgit 1.4.1