about summary refs log tree commit diff stats
path: root/miasm_util.py
blob: 4f20dd8902c552d473ca9c7697b2fb871f0c19ca (plain) (blame)
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
from angr_targets.memory_map import MemoryMap
from miasm.core.locationdb import LocationDB
from miasm.expression.expression import Expr, ExprOp, ExprId, ExprLoc, \
                                        ExprInt, ExprMem, ExprCompose, \
                                        ExprSlice, ExprCond
from miasm.expression.simplifications import expr_simp_explicit

class MiasmProgramState:
    def __init__(self,
                 regs: dict[ExprId, ExprInt],
                 mem: list[tuple[MemoryMap, bytes]]
                 ):
        self.regs = regs
        self.memory = mem

    def _find_mem_map(self, addr: int) \
            -> tuple[MemoryMap, bytes] | tuple[None, None]:
        for m, data in self.memory:
            if addr >= m.start_address and addr < m.end_address:
                return m, data
        return None, None

    def read_memory(self, addr: int, size: int) -> bytes:
        res = bytes()
        while size > 0:
            m, data = self._find_mem_map(addr)
            if m is None:
                raise AttributeError(f'No memory mapping contains the address {addr}.')

            assert(m is not None and data is not None)
            read_off = addr - m.start_address
            read_size = min(size, m.end_address - addr)
            assert(read_off + read_size <= len(data))
            res += data[read_off:read_off+read_size]

            size -= read_size
            addr += read_size
        return res

def eval_expr(expr: Expr, conc_state: MiasmProgramState, loc_db) -> int:
    # Most of these implementation are just copy-pasted members of
    # `SymbolicExecutionEngine`.
    expr_to_visitor = {
        ExprInt:     _eval_exprint,
        ExprId:      _eval_exprid,
        ExprLoc:     _eval_exprloc,
        ExprMem:     _eval_exprmem,
        ExprSlice:   _eval_exprslice,
        ExprCond:    _eval_exprcond,
        ExprOp:      _eval_exprop,
        ExprCompose: _eval_exprcompose,
    }

    visitor = expr_to_visitor.get(expr.__class__, None)
    if visitor is None:
        raise TypeError("Unknown expr type")

    ret = visitor(expr, conc_state, loc_db)
    ret = expr_simp_explicit(ret)
    assert(ret is not None)

    return ret

def _eval_exprint(expr: ExprInt, _, __: LocationDB):
    """Evaluate an ExprInt using the current state"""
    return expr

def _eval_exprid(expr: ExprId, state: MiasmProgramState, _):
    """Evaluate an ExprId using the current state"""
    return state.regs[expr]

def _eval_exprloc(expr: ExprLoc, _, loc_db: LocationDB):
    """Evaluate an ExprLoc using the current state"""
    offset = loc_db.get_location_offset(expr.loc_key)
    if offset is not None:
        return ExprInt(offset, expr.size)
    return expr

def _eval_exprmem(expr: ExprMem, state: MiasmProgramState, loc_db: LocationDB):
    """Evaluate an ExprMem using the current state.
    This function first evaluates the memory pointer value.
    """
    # TODO: Implement cases with more than 64 bytes.
    #
    # The symbolic memory class used in SymbolicExecutionEngine may return
    # ExprCompose objects here. Maybe I should use that.
    assert(expr.size <= 64)
    assert(expr.size % 8 == 0)

    addr = eval_expr(expr.ptr, state, loc_db)
    ret = state.read_memory(int(addr), int(expr.size / 8))
    assert(len(ret) * 8 == expr.size)
    return ExprInt(int.from_bytes(ret, byteorder='little'), expr.size)

def _eval_exprcond(expr, state: MiasmProgramState, loc_db: LocationDB):
    """Evaluate an ExprCond using the current state"""
    cond = eval_expr(expr.cond, state, loc_db)
    src1 = eval_expr(expr.src1, state, loc_db)
    src2 = eval_expr(expr.src2, state, loc_db)
    return ExprCond(cond, src1, src2)

def _eval_exprslice(expr, state: MiasmProgramState, loc_db: LocationDB):
    """Evaluate an ExprSlice using the current state"""
    arg = eval_expr(expr.arg, state, loc_db)
    return ExprSlice(arg, expr.start, expr.stop)

def _eval_exprop(expr, state: MiasmProgramState, loc_db: LocationDB):
    """Evaluate an ExprOp using the current state"""
    args = []
    for oarg in expr.args:
        arg = eval_expr(oarg, state, loc_db)
        args.append(arg)
    return ExprOp(expr.op, *args)

def _eval_exprcompose(expr, state: MiasmProgramState, loc_db: LocationDB):
    """Evaluate an ExprCompose using the current state"""
    args = []
    for arg in expr.args:
        args.append(eval_expr(arg, state, loc_db))
    return ExprCompose(*args)