diff options
Diffstat (limited to 'miasm2/core/sembuilder.py')
| -rw-r--r-- | miasm2/core/sembuilder.py | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/miasm2/core/sembuilder.py b/miasm2/core/sembuilder.py new file mode 100644 index 00000000..34271960 --- /dev/null +++ b/miasm2/core/sembuilder.py @@ -0,0 +1,155 @@ +"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] + argument_names = [name.id for name in fc_ast.args.args] + 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]) + + 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', + 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, '<string>', 'exec') + ctx = self.ctx.copy() + eval(codeobj, ctx) + + # Get the function back + return ctx[fc_ast.name] |