1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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, '<string>', 'exec')
ctx = self.ctx.copy()
eval(codeobj, ctx)
# Get the function back
return ctx[fc_ast.name]
|