1
2import cle
3import io
4import logging
5import os
6import re
7import struct
8
9from .plugin import SimStatePlugin
10from ..errors import SimConcreteRegisterError
11from archinfo import ArchX86, ArchAMD64
12
13l = logging.getLogger("state_plugin.concrete")
14#l.setLevel(logging.DEBUG)
15
16
17class Concrete(SimStatePlugin):
18    def __init__(self, segment_registers_initialized=False, segment_registers_callback_initialized=False,
19                 whitelist=None, fs_register_bp=None, already_sync_objects_addresses=None,
20                 ):
21
22        super().__init__()
23
24        self.segment_registers_initialized = segment_registers_initialized
25        self.segment_registers_callback_initialized = segment_registers_callback_initialized
26
27        if not whitelist:
28            self.whitelist = []
29        else:
30            self.whitelist = whitelist
31
32        self.synchronize_cle = False
33        self.stubs_on_sync = False
34
35        self.fs_register_bp = fs_register_bp
36
37        if not already_sync_objects_addresses:
38            self.already_sync_objects_addresses = []
39        else:
40            self.already_sync_objects_addresses = already_sync_objects_addresses
41
42    def copy(self, _memo):
43        conc = Concrete(segment_registers_initialized=self.segment_registers_initialized,
44                        segment_registers_callback_initialized=self.segment_registers_callback_initialized,
45                        whitelist=list(self.whitelist),
46                        fs_register_bp=self.fs_register_bp,
47                        already_sync_objects_addresses=list(self.already_sync_objects_addresses)
48                        )
49        return conc
50
51    def merge(self, _others, _merge_conditions, _common_ancestor=None):
52        pass
53
54    def widen(self, _others):
55        pass
56
57    def set_state(self, state):
58        SimStatePlugin.set_state(self, state)
59
60    def sync(self):
61        """
62        Handle the switch between the concrete execution and angr.
63        This method takes care of:
64        1- Synchronize registers.
65        2- Set a concrete target to the memory backer so the memory reads are redirected in the concrete process memory.
66        3- If possible restore the SimProcedures with the real addresses inside the concrete process.
67        4- Set an inspect point to sync the segments register as soon as they are read during the symbolic execution.
68        5- Flush all the pages loaded until now.
69
70        :return:
71        """
72
73        def _sync_segments(state):
74            """
75            Segment registers synchronization is on demand as soon as the
76            symbolic execution access a segment register.
77            """
78            concr_target = state.project.concrete_target
79
80            if isinstance(state.arch, ArchAMD64):
81                state.project.simos.initialize_segment_register_x64(state, concr_target)
82            elif isinstance(state.arch, ArchX86):
83                gdt = state.project.simos.initialize_gdt_x86(state, concr_target)
84                state.concrete.whitelist.append((gdt.addr, gdt.addr + gdt.limit))
85
86            state.inspect.remove_breakpoint('reg_read', bp=state.concrete.fs_register_bp)
87            state.concrete.segment_registers_initialized = True
88
89            state.concrete.fs_register_bp = None
90
91        l.debug("Sync the state with the concrete memory inside the Concrete plugin")
92
93        # Configure plugin with state options
94        if options.SYMBION_SYNC_CLE in self.state.options:
95            self.synchronize_cle = True
96        if options.SYMBION_KEEP_STUBS_ON_SYNC in self.state.options:
97            self.stubs_on_sync = True
98
99        target = self.state.project.concrete_target
100
101        # Sync angr registers with the one getting from the concrete target
102        # registers that we don't want to concretize.
103        l.debug("Synchronizing general purpose registers")
104
105        to_sync_register = list(filter(lambda x: x.concrete, self.state.arch.register_list))
106
107        for register in to_sync_register:
108
109            # before let's sync all the subregisters of the current register.
110            # sometimes this can be helpful ( i.e. ymmm0 e xmm0 )
111            if register.subregisters:
112                subregisters_names = map(lambda x: x[0], register.subregisters)
113                self._sync_registers(subregisters_names, target)
114
115            # finally let's synchronize the whole register
116            self._sync_registers([register.name], target)
117
118        if self.synchronize_cle:
119            self._sync_cle(target)
120
121        # Synchronize the imported functions addresses (.got, IAT) in the
122        # concrete process with ones used in the SimProcedures dictionary
123        if self.state.project.use_sim_procedures and not self.state.project.loader.main_object.pic:
124            self._sync_simproc()
125        else:
126            l.debug("SimProc not restored, you are going to simulate also the code of external libraries!")
127
128        # flush the angr memory in order to synchronize them with the content of the
129        # concrete process memory when a read/write to the page is performed
130        self.state.memory.flush_pages(self.whitelist)
131        l.info("Exiting SimEngineConcrete: simulated address %x concrete address %x ", self.state.addr,
132               target.read_register("pc"))
133
134        # now we have to register a SimInspect in order to synchronize the segments register
135        # on demand when the symbolic execution accesses it
136        if self.state.project.arch.name != 'ARMHF' and not self.segment_registers_callback_initialized:
137            segment_register_name = self.state.project.simos.get_segment_register_name()
138            if segment_register_name:
139                self.fs_register_bp = self.state.inspect.b('reg_read',
140                                                           reg_read_offset=segment_register_name,
141                                                           action=_sync_segments)
142
143                self.segment_registers_callback_initialized = True
144
145                l.debug("Set SimInspect breakpoint to the new state!")
146            else:
147                l.error("Can't set breakpoint to synchronize segments registers, horrible things will happen.")
148
149    def _sync_registers(self, register_names, target):
150        for register_name in register_names:
151            try:
152                reg_value = target.read_register(register_name)
153                setattr(self.state.regs, register_name, reg_value)
154                l.debug("Register: %s value: %x ", register_name, self.state.solver.eval(getattr(self.state.regs,
155                                                                                                 register_name),
156                                                                                         cast_to=int))
157            except SimConcreteRegisterError as exc:
158                l.debug("Can't set register %s reason: %s, if this register is not used "
159                        "this message can be ignored", register_name, exc)
160
161    def _sync_cle(self, target):
162
163        def _check_mapping_name(cle_mapping_name, concrete_mapping_name):
164            if cle_mapping_name == concrete_mapping_name:
165                return True
166            else:
167                # removing version and extension information from the library name
168                cle_mapping_name = re.findall(r"[\w']+", cle_mapping_name)
169                concrete_mapping_name = re.findall(r"[\w']+", concrete_mapping_name)
170                return cle_mapping_name[0] == concrete_mapping_name[0]
171
172        l.debug("Synchronizing CLE backend with the concrete process memory mapping")
173        try:
174            vmmap = target.get_mappings()
175        except NotImplementedError:
176            l.critical("Can't synchronize CLE backend using the ConcreteTarget provided.")
177            self.synchronize_cle = False  # so, deactivate this feature
178            l.debug("CLE synchronization has been deactivated")
179            return
180
181        for mapped_object in self.state.project.loader.all_elf_objects:
182            binary_name = os.path.basename(mapped_object.binary)
183
184            # this object has already been sync, skip it.
185            if binary_name in self.already_sync_objects_addresses:
186                continue
187
188            for mmap in vmmap:
189                if _check_mapping_name(binary_name, mmap.name):
190                    l.debug("Match! %s -> %s", mmap.name, binary_name)
191
192                    # let's make sure that we have the header at this address to confirm that it is the
193                    # base address.
194                    # That's not a perfect solution, but should work most of the time.
195                    result = target.read_memory(mmap.start_address, 0x10)
196
197                    if self.state.project.loader.main_object.check_magic_compatibility(io.BytesIO(result)):
198                        if mapped_object.mapped_base == mmap.start_address:
199                            # We already have the correct address for this memory mapping
200                            l.debug("Object %s is already rebased correctly at 0x%x", binary_name,
201                                    mapped_object.mapped_base)
202                            self.already_sync_objects_addresses.append(mmap.name)
203
204                            break  # object has been synchronized, move to the next one!
205
206                        # rebase the object if the CLE address doesn't match the real one,
207                        # this can happen with PIE binaries and libraries.
208                        l.debug("Remapping object %s mapped at address 0x%x at address 0x%x", binary_name,
209                                mapped_object.mapped_base, mmap.start_address)
210
211                        old_mapped_base = mapped_object.mapped_base
212                        mapped_object.mapped_base = mmap.start_address  # Rebase now!
213
214                        # TODO re-write this horrible thing
215                        mapped_object.sections._rebase(abs(mmap.start_address - old_mapped_base))  # fix sections
216                        mapped_object.segments._rebase(abs(mmap.start_address - old_mapped_base))  # fix segments
217
218                        self.already_sync_objects_addresses.append(mmap.name)
219                        break  # object has been synchronized, move to the next one!
220
221    def _sync_simproc(self):
222
223        l.debug("Restoring SimProc using concrete memory")
224
225        for reloc in self.state.project.loader.main_object.relocs:
226            if reloc.symbol:  # consider only reloc with a symbol
227                l.debug("Trying to re-hook SimProc %s", reloc.symbol.name)
228                # l.debug("reloc.rebased_addr: %#x " % reloc.rebased_addr)
229
230                if self.state.project.simos.name == 'Win32':
231                    func_address = self.state.project.concrete_target.read_memory(reloc.rebased_addr, self.state.arch.bytes)
232                    func_address = struct.unpack(self.state.project.arch.struct_fmt(), func_address)[0]
233                elif self.state.project.simos.name == 'Linux':
234                    try:
235                        func_address = self.state.project.loader.main_object.plt[reloc.symbol.name]
236                    except KeyError:
237                        continue
238                else:
239                    l.info("Can't synchronize simproc, binary format not supported.")
240                    return
241
242                l.debug("Function address hook is now: %#x ", func_address)
243                self.state.project.rehook_symbol(func_address, reloc.symbol.name, self.stubs_on_sync)
244
245                if self.synchronize_cle and not self.state.project.loader.main_object.contains_addr(func_address):
246                    old_func_symbol = self.state.project.loader.find_symbol(reloc.symbol.name)
247
248                    if old_func_symbol:  # if we actually have a symbol
249                        owner_obj = old_func_symbol.owner
250
251                        # calculating the new real address
252                        new_relative_address = func_address - owner_obj.mapped_base
253
254                        new_func_symbol = cle.backends.Symbol(owner_obj, old_func_symbol.name, new_relative_address,
255                                                              old_func_symbol.size, old_func_symbol.type)
256
257                        for new_reloc in self.state.project.loader.find_relevant_relocations(old_func_symbol.name):
258                            if new_reloc.symbol.name == new_func_symbol.name and \
259                                    new_reloc.value != new_func_symbol.rebased_addr:
260                                l.debug("Updating CLE symbols metadata, moving %s from 0x%x to 0x%x",
261                                        new_reloc.symbol.name,
262                                        new_reloc.value,
263                                        new_func_symbol.rebased_addr)
264
265                                new_reloc.resolve(new_func_symbol)
266                                new_reloc.relocate([])
267
268
269from ..sim_state import SimState
270from .. import sim_options as options
271SimState.register_default('concrete', Concrete)
272