1import logging
2import threading
3
4from angr.errors import AngrError
5from .engine import SuccessorsMixin
6from ..errors import SimConcreteMemoryError, SimConcreteRegisterError
7
8l = logging.getLogger("angr.engines.concrete")
9#l.setLevel(logging.DEBUG)
10
11try:
12    from angr_targets.concrete import ConcreteTarget
13except ImportError:
14    ConcreteTarget = None
15
16
17class SimEngineConcrete(SuccessorsMixin):
18    """
19    Concrete execution using a concrete target provided by the user.
20    """
21    def __init__(self, project):
22        if not ConcreteTarget:
23            l.critical("Error, can't find angr_target project")
24            raise AngrError
25
26        l.info("Initializing SimEngineConcrete with ConcreteTarget provided.")
27        super(SimEngineConcrete, self).__init__()
28        self.project = project
29        if isinstance(self.project.concrete_target, ConcreteTarget) and \
30                self.check_concrete_target_methods(self.project.concrete_target):
31
32            self.target = self.project.concrete_target
33        else:
34            l.warning("Error, you must provide an instance of a ConcreteTarget to initialize a SimEngineConcrete.")
35            self.target = None
36            raise NotImplementedError
37
38        self.segment_registers_already_init = False
39
40    def process_successors(self, successors, extra_stop_points=None, memory_concretize=None,
41                           register_concretize=None, timeout=0, *args, **kwargs):
42
43        new_state = self.state
44        # setup the concrete process and resume the execution
45        self.to_engine(new_state, extra_stop_points, memory_concretize, register_concretize, timeout)
46
47        # sync angr with the current state of the concrete process using
48        # the state plugin
49        new_state.concrete.sync()
50
51        successors.engine = "SimEngineConcrete"
52        successors.sort = "SimEngineConcrete"
53        successors.add_successor(new_state, new_state.ip, new_state.solver.true, new_state.unicorn.jumpkind)
54        successors.description = "Concrete Successors"
55        successors.processed = True
56
57    def to_engine(self, state, extra_stop_points, memory_concretize, register_concretize, timeout):
58        """
59        Handle the concrete execution of the process
60        This method takes care of:
61        1- Set the breakpoints on the addresses provided by the user
62        2- Concretize the symbolic variables and perform the write inside the concrete process
63        3- Continue the program execution.
64
65        :param state:               The state with which to execute
66        :param extra_stop_points:   list of a addresses where to stop the concrete execution and return to the
67                                    simulated one
68        :param memory_concretize:   list of tuples (address, symbolic variable) that are going to be written
69                                    in the concrete process memory.
70        :param register_concretize:  list of tuples (reg_name, symbolic variable) that are going to be written
71        :param timeout:             how long we should wait the concrete target to reach the breakpoint
72        :return: None
73        """
74
75        state.globals["symbion_timeout"] = False
76        extra_stop_points = [] if extra_stop_points is None else extra_stop_points
77
78        l.debug("Entering in SimEngineConcrete: simulated address %#x concrete address %#x stop points %s",
79                state.addr, self.target.read_register("pc"), map(hex, extra_stop_points))
80
81        if memory_concretize:
82            l.debug("SimEngineConcrete is concretizing memory variables before resuming the concrete process")
83
84            for sym_var in memory_concretize:
85                sym_var_address = state.solver.eval(sym_var[0])
86                sym_var_value = state.solver.eval(sym_var[1], cast_to=bytes)
87                l.debug("Concretize memory at address %#x with value %s", sym_var_address, str(sym_var_value))
88                self.target.write_memory(sym_var_address, sym_var_value, raw=True)
89
90        if register_concretize:
91            l.debug("SimEngineConcrete is concretizing registers variables before resuming the concrete process")
92            for reg in register_concretize:
93                register_name = reg[0]
94                register_value = state.solver.eval(reg[1])
95                l.debug("Concretize register %s with value %s", register_name, str(register_value))
96                self.target.write_register(register_name, register_value)
97
98        # Set breakpoint on remote target
99        for stop_point in extra_stop_points:
100            l.debug("Setting breakpoints at %#x", stop_point)
101            self.target.set_breakpoint(stop_point, hardware=True, temporary=True)
102
103        if timeout > 0:
104            l.debug("Found timeout as option, setting it up!")
105
106            def timeout_handler():
107                self.target.stop()    # stop the concrete target now!
108                state.globals["symbion_timeout"] = True  # this will end up in the timeout stash
109
110            execution_timer = threading.Timer(timeout, timeout_handler)
111            execution_timer.start()  # start the timer!
112
113        # resuming of the concrete process, if the target won't reach the
114        # breakpoint specified by the user the timeout will abort angr execution.
115        l.debug("SimEngineConcrete is resuming the concrete process")
116        self.target.run()
117        l.debug("SimEngineConcrete has successfully resumed the process")
118
119        if state.globals["symbion_timeout"]:
120            l.critical("Timeout has been reached during resuming of concrete process")
121            l.critical("This can be a bad thing ( the ConcreteTarget didn't hit your breakpoint ) or"
122                       "just it will take a while.")
123
124        # reset the alarm
125        if timeout > 0:
126            execution_timer.cancel()
127
128        # removing all breakpoints set by the concrete target
129        for stop_point in extra_stop_points:
130            self.target.remove_breakpoint(stop_point)
131
132        # handling the case in which the program stops at a point different than the breakpoints set
133        # by the user.
134        current_pc = self.target.read_register("pc")
135        if current_pc not in extra_stop_points and not state.globals["symbion_timeout"]:
136            l.critical("Stopped at unexpected location inside the concrete process: %#x", current_pc)
137            raise AngrError
138
139    @staticmethod
140    def check_concrete_target_methods(concrete_target):
141        """
142        Check if the concrete target methods return the correct type of data
143        :return: True if the concrete target is compliant
144        """
145        entry_point = concrete_target.read_register("pc")
146        if not type(entry_point) is int:
147            l.error("read_register result type is %s, should be <type 'int'>", (type(entry_point)))
148            return False
149
150        mem_read = concrete_target.read_memory(entry_point, 0x4)
151
152        if not type(mem_read) is bytes:
153            l.error("read_memory result type is %s, should be <type 'bytes'>", (type(mem_read)))
154            return False
155
156        try:
157            concrete_target.read_register("not_existent_reg")
158            l.error("read_register should raise a SimConcreteRegisterError when accessing non existent registers")
159            return False
160
161        except SimConcreteRegisterError:
162            l.debug("read_register raise a SimConcreteRegisterError, ok!")
163
164        try:
165            concrete_target.read_memory(0x0, 0x4)
166            l.error("read_memory should raise a SimConcreteMemoryError when accessing non mapped memory")
167            return False
168
169        except SimConcreteMemoryError:
170            l.debug("read_register raise a SimConcreteMemoryError, ok!")
171
172        return True
173