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