def change_symbolic_target(state): if state.inspect.address_concretization_action == 'store': state.inspect.address_concretization_expr = claripy.BVV( 0x1000, state.arch.bits)
Follow HeapHopper Setting ''' added_options = set() added_options.add(angr.options.REVERSE_MEMORY_NAME_MAP) # I don't know wtf # added_options.add(angr.options.STRICT_PAGE_ACCESS) # I don't know wtf added_options.add(angr.options.CONCRETIZE_SYMBOLIC_FILE_READ_SIZES) # I don't know wtf added_options.add(angr.options.ZERO_FILL_UNCONSTRAINED_MEMORY) # make unknown regions hold null added_options.add(angr.options.ZERO_FILL_UNCONSTRAINED_REGISTERS) # make unknown regions hold null state = proj.factory.entry_state(add_options=added_options) bss = proj.loader.main_object.sections_map['.bss'] heap_base = ((bss.vaddr + bss.memsize) & ~0xfff) + 0x1000 heap_size = 64 * 4096 new_brk = claripy.BVV(heap_base + heap_size, proj.arch.bits) print(bss) print(hex(heap_base)) print(new_brk) heap = angr.SimHeapBrk(heap_base=heap_base, heap_size=heap_size) set_brk_ret = state.posix.set_brk(new_brk) state.register_plugin('heap', heap) is_UAF = False state.globals['alloc_list'] = [] ''' alloc_status['addr'] = 0xdeadbeef alloc_status['free'] = False ### double-free means when this is true, and still free this
def _op_generic_CmpNEZ(self, args): assert len(args) == 1 args = [args[0], claripy.BVV(0, args[0].size())] return self.generic_compare( args, operator.ne) # TODO: Is this the correct action for scalars?
def interpret(self, startpos, args, addr=None, simfd=None): """ implement scanf - extract formatted data from memory or a file according to the stored format specifiers and store them into the pointers extracted from `args`. :param startpos: The index of the first argument corresponding to the first format element :param args: A function which, given the index of an argument to the function, returns that argument :param addr: The address in the memory to extract data from, or... :param simfd: A file descriptor to use for reading data from :return: The number of arguments parsed """ if simfd is not None and isinstance( simfd.read_storage, SimPackets) and simfd.read_pos == len( simfd.read_storage.content): argnum = startpos for component in self.components: if type(component) is bytes: sdata, _ = simfd.read_data(len(component), short_reads=False) self.state.solver.add(sdata == component) elif isinstance(component, claripy.Bits): sdata, _ = simfd.read_data(len(component) // 8, short_reads=False) self.state.solver.add(sdata == component) elif component.spec_type == b's': if component.length_spec is None: sdata, slen = simfd.read_data( self.state.libc.buf_symbolic_bytes) else: sdata, slen = simfd.read_data(component.length_spec) for byte in sdata.chop(8): self.state.solver.add( claripy.And(*[ byte != char for char in self.SCANF_DELIMITERS ])) self.state.memory.store(args(argnum), sdata, size=slen) self.state.memory.store( args(argnum) + slen, claripy.BVV(0, 8)) argnum += 1 elif component.spec_type == b'c': sdata, _ = simfd.read_data(1, short_reads=False) self.state.memory.store(args(argnum), sdata) argnum += 1 else: bits = component.size * 8 if component.spec_type == b'x': base = 16 elif component.spec_type == b'o': base = 8 else: base = 10 # here's the variable representing the result of the parsing target_variable = self.state.solver.BVS( 'scanf_' + component.string.decode(), bits, key=('api', 'scanf', argnum - startpos, component.string)) negative = claripy.SLT(target_variable, 0) # how many digits does it take to represent this variable fully? max_digits = int(math.ceil(math.log(2**bits, base))) # how many digits does the format specify? spec_digits = component.length_spec # how many bits can we specify as input? available_bits = float( 'inf' ) if spec_digits is None else spec_digits * math.log( base, 2) not_enough_bits = available_bits < bits # how many digits will we model this input as? digits = max_digits if spec_digits is None else spec_digits # constrain target variable range explicitly if it can't take on all possible values if not_enough_bits: self.state.solver.add( self.state.solver.And( self.state.solver.SLE(target_variable, (base**digits) - 1), self.state.solver.SGE( target_variable, -(base**(digits - 1) - 1)))) # perform the parsing in reverse - constrain the input digits to be the string version of the input # this only works because we're reading from a packet stream and therefore nobody has the ability # to add other constraints to this data! # this makes z3's job EXTREMELY easy sdata, _ = simfd.read_data(digits, short_reads=False) for i, digit in enumerate(reversed(sdata.chop(8))): digit_value = (target_variable // (base**i)) % base digit_ascii = digit_value + ord('0') if base > 10: digit_ascii = claripy.If( digit_value >= 10, digit_value + (-10 + ord('a')), digit_ascii) # if there aren't enough bits, we can increase the range by accounting for the possibility that # the first digit is a minus sign if not_enough_bits: if i == digits - 1: neg_digit_ascii = ord('-') else: neg_digit_value = (-target_variable // (base**i)) % base neg_digit_ascii = neg_digit_value + ord('0') if base > 10: neg_digit_ascii = claripy.If( neg_digit_value >= 10, neg_digit_value + (-10 + ord('a')), neg_digit_ascii) digit_ascii = claripy.If(negative, neg_digit_ascii, digit_ascii) self.state.solver.add(digit == digit_ascii[7:0]) self.state.memory.store( args(argnum), target_variable, endness=self.state.arch.memory_endness) argnum += 1 return argnum - startpos # TODO: we only support one format specifier in interpretation for now format_specifier_count = sum(1 for x in self.components if isinstance(x, FormatSpecifier)) if format_specifier_count > 1: l.warning( "We don't support more than one format specifiers in format strings." ) if simfd is not None: region = simfd.read_storage addr = simfd._pos if hasattr( simfd, '_pos') else simfd._read_pos # XXX THIS IS BAD else: region = self.parser.state.memory bits = self.parser.state.arch.bits failed = self.parser.state.solver.BVV(0, bits) argpos = startpos position = addr for component in self.components: if isinstance(component, bytes): # TODO we skip non-format-specifiers in format string interpretation for now # if the region doesn't match the concrete component, we need to return immediately pass else: fmt_spec = component try: dest = args(argpos) except SimProcedureArgumentError: dest = None if fmt_spec.spec_type == b's': # set some limits for the find max_str_len = self.parser.state.libc.max_str_len max_sym_bytes = self.parser.state.libc.buf_symbolic_bytes # has the length of the format been limited by the string itself? if fmt_spec.length_spec is not None: max_str_len = fmt_spec.length_spec max_sym_bytes = fmt_spec.length_spec # TODO: look for limits on other characters which scanf is sensitive to, '\x00', '\x20' ohr, ohc, ohi = region.find( position, self.parser.state.solver.BVV(b'\n'), max_str_len, max_symbolic_bytes=max_sym_bytes) # if no newline is found, mm is position + max_strlen # If-branch will really only happen for format specifiers with a length mm = self.parser.state.solver.If(ohr == 0, position + max_str_len, ohr) # we're just going to concretize the length, load will do this anyways length = self.parser.state.solver.max_int(mm - position) src_str = region.load(position, length) # TODO all of these should be delimiters we search for above # add that the contents of the string cannot be any scanf %s string delimiters for delimiter in set(FormatString.SCANF_DELIMITERS): delim_bvv = self.parser.state.solver.BVV(delimiter) for i in range(length): self.parser.state.add_constraints( region.load(position + i, 1) != delim_bvv) # write it out to the pointer self.parser.state.memory.store(dest, src_str) # store the terminating null byte self.parser.state.memory.store( dest + length, self.parser.state.solver.BVV(0, 8)) position += length else: # XXX: atoi only supports strings of one byte if fmt_spec.spec_type in [b'd', b'i', b'u', b'x']: base = 16 if fmt_spec.spec_type == b'x' else 10 status, i, num_bytes = self.parser._sim_atoi_inner( position, region, base=base, read_length=fmt_spec.length_spec) # increase failed count if we were unable to parse it failed = self.parser.state.solver.If( status, failed, failed + 1) position += num_bytes elif fmt_spec.spec_type == b'c': i = region.load(position, 1) i = i.zero_extend(bits - 8) position += 1 else: raise SimProcedureError( "unsupported format spec '%s' in interpret" % fmt_spec.spec_type) i = self.parser.state.solver.Extract( fmt_spec.size * 8 - 1, 0, i) self.parser.state.memory.store( dest, i, size=fmt_spec.size, endness=self.parser.state.arch.memory_endness) argpos += 1 if simfd is not None: _, realsize = simfd.read_data(position - addr) self.state.solver.add(realsize == position - addr) return (argpos - startpos) - failed
def _fp_vector_comparison(cmp, a0, a1): # for cmpps_eq stuff, i.e. Iop_CmpEQ32Fx4 return claripy.If(cmp(a0, a1), claripy.BVV(-1, len(a0)), claripy.BVV(0, len(a0)))
def _ail_handle_Const(self, expr: Expr.Const) -> PropValue: return PropValue.from_value_and_details( claripy.BVV(expr.value, expr.bits), expr.size, expr, self._codeloc())
#!/usr/bin/env python3 import angr import socket import claripy import pickle with open("./code.bin", "rb") as f: code = f.read() code = angr.SimFile('code.bin', content=code) stdin = claripy.BVV(b"1234567789012345678901234567" + b"\n") p = angr.Project("./eVMoji") s = p.factory.main_state(args=["eVMoji", "code.bin"], stdin=stdin) s.fs.insert("code.bin", code) s.options.add(angr.options.UNICORN) # base = tracer.QEMURunner(argv=["eVMoji", "code.bin"], binary="./eVMoji", input=b'A'*32 + b'\n', record_stdout=True, project=p) # print(base.stdout.decode()) # from IPython import embed # embed() sm = p.factory.simulation_manager(s) sm.explore(find=lambda s: b"tRy" in s.posix.dumps(1)) print(sm)
def run(self): addr = self.state.libc.heap_location self.state.libc.heap_location += 4 self.state.memory.store(addr, claripy.BVV(0x00000000, 32)) return addr
def run(self, hProv, dwLen, pbBuffer): print 'CryptGenRandom(%s, %s, %s)' % (hProv, dwLen, pbBuffer) l = self.state.se.any_int(dwLen) * 8 self.state.memory.store(pbBuffer, claripy.BVV(random.getrandbits(l), l)) return 1
def run(self, ptr): print 'GetSystemTimeAsFileTime(%s)' % ptr self.state.memory.store(ptr, claripy.BVV(0x0000000000000000, 64)) return 0
def run(self, ptr): print 'QueryPerformanceCounter(%s)' % ptr self.state.memory.store(ptr, claripy.BVV(0x0000000000000000, 64)) return 1
def run(self): addr = self.state.libc.heap_location self.state.libc.heap_location += 1 self.state.memory.store(addr, claripy.BVV(0, 8)) print 'GetCommandLineA() = %s' % hex(addr) return addr
def run(self, hModule, lpFilename, nSize): print 'GetModuleFileName(%s, %s, %s)' % (hModule, lpFilename, nSize) self.state.memory.store(lpFilename, claripy.BVV(0, 16)) return 1
def run(self, key, subkey, options, sam, result): print 'RegOpenKeyExA(%s, %s, %s, %s, %s)' % ( key, self.load_str(subkey), options, sam, result) self.state.memory.store(result, claripy.BVV(0xdeadbeefdeadbeef, 64)) return 0
import angr import claripy p = angr.Project("change_my_mind") flag_chars = [claripy.BVS('flag_%d' % i, 8) for i in range(0x30-1)] flag = claripy.Concat(*flag_chars + [claripy.BVV(b'\n')]) state = p.factory.entry_state(stdin=flag) simgr = p.factory.simulation_manager(state) print(simgr.explore(find=(0x400000+0x169d), avoid=(0x400000+0x160A))) f = simgr.found[0] # this just confirms that the character length is correct (0x2F characters) simgr.move('found', 'active') print(simgr.explore(find=(0x400000+0x1693), avoid=(0x400000+0x160A))) f = simgr.found[0] # this is the state passing a check of some calculate on input == 0 print(f) print((b"STDOUT: "+f.posix.dumps(1)).decode("utf-8")) print((b"FLAG: "+f.solver.eval(flag, cast_to=bytes)).decode("utf-8")) """ STDOUT: Hello! Please provide your credentials! >>Lets'check... FLAG: justCTF{1_ch4ng3d_my_m1nd_by_cl34r1n6_7h3_r00m} """
def trace(config_name, binary_name): # Get config config = parse_config(config_name) print(config) # Set logging logger.info('Searching for vulns') logging.basicConfig() # TODO: disable in production config['log_level'] = "DEBUG" angr.manager.l.setLevel(config['log_level']) angr.exploration_techniques.spiller.l.setLevel(config['log_level']) cle.loader.l.setLevel(config['log_level']) logger.setLevel(config['log_level']) # Get libc path libc_path = os.path.expanduser(config['libc']) libc_name = os.path.basename(libc_path) # Get allocator path allocator_path = os.path.expanduser(config['allocator']) if allocator_path == 'default': allocator_path = libc_path allocator_name = os.path.basename(allocator_path) print("allocator_path: ", allocator_path) print("libc_path: ", libc_path) # Create project and disable sim_procedures for the libc proj = angr.Project( binary_name, exclude_sim_procedures_func=use_sim_procedure, load_options={ 'ld_path': [os.path.dirname(allocator_path), os.path.dirname(libc_path)], 'auto_load_libs': True }) input("@ angr init @") # Find write_target write_target_var = proj.loader.main_object.get_symbol('write_target') print("write_target_var: ", write_target_var) # Get libc print("libc_name: ", libc_name) libc = proj.loader.shared_objects[libc_name] # Get allocator print("allocator_name: ", allocator_name) allocator = proj.loader.shared_objects[allocator_name] # Create state and enable reverse memory map added_options = set() added_options.add(angr.options.REVERSE_MEMORY_NAME_MAP) added_options.add(angr.options.STRICT_PAGE_ACCESS) added_options.add(angr.options.CONCRETIZE_SYMBOLIC_FILE_READ_SIZES) added_options.add(angr.options.ZERO_FILL_UNCONSTRAINED_MEMORY) added_options.add(angr.options.ZERO_FILL_UNCONSTRAINED_REGISTERS) added_options.add(angr.options.SIMPLIFY_EXPRS) added_options.update(angr.options.simplification) added_options.update(angr.options.unicorn) #print(added_options) print("config['use_vsa']: ", config['use_vsa']) if config['use_vsa']: added_options.add( angr.options.APPROXIMATE_SATISFIABILITY) # vsa for satisfiability added_options.add(angr.options.APPROXIMATE_GUARDS) # vsa for guards added_options.add( angr.options.APPROXIMATE_MEMORY_SIZES) # vsa for mem_sizes added_options.add( angr.options.APPROXIMATE_MEMORY_INDICES) # vsa for mem_indices #print(added_options) removed_options = set() removed_options.add(angr.options.LAZY_SOLVES) # set heap location right after bss ### just to know the malloc addr should inside the heap ### nothing about heap content bss = proj.loader.main_object.sections_map['.bss'] heap_base = ((bss.vaddr + bss.memsize) & ~0xfff) + 0x1000 heap_size = 64 * 4096 new_brk = claripy.BVV(heap_base + heap_size, proj.arch.bits) print(bss) print(hex(heap_base)) print(new_brk) heap = SimHeapBrk(heap_base=heap_base, heap_size=heap_size) state = proj.factory.entry_state(add_options=added_options, remove_options=removed_options) state.register_plugin('heap', heap) state.register_plugin( 'heaphopper', HeapConditionTracker(config=config, wtarget=(write_target_var.rebased_addr, write_target_var.size), libc=libc, allocator=allocator)) set_brk_ret = state.posix.set_brk(new_brk) state.heaphopper.set_level(config['log_level']) print("set_brk_ret: ", set_brk_ret) print("state.heap: ", state.heap) print("state.heaphopper: ", state.heaphopper) # IPython.embed() #print(config['fix_loader_problem']) if config['fix_loader_problem']: loader_name = os.path.basename(config['loader']) libc_version = re.findall(r'libc-([\d\.]+)', libc_path)[0] fix_loader_problem(proj, state, loader_name, libc_version) var_dict = setup_state(state, proj, config) #print("var_dict:", var_dict) # Set memory concretization strategies state.memory.read_strategies = [ angr.state_plugins.symbolic_memory.concretization_strategies. SimConcretizationStrategySolutions(16), angr.state_plugins.symbolic_memory.concretization_strategies. SimConcretizationStrategyControlledData(4096, var_dict['global_vars']), angr.state_plugins.symbolic_memory.concretization_strategies. SimConcretizationStrategyEval(4096) ] state.memory.write_strategies = [ angr.state_plugins.symbolic_memory.concretization_strategies. SimConcretizationStrategySolutions(16), angr.state_plugins.symbolic_memory.concretization_strategies. SimConcretizationStrategyControlledData(4096, var_dict['global_vars']), angr.state_plugins.symbolic_memory.concretization_strategies. SimConcretizationStrategyEval(4096) ] # Hook malloc and free malloc_addr = allocator.get_symbol('malloc').rebased_addr print("malloc_addr: ", hex(malloc_addr)) free_addr = allocator.get_symbol('free').rebased_addr malloc_plt = proj.loader.main_object.plt.get('malloc') free_plt = proj.loader.main_object.plt.get('free') proj.hook(addr=malloc_plt, hook=MallocInspect(malloc_addr=malloc_addr, vulns=config['vulns'], ctrl_data=var_dict['allocs'])) proj.hook(addr=free_plt, hook=FreeInspect(free_addr=free_addr, vulns=config['vulns'], sym_data=var_dict['sdata_addrs'])) found_paths = [] # Configure simgr sm = proj.factory.simgr(thing=state) sm.use_technique( VulnChecker(config['mem_corruption_fd'], config['input_pre_constraint'], config['input_values'], config['stop_found'], config['filter_fake_frees'])) if config['use_mem_limiter']: sm.use_technique( MemLimiter(config['mem_limit'], config['drop_errored'])) if config['use_dfs']: sm.use_technique(angr.exploration_techniques.DFS()) if config['use_veritesting']: sm.use_technique(angr.exploration_techniques.Veritesting()) if config['use_concretizer']: concr_addrs = var_dict['malloc_size_addrs'] + var_dict['allocs'] sm.use_technique(Concretizer(concr_addrs)) if config['spiller']: if config['dfs']: src_stash = 'deferred' else: src_stash = 'active' spill_conf = config['spiller_conf'] ana_setup() spiller = angr.exploration_techniques.Spiller( src_stash=src_stash, min=spill_conf['min'], max=spill_conf['max'], staging_min=spill_conf['staging_min'], staging_max=spill_conf['staging_max'], priority_key=priority_key) sm.use_technique(spiller) avoids = (libc.get_symbol('abort').rebased_addr, ) sm.use_technique( angr.exploration_techniques.Explorer(find=(var_dict['winning_addr'], ), avoid=avoids)) # Create fd for memory corruption input name = b'memory_corruption' path = b'/tmp/%s' % name mem_corr_fd = config['mem_corruption_fd'] # backing = SimSymbolicMemory(memory_id='file_%s' % name) # backing.set_state(state) f = SimFile(name, writable=False) f.set_state(state) state.fs.insert(path, f) real_fd = state.posix.open(path, flags=Flags.O_RDONLY, preferred_fd=mem_corr_fd) if mem_corr_fd != real_fd: raise Exception("Overflow fd already exists.") #state.posix.fd[mem_corr_fd] = f #state.posix.fs[name] = f # constrain input if config['input_pre_constraint']: if 'overflow' in config['zoo_actions']: overflow_bytes = config['zoo_actions']['overflow'] * max( config['overflow_sizes']) else: overflow_bytes = 0 if 'uaf' in config['zoo_actions']: uaf_bytes = config['zoo_actions']['uaf'] * config['header_size'] else: uaf_bytes = 0 if 'arb_relative_write' in config['zoo_actions']: arw_bytes = config['zoo_actions'][ 'arb_relative'] * state.arch.byte_width * 2 else: arw_bytes = 0 num_bytes = overflow_bytes + uaf_bytes + arw_bytes input_bytes = state.posix.fd[mem_corr_fd].read_data(num_bytes)[0].chop( 8) state.posix.fd[mem_corr_fd].seek(0) constrain_input(state, input_bytes, config['input_values']) # Manual inspection loop debug = False stop = False while len(sm.active) > 0 and not stop: if debug: debug = False IPython.embed() sm.step() logger.debug(f"sm addr: {sm.active}") #input("@") if heardEnter(): debug = True print(sm.vuln) print("found_paths: ", found_paths) sm.move(from_stash='found', to_stash='vuln', filter_func=lambda p: p.heaphopper.vulnerable) found_paths.extend(sm.vuln) if config['spiller']: ana_teardown() print("found_paths: ", found_paths) input("@ found vulns @") for path in found_paths: win_addr, heap_func = get_win_addr(proj, path.history.bbl_addrs.hardcopy) path.heaphopper.win_addr = win_addr last_line = get_last_line(win_addr, binary_name) path.heaphopper.last_line = last_line if path.heaphopper.arb_write_info: path.heaphopper.arb_write_info[ 'instr'] = proj.loader.shared_objects[ allocator_name].addr_to_offset( path.heaphopper.arb_write_info['instr']) logger.info('Found {} vulns'.format(len(found_paths))) if len(found_paths) > 0: arb_writes = store_results(config['num_results'], binary_name, found_paths, var_dict, config['mem_corruption_fd']) if config['store_desc']: store_vuln_descs(binary_name, found_paths, var_dict, arb_writes) IPython.embed() return 0
def test_symbolic_merge(state): val = 0x01020304 state.memory.store(0x0, claripy.BVV(val, 32)) a = claripy.BVS('a0', 64) state.se.add(a <= 1) state.memory.store(a, claripy.BVV(0x5, 8)) res = state.memory.load(0x0, 1) check(state, res, [5, 1]) s1 = state.copy() s1.memory.store(0x1, claripy.BVV(0x6, 8)) a1 = claripy.BVS('a1', 64) s1.se.add(a1 >= 1) s1.se.add(a1 <= 2) s1.memory.store(a1, claripy.BVV(0x7, 8)) s2 = state.copy() s2.memory.store(0x1, claripy.BVV(0x8, 8)) a2 = claripy.BVS('a2', 64) s2.se.add(a2 >= 1) s2.se.add(a2 <= 2) s2.memory.store(a2, claripy.BVV(0x9, 8)) s3 = s1.copy() guard = claripy.BVS('guard', 32) s3.memory.merge([s2.memory], [guard > 1, guard <= 1], s1.memory) res = s3.memory.load(0x0, 1) check(s3, res, [5], (a == 0, )) check(s3, res, [1], (a == 1, )) res = s3.memory.load(0x1, 1) check(s3, res, [7], ( guard > 1, a1 == 1, )) check(s3, res, [6], ( guard > 1, a1 == 2, )) check(s3, res, [9], ( guard <= 1, a2 == 1, )) check(s3, res, [8], ( guard <= 1, a2 == 2, )) res = s3.memory.load(0x2, 1) check(s3, res, [7], ( guard > 1, a1 == 2, )) check(s3, res, [3], ( guard > 1, a1 == 1, )) check(s3, res, [9], ( guard <= 1, a2 == 2, )) check(s3, res, [3], ( guard <= 1, a2 == 1, )) res = s3.memory.load(0x3, 1) check(s3, res, [4], set())
def _op_fgeneric_CmpEQ(self, a0, a1): # pylint: disable=no-self-use # for cmpps_eq stuff, i.e. Iop_CmpEQ32Fx4 return claripy.If(claripy.fpEQ(a0, a1), claripy.BVV(-1, len(a0)), claripy.BVV(0, len(a0)))
def state_blank(self, addr=None, initial_prefix=None, brk=None, stack_end=None, stack_size=1024*1024*8, stdin=None, thread_idx=None, **kwargs): """ Initialize a blank state. All parameters are optional. :param addr: The execution start address. :param initial_prefix: :param stack_end: The end of the stack (i.e., the byte after the last valid stack address). :param stack_size: The number of bytes to allocate for stack space :param brk: The address of the process' break. :return: The initialized SimState. Any additional arguments will be passed to the SimState constructor """ # TODO: move ALL of this into the SimState constructor if kwargs.get('mode', None) is None: kwargs['mode'] = self.project._default_analysis_mode if kwargs.get('permissions_backer', None) is None: # just a dict of address ranges to permission bits permission_map = { } for obj in self.project.loader.all_objects: for seg in obj.segments: perms = 0 # bit values based off of protection bit values from sys/mman.h if seg.is_readable: perms |= 1 # PROT_READ if seg.is_writable: perms |= 2 # PROT_WRITE if seg.is_executable: perms |= 4 # PROT_EXEC permission_map[(seg.min_addr, seg.max_addr)] = perms permissions_backer = (self.project.loader.main_object.execstack, permission_map) kwargs['permissions_backer'] = permissions_backer if kwargs.get('memory_backer', None) is None: kwargs['memory_backer'] = self.project.loader.memory if kwargs.get('os_name', None) is None: kwargs['os_name'] = self.name state = SimState(self.project, **kwargs) if stdin is not None and not isinstance(stdin, SimFileBase): if type(stdin) is type: stdin = stdin(name='stdin', has_end=False) else: stdin = SimFileStream(name='stdin', content=stdin, has_end=True) last_addr = self.project.loader.main_object.max_addr actual_brk = (last_addr - last_addr % 0x1000 + 0x1000) if brk is None else brk state.register_plugin('posix', SimSystemPosix(stdin=stdin, brk=actual_brk)) actual_stack_end = state.arch.initial_sp if stack_end is None else stack_end if o.ABSTRACT_MEMORY not in state.options: state.memory.mem._preapproved_stack = IRange(actual_stack_end - stack_size, actual_stack_end) if state.arch.sp_offset is not None: state.regs.sp = actual_stack_end + (state.arch.stack_change if state.arch.stack_change is not None else 0) if initial_prefix is not None: for reg in state.arch.default_symbolic_registers: state.registers.store(reg, state.solver.BVS( initial_prefix + "_" + reg, state.arch.bits, explicit_name=True, key=('reg', reg), eternal=True)) for reg, val, is_addr, mem_region in state.arch.default_register_values: region_base = None # so pycharm does not complain if is_addr: if isinstance(mem_region, tuple): # unpack it mem_region, region_base = mem_region elif mem_region == 'global': # Backward compatibility region_base = 0 else: raise AngrSimOSError('You must specify the base address for memory region "%s". ' % mem_region) # special case for stack pointer override if actual_stack_end is not None and state.arch.registers[reg][0] == state.arch.sp_offset: continue if o.ABSTRACT_MEMORY in state.options and is_addr: address = claripy.ValueSet(state.arch.bits, mem_region, region_base, val) state.registers.store(reg, address) else: state.registers.store(reg, val) if addr is None: state.regs.ip = self.project.entry thread_name = self.project.loader.main_object.threads[thread_idx] if thread_idx is not None else None for reg, val in self.project.loader.main_object.thread_registers(thread_name).items(): if reg in state.arch.registers or reg in ('flags', 'eflags', 'rflags'): setattr(state.regs, reg, val) elif reg == 'fctrl': state.regs.fpround = (val & 0xC00) >> 10 elif reg == 'fstat': state.regs.fc3210 = (val & 0x4700) elif reg == 'ftag': empty_bools = [((val >> (x * 2)) & 3) == 3 for x in range(8)] tag_chars = [claripy.BVV(0 if x else 1, 8) for x in empty_bools] for i, tag in enumerate(tag_chars): setattr(state.regs, 'fpu_t%d' % i, tag) elif reg in ('fiseg', 'fioff', 'foseg', 'fooff', 'fop'): pass elif reg == 'mxcsr': state.regs.sseround = (val & 0x600) >> 9 else: _l.error("What is this register %s I have to translate?", reg) if addr is not None: state.regs.ip = addr # set up the "root history" node state.scratch.ins_addr = addr state.scratch.bbl_addr = addr state.scratch.stmt_idx = 0 state.history.jumpkind = 'Ijk_Boring' return state
def main(): global retn, pre_dispatcher, main_dispatcher, prologue, b, relevants, opcode if len(sys.argv) != 3: print('Usage: python deflat.py filename function_address(hex)') exit(0) opcode = {'a':'\x87', 'ae': '\x83', 'b':'\x82', 'be':'\x86', 'c':'\x82', 'e':'\x84', 'z':'\x84', 'g':'\x8F', 'ge':'\x8D', 'l':'\x8C', 'le':'\x8E', 'na':'\x86', 'nae':'\x82', 'nb':'\x83', 'nbe':'\x87', 'nc':'\x83', 'ne':'\x85', 'ng':'\x8E', 'nge':'\x8C', 'nl':'\x8D', 'nle':'\x8F', 'no':'\x81', 'np':'\x8B', 'ns':'\x89', 'nz':'\x85', 'o':'\x80', 'p':'\x8A', 'pe':'\x8A', 'po':'\x8B', 's':'\x88', 'nop':'\x90', 'jmp':'\xE9', 'j':'\x0F'} for k, v in opcode.items(): opcode[k] = ord(v) filename = sys.argv[1] start = int(sys.argv[2], 16) barf = BARF(filename) base_addr = barf.binary.entry_point >> 12 << 12 b = angr.Project(filename, load_options={'auto_load_libs': False, 'main_opts':{'custom_base_addr': 0}}) print("entry: ", b.entry) cfg = barf.recover_cfg(start=start) blocks = cfg.basic_blocks prologue = start main_dispatcher = cfg.find_basic_block(prologue).direct_branch retn, pre_dispatcher = get_retn_predispatcher(cfg) relevant_blocks, nop_blocks = get_relevant_nop_blocks(cfg) print('*******************relevant blocks************************') print('prologue:%#x' % start) print('main_dispatcher:%#x' % main_dispatcher) print('pre_dispatcher:%#x' % pre_dispatcher) print('retn:%#x' % retn) print('relevant_blocks:', [hex(addr) for addr in relevant_blocks]) print('*******************symbolic execution*********************') relevants = relevant_blocks relevants.append(prologue) relevants_without_retn = list(relevants) relevants.append(retn) flow = {} for parent in relevants: flow[parent] = [] modify_value = None patch_instrs = {} for relevant in relevants_without_retn: print('-------------------dse %#x---------------------' % relevant) block = cfg.find_basic_block(relevant) has_branches = False hook_addr = None for ins in block.instrs: if ins.mnemonic.startswith('cmov'): patch_instrs[relevant] = ins has_branches = True elif ins.mnemonic.startswith('call'): hook_addr = ins.address if has_branches: flow[relevant].append(symbolic_execution(relevant, hook_addr, claripy.BVV(1, 1), True)) flow[relevant].append(symbolic_execution(relevant, hook_addr, claripy.BVV(0, 1), True)) else: flow[relevant].append(symbolic_execution(relevant, hook_addr)) print('************************flow******************************') for (k, v) in flow.items(): print('%#x:' % k, [hex(child) for child in v]) print('************************patch*****************************') flow.pop(retn) origin = open(filename, 'rb') origin_data = list(origin.read()) origin.close() recovery = open(filename + '.recovered', 'wb') for nop_block in nop_blocks: fill_nop(origin_data, nop_block.start_address - base_addr, nop_block.end_address - base_addr + 1) for (parent, childs) in flow.items(): if len(childs) == 1: last_instr = cfg.find_basic_block(parent).instrs[-1] file_offset = last_instr.address - base_addr origin_data[file_offset] = opcode['jmp'] file_offset += 1 fill_nop(origin_data, file_offset, file_offset + last_instr.size - 1) fill_jmp_offset(origin_data, file_offset, childs[0] - last_instr.address - 5) else: instr = patch_instrs[parent] file_offset = instr.address - base_addr fill_nop(origin_data, file_offset, cfg.find_basic_block(parent).end_address - base_addr + 1) origin_data[file_offset] = opcode['j'] origin_data[file_offset + 1] = opcode[instr.mnemonic[4:]] fill_jmp_offset(origin_data, file_offset + 2, childs[0] - instr.address - 6) file_offset += 6 origin_data[file_offset] = opcode['jmp'] fill_jmp_offset(origin_data, file_offset + 1, childs[1] - (instr.address + 6) - 5) recovery.write(bytearray(origin_data)) recovery.close() print('Successful! The recovered file: %s' % (filename + '.recovered'))
def state_entry(self, **kwargs): if isinstance(self.proj.loader.main_bin, BackedCGC): kwargs['permissions_backer'] = ( True, self.proj.loader.main_bin.permissions_map) kwargs['add_options'] = {o.CGC_ZERO_FILL_UNCONSTRAINED_MEMORY } | kwargs.get('add_options', set()) state = super(SimCGC, self).state_entry(**kwargs) if isinstance(self.proj.loader.main_bin, BackedCGC): for reg, val in self.proj.loader.main_bin.initial_register_values( ): if reg in state.arch.registers: setattr(state.regs, reg, val) elif reg == 'eflags': pass elif reg == 'fctrl': state.regs.fpround = (val & 0xC00) >> 10 elif reg == 'fstat': state.regs.fc3210 = (val & 0x4700) elif reg == 'ftag': empty_bools = [((val >> (x * 2)) & 3) == 3 for x in xrange(8)] tag_chars = [ claripy.BVV(0 if x else 1, 8) for x in empty_bools ] for i, tag in enumerate(tag_chars): setattr(state.regs, 'fpu_t%d' % i, tag) elif reg in ('fiseg', 'fioff', 'foseg', 'fooff', 'fop'): pass elif reg == 'mxcsr': state.regs.sseround = (val & 0x600) >> 9 else: l.error("What is this register %s I have to translate?", reg) # Update allocation base state.cgc.allocation_base = self.proj.loader.main_bin.current_allocation_base # Do all the writes writes_backer = self.proj.loader.main_bin.writes_backer stdout = 1 for size in writes_backer: if size == 0: continue str_to_write = state.posix.files[1].content.load( state.posix.files[1].pos, size) a = SimActionData(state, 'file_1_0', 'write', addr=claripy.BVV(state.posix.files[1].pos, state.arch.bits), data=str_to_write, size=size) state.posix.write(stdout, str_to_write, size) state.log.add_action(a) else: # Set CGC-specific variables state.regs.eax = 0 state.regs.ebx = 0 state.regs.ecx = 0x4347c000 state.regs.edx = 0 state.regs.edi = 0 state.regs.esi = 0 state.regs.esp = 0xbaaaaffc state.regs.ebp = 0 state.regs.cc_dep1 = 0x202 # default eflags state.regs.cc_op = 0 # OP_COPY state.regs.cc_dep2 = 0 # doesn't matter state.regs.cc_ndep = 0 # doesn't matter # fpu values state.regs.mm0 = 0 state.regs.mm1 = 0 state.regs.mm2 = 0 state.regs.mm3 = 0 state.regs.mm4 = 0 state.regs.mm5 = 0 state.regs.mm6 = 0 state.regs.mm7 = 0 state.regs.fpu_tags = 0 state.regs.fpround = 0 state.regs.fc3210 = 0x0300 state.regs.ftop = 0 # sse values state.regs.sseround = 0 state.regs.xmm0 = 0 state.regs.xmm1 = 0 state.regs.xmm2 = 0 state.regs.xmm3 = 0 state.regs.xmm4 = 0 state.regs.xmm5 = 0 state.regs.xmm6 = 0 state.regs.xmm7 = 0 # segmentation registers state.regs.ds = 0 state.regs.es = 0 state.regs.fs = 0 state.regs.gs = 0 state.regs.ss = 0 state.regs.cs = 0 return state
def test_expression(): bc = claripy.backends.concrete e = claripy.BVV(0x01020304, 32) nose.tools.assert_equal(len(e), 32) r = e.reversed nose.tools.assert_equal(bc.convert(r), 0x04030201) nose.tools.assert_equal(len(r), 32) nose.tools.assert_equal([ bc.convert(i) for i in r.chop(8) ], [ 4, 3, 2, 1 ] ) e1 = r[31:24] nose.tools.assert_equal(bc.convert(e1), 0x04) nose.tools.assert_equal(len(e1), 8) nose.tools.assert_equal(bc.convert(e1[2]), 1) nose.tools.assert_equal(bc.convert(e1[1]), 0) ee1 = e1.zero_extend(8) nose.tools.assert_equal(bc.convert(ee1), 0x0004) nose.tools.assert_equal(len(ee1), 16) ee1 = claripy.BVV(0xfe, 8).sign_extend(8) nose.tools.assert_equal(bc.convert(ee1), 0xfffe) nose.tools.assert_equal(len(ee1), 16) xe1 = [ bc.convert(i) for i in e1.chop(1) ] nose.tools.assert_equal(xe1, [ 0, 0, 0, 0, 0, 1, 0, 0 ]) a = claripy.BVV(1, 1) nose.tools.assert_equal(bc.convert(a+a), 2) x = claripy.BVV(1, 32) nose.tools.assert_equal(x.length, 32) y = claripy.LShR(x, 10) nose.tools.assert_equal(y.length, 32) r = claripy.BVV(0x01020304, 32) rr = r.reversed rrr = rr.reversed #nose.tools.assert_is(bc.convert(r), bc.convert(rrr)) #nose.tools.assert_is(type(bc.convert(rr)), claripy.A) nose.tools.assert_equal(bc.convert(rr), 0x04030201) nose.tools.assert_is(r.concat(rr), claripy.Concat(r, rr)) rsum = r+rr nose.tools.assert_equal(bc.convert(rsum), 0x05050505) r = claripy.BVS('x', 32) rr = r.reversed rrr = rr.reversed nose.tools.assert_is(r, rrr) # test identity nose.tools.assert_is(r, rrr) nose.tools.assert_is_not(r, rr) ii = claripy.BVS('ii', 32) ij = claripy.BVS('ij', 32) nose.tools.assert_is(ii, ii) nose.tools.assert_is_not(ii, ij) si = claripy.SI(bits=32, stride=2, lower_bound=20, upper_bound=100) sj = claripy.SI(bits=32, stride=2, lower_bound=10, upper_bound=10) sk = claripy.SI(bits=32, stride=2, lower_bound=20, upper_bound=100) nose.tools.assert_true(claripy.backends.vsa.identical(si, si)) nose.tools.assert_false(claripy.backends.vsa.identical(si, sj)) nose.tools.assert_true(claripy.backends.vsa.identical(si, sk)) nose.tools.assert_is_not(si, sj) nose.tools.assert_is_not(sj, sk) nose.tools.assert_is_not(sk, si) # test hash cache nose.tools.assert_is(a+a, a+a) # test replacement old = claripy.BVS('old', 32, explicit_name=True) new = claripy.BVS('new', 32, explicit_name=True) ooo = claripy.BVV(0, 32) old_formula = claripy.If((old + 1)%256 == 0, old+10, old+20) print old_formula.dbg_repr() new_formula = old_formula.replace(old, new) print new_formula.dbg_repr() ooo_formula = new_formula.replace(new, ooo) print ooo_formula.dbg_repr() nose.tools.assert_not_equal(hash(old_formula), hash(new_formula)) nose.tools.assert_not_equal(hash(old_formula), hash(ooo_formula)) nose.tools.assert_not_equal(hash(new_formula), hash(ooo_formula)) nose.tools.assert_equal(old_formula.variables, { 'old' }) nose.tools.assert_equal(new_formula.variables, { 'new' }) nose.tools.assert_equal(ooo_formula.variables, ooo.variables) nose.tools.assert_true(old_formula.symbolic) nose.tools.assert_true(new_formula.symbolic) nose.tools.assert_true(new_formula.symbolic) nose.tools.assert_equal(str(old_formula).replace('old', 'new'), str(new_formula)) nose.tools.assert_equal(bc.convert(ooo_formula), 20) # test dict replacement old = claripy.BVS('old', 32, explicit_name=True) new = claripy.BVS('new', 32, explicit_name=True) c = (old + 10) - (old + 20) d = (old + 1) - (old + 2) cr = c.replace_dict({(old+10).cache_key: (old+1), (old+20).cache_key: (old+2)}) nose.tools.assert_is(cr, d) # test AST collapse s = claripy.SI(bits=32, stride=0, lower_bound=10, upper_bound=10) b = claripy.BVV(20, 32) sb = s+b nose.tools.assert_is_instance(sb.args[0], claripy.ast.Base) bb = b+b # this was broken previously -- it was checking if type(bb.args[0]) == A, # and it wasn't, but was instead a subclass. leaving this out for now # nose.tools.assert_not_is_instance(bb.args[0], claripy.ast.Base) # ss = s+s # (see above) # nose.tools.assert_not_is_instance(ss.args[0], claripy.ast.Base) sob = s|b # for now, this is collapsed. Presumably, Fish will make it not collapse at some point nose.tools.assert_is_instance(sob.args[0], claripy.ast.Base) # make sure the AST collapses for delayed ops like reversing rb = b.reversed #nose.tools.assert_is_instance(rb.args[0], claripy.ast.Base) # TODO: Properly delay reversing: should not be eager nose.tools.assert_is_not(rb, bb) nose.tools.assert_is(rb, rb) # test some alternate bvv creation methods nose.tools.assert_is(claripy.BVV('AAAA'), claripy.BVV(0x41414141, 32)) nose.tools.assert_is(claripy.BVV('AAAA', 32), claripy.BVV(0x41414141, 32)) nose.tools.assert_is(claripy.BVV('AB'), claripy.BVV(0x4142, 16)) nose.tools.assert_is(claripy.BVV('AB', 16), claripy.BVV(0x4142, 16)) nose.tools.assert_raises(claripy.errors.ClaripyValueError, claripy.BVV, 'AB', 8)
def test_complex_guy(): guy_wide = claripy.widen( claripy.union( claripy.union( claripy.union(claripy.BVV(0L, 32), claripy.BVV(1, 32)), claripy.union(claripy.BVV(0L, 32), claripy.BVV(1, 32)) + claripy.BVV(1, 32)), claripy.union( claripy.union(claripy.BVV(0L, 32), claripy.BVV(1, 32)), claripy.union(claripy.BVV(0L, 32), claripy.BVV(1, 32)) + claripy.BVV(1, 32)) + claripy.BVV(1, 32)), claripy.union( claripy.union( claripy.union( claripy.union(claripy.BVV(0L, 32), claripy.BVV(1, 32)), claripy.union(claripy.BVV(0L, 32), claripy.BVV(1, 32)) + claripy.BVV(1, 32)), claripy.union( claripy.union(claripy.BVV(0L, 32), claripy.BVV(1, 32)), claripy.union(claripy.BVV(0L, 32), claripy.BVV(1, 32)) + claripy.BVV(1, 32)) + claripy.BVV(1, 32)), claripy.union( claripy.union( claripy.union(claripy.BVV(0L, 32), claripy.BVV(1, 32)), claripy.union(claripy.BVV(0L, 32), claripy.BVV(1, 32)) + claripy.BVV(1, 32)), claripy.union( claripy.union(claripy.BVV(0L, 32), claripy.BVV(1, 32)), claripy.union(claripy.BVV(0L, 32), claripy.BVV(1, 32)) + claripy.BVV(1, 32)) + claripy.BVV(1, 32)) + claripy.BVV(1, 32))) guy_inc = guy_wide + claripy.BVV(1, 32) guy_zx = claripy.ZeroExt(32, guy_inc) s, r = claripy.balancer.Balancer(claripy.backends.vsa, guy_inc <= claripy.BVV(39, 32)).compat_ret assert s assert r[0][0] is guy_wide assert claripy.backends.vsa.min(r[0][1]) == 0 assert set(claripy.backends.vsa.eval(r[0][1], 1000)) == set([4294967295] + range(39)) s, r = claripy.balancer.Balancer(claripy.backends.vsa, guy_zx <= claripy.BVV(39, 64)).compat_ret assert r[0][0] is guy_wide assert claripy.backends.vsa.min(r[0][1]) == 0 assert set(claripy.backends.vsa.eval(r[0][1], 1000)) == set([4294967295] + range(39))
def _initialize_page(self, n, new_page): if n in self._initialized: return False self._initialized.add(n) new_page_addr = n * self._page_size initialized = False if self.state is not None: self.state.scratch.push_priv(True) if self._memory_backer is None: pass elif isinstance(self._memory_backer, cle.Clemory): # first, find the right clemory backer for addr, backer in self._memory_backer.cbackers: start_backer = new_page_addr - addr if isinstance(start_backer, BV): continue if start_backer < 0 and abs(start_backer) >= self._page_size: continue if start_backer >= len(backer): continue # find permission backer associated with the address, there should be a # memory backer that matches the start_backer. if not fall back to read-write flags = Page.PROT_READ | Page.PROT_WRITE for start, end in self._permission_map: if start == addr: flags = self._permission_map[(start, end)] break snip_start = max(0, start_backer) write_start = max(new_page_addr, addr + snip_start) write_size = self._page_size - write_start % self._page_size snip = _ffi.buffer(backer)[snip_start:snip_start + write_size] mo = SimMemoryObject(claripy.BVV(snip), write_start) self._apply_object_to_page(n * self._page_size, mo, page=new_page) new_page.permissions = claripy.BVV(flags, 3) initialized = True elif len(self._memory_backer) <= self._page_size: for i in self._memory_backer: if new_page_addr <= i and i <= new_page_addr + self._page_size: if isinstance(self._memory_backer[i], claripy.ast.Base): backer = self._memory_backer[i] else: backer = claripy.BVV(self._memory_backer[i]) mo = SimMemoryObject(backer, i) self._apply_object_to_page(n * self._page_size, mo, page=new_page) initialized = True elif len(self._memory_backer) > self._page_size: for i in range(self._page_size): try: if isinstance(self._memory_backer[i], claripy.ast.Base): backer = self._memory_backer[i] else: backer = claripy.BVV(self._memory_backer[i]) mo = SimMemoryObject(backer, new_page_addr + i) self._apply_object_to_page(n * self._page_size, mo, page=new_page) initialized = True except KeyError: pass if self.state is not None: self.state.scratch.pop_priv() return initialized
def call(cls, project, target, args=(), start=None, prototype=None, **kwargs): #pylint:disable=unused-argument """ Calls a function in the binary, returning a PathGroup with the call set up. :param project: A Project instance. :type project: angr.project.Project :param target: Address of function to call. :param args: Arguments to call the function with. :param start: Optional, path (or paths) to start the call with. :param prototype: Optional, A SimTypeFunction to typecheck arguments against. :param kwargs: Any other keyword args will be passed to the PathGroup constructor. :returns: A PathGroup calling the function. :rtype: PathGroup """ fake_return_addr = project._extern_obj.get_pseudo_addr('FAKE_RETURN_ADDR') if not project.is_hooked(fake_return_addr): project.hook(fake_return_addr, CallReturn) cc = simuvex.DefaultCC[project.arch.name](project.arch) active_paths = [] if start is None: active_paths.append(project.factory.path(addr=target)) elif hasattr(start, '__iter__'): active_paths.extend(start) else: active_paths.append(start) ret_addr = claripy.BVV(fake_return_addr, project.arch.bits) def fix_arg(st, arg): if isinstance(arg, str): # store the string, nul-terminated, at the current heap location # then return a pointer to that location ptr = st.libc.heap_location st.memory.store(ptr, st.se.BVV(arg, st.arch.bits)) st.memory.store(ptr + len(arg), st.se.BVV(0, 8)) st.libc.heap_location += len(arg) + 1 return ptr elif isinstance(arg, (tuple, list)): # fix the entries of the list, then store them in a # NULL-terminated array at the current heap location and return # a pointer to there # N.B.: uses host endianness to store entries!!! mostly useful for string arrays fixed_entries = [fix_arg(st, entry) for entry in arg] cur_ptr = start_ptr = st.libc.heap_location for entry in fixed_entries: st.memory.store(cur_ptr, entry, endness=st.arch.memory_endness) cur_ptr += entry.length entry_length = fixed_entries[0].length if len(fixed_entries) > 0 else st.arch.bits st.memory.store(cur_ptr, st.se.BVV(0, entry_length)) st.libc.heap_location = cur_ptr + entry_length return start_ptr elif isinstance(arg, (int, long)): return st.se.BVV(arg, st.arch.bits) elif isinstance(arg, StringSpec): ptr = st.libc.heap_location arg.dump(st, ptr) st.libc.heap_location += len(arg) return ptr else: return arg for p in active_paths: p.state.ip = target fixed_args = [fix_arg(p.state, arg) for arg in args] cc.setup_callsite(p.state, ret_addr, fixed_args) return cls(project, active_paths=active_paths, **kwargs)
def state_entry(self, args=None, env=None, argc=None, **kwargs): state = super(SimLinux, self).state_entry(**kwargs) # Handle default values filename = self.project.filename or 'dummy_filename' if args is None: args = [filename] if env is None: env = {} # Prepare argc if argc is None: argc = claripy.BVV(len(args), state.arch.bits) elif type(argc) is int: # pylint: disable=unidiomatic-typecheck argc = claripy.BVV(argc, state.arch.bits) # Make string table for args/env/auxv table = StringTableSpec() # Add args to string table table.append_args(args) # Add environment to string table table.append_env(env) # Prepare the auxiliary vector and add it to the end of the string table # TODO: Actually construct a real auxiliary vector # current vector is an AT_RANDOM entry where the "random" value is 0xaec0aec0aec0... aux = [(25, b"\xAE\xC0" * 8)] for a, b in aux: table.add_pointer(a) if isinstance(b, bytes): table.add_string(b) else: table.add_pointer(b) table.add_null() table.add_null() # Dump the table onto the stack, calculate pointers to args, env, and auxv state.memory.store(state.regs.sp - 16, claripy.BVV(0, 8 * 16)) argv = table.dump(state, state.regs.sp - 16) envp = argv + ((len(args) + 1) * state.arch.bytes) auxv = argv + ((len(args) + len(env) + 2) * state.arch.bytes) # Put argc on stack and fix the stack pointer newsp = argv - state.arch.bytes state.memory.store(newsp, argc, endness=state.arch.memory_endness) state.regs.sp = newsp if state.arch.name in ('PPC32', ): state.stack_push(claripy.BVV(0, 32)) state.stack_push(claripy.BVV(0, 32)) state.stack_push(claripy.BVV(0, 32)) state.stack_push(claripy.BVV(0, 32)) # store argc argv envp auxv in the posix plugin state.posix.argv = argv state.posix.argc = argc state.posix.environ = envp state.posix.auxv = auxv self.set_entry_register_values(state) # set __progname progname_full = 0 progname = 0 if args: progname_full = state.mem[argv].long.concrete progname_cur = progname_full progname = progname_full while True: byte = state.mem[progname_cur].byte.resolved if byte.symbolic: break else: if state.solver.eval(byte) == ord('/'): progname = progname_cur + 1 elif state.solver.eval(byte) == 0: break progname_cur += 1 # there will be multiple copies of these symbol but the canonical ones (in the main binary, # or elsewhere if the main binary didn't have one) should get picked up here for name, val in [('__progname_full', progname_full), ('__progname', progname), ('__environ', envp), ('environ', envp), ('__libc_stack_end', state.regs.sp)]: sym = self.project.loader.find_symbol(name) if sym is not None: if sym.size != self.arch.bytes: _l.warning("Something is wrong with %s - bad size", name) else: state.mem[sym.rebased_addr].long = val return state
def state_entry(self, args=None, env=None, argc=None, **kwargs): state = super(SimLinux, self).state_entry(**kwargs) # Handle default values if args is None: args = [] if env is None: env = {} # Prepare argc if argc is None: argc = claripy.BVV(len(args), state.arch.bits) elif type(argc) in (int, long): # pylint: disable=unidiomatic-typecheck argc = claripy.BVV(argc, state.arch.bits) # Make string table for args/env/auxv table = StringTableSpec() # Add args to string table table.append_args(args) # Add environment to string table table.append_env(env) # Prepare the auxiliary vector and add it to the end of the string table # TODO: Actually construct a real auxiliary vector # current vector is an AT_RANDOM entry where the "random" value is 0xaec0aec0aec0... aux = [(25, ("AEC0" * 8).decode('hex'))] for a, b in aux: table.add_pointer(a) if isinstance(b, str): table.add_string(b) else: table.add_pointer(b) table.add_null() table.add_null() # Dump the table onto the stack, calculate pointers to args, env, and auxv state.memory.store(state.regs.sp - 16, claripy.BVV(0, 8 * 16)) argv = table.dump(state, state.regs.sp - 16) envp = argv + ((len(args) + 1) * state.arch.bytes) auxv = argv + ((len(args) + len(env) + 2) * state.arch.bytes) # Put argc on stack and fix the stack pointer newsp = argv - state.arch.bytes state.memory.store(newsp, argc, endness=state.arch.memory_endness) state.regs.sp = newsp if state.arch.name in ('PPC32', ): state.stack_push(claripy.BVV(0, 32)) state.stack_push(claripy.BVV(0, 32)) state.stack_push(claripy.BVV(0, 32)) state.stack_push(claripy.BVV(0, 32)) # store argc argv envp auxv in the posix plugin state.posix.argv = argv state.posix.argc = argc state.posix.environ = envp state.posix.auxv = auxv self.set_entry_register_values(state) return state
def state_entry(self, add_options=None, **kwargs): if isinstance(self.project.loader.main_object, BackedCGC): kwargs['permissions_backer'] = ( True, self.project.loader.main_object.permissions_map) if add_options is None: add_options = set() add_options.add(o.ZERO_FILL_UNCONSTRAINED_MEMORY) state = super(SimCGC, self).state_entry(add_options=add_options, **kwargs) if isinstance(self.project.loader.main_object, BackedCGC): # Update allocation base state.cgc.allocation_base = self.project.loader.main_object.current_allocation_base # Do all the writes writes_backer = self.project.loader.main_object.writes_backer stdout = state.posix.get_fd(1) pos = 0 for size in writes_backer: if size == 0: continue str_to_write = state.solver.BVS('file_write', size * 8) a = SimActionData(state, 'file_1_0', 'write', addr=claripy.BVV(pos, state.arch.bits), data=str_to_write, size=size) stdout.write_data(str_to_write) state.history.add_action(a) pos += size else: # Set CGC-specific variables state.regs.eax = 0 state.regs.ebx = 0 state.regs.ecx = 0x4347c000 state.regs.edx = 0 state.regs.edi = 0 state.regs.esi = 0 state.regs.esp = 0xbaaaaffc state.regs.ebp = 0 state.regs.cc_dep1 = 0x202 # default eflags state.regs.cc_op = 0 # OP_COPY state.regs.cc_dep2 = 0 # doesn't matter state.regs.cc_ndep = 0 # doesn't matter # fpu values state.regs.mm0 = 0 state.regs.mm1 = 0 state.regs.mm2 = 0 state.regs.mm3 = 0 state.regs.mm4 = 0 state.regs.mm5 = 0 state.regs.mm6 = 0 state.regs.mm7 = 0 state.regs.fpu_tags = 0 state.regs.fpround = 0 state.regs.fc3210 = 0x0300 state.regs.ftop = 0 # sse values state.regs.sseround = 0 state.regs.xmm0 = 0 state.regs.xmm1 = 0 state.regs.xmm2 = 0 state.regs.xmm3 = 0 state.regs.xmm4 = 0 state.regs.xmm5 = 0 state.regs.xmm6 = 0 state.regs.xmm7 = 0 # segmentation registers state.regs.ds = 0 state.regs.es = 0 state.regs.fs = 0 state.regs.gs = 0 state.regs.ss = 0 state.regs.cs = 0 return state
def setup_state(state, proj, config): # set heap location right after bss bss = proj.loader.main_object.sections_map['.bss'] heap_start = ((bss.vaddr + bss.memsize) & ~0xfff) + 0x1000 heap_size = 64 * 4096 new_brk = claripy.BVV(heap_start + heap_size, state.arch.bits) state.posix.set_brk(new_brk) state.libc.heap_location = heap_start state.libc.mmap_base = heap_start + heap_size * 2 # Inject symbolic controlled data into memory var_dict = dict() var_dict['global_vars'] = [] var_dict['allocs'] = [] for i in range(20): cdata = proj.loader.main_object.get_symbol('ctrl_data_{}'.format(i)) # check if ctrl_data exists if not cdata: break var_dict['global_vars'].append(cdata.rebased_addr) var_dict['allocs'].append(cdata.rebased_addr) # Set mem2chunk offset mem2chunk_var = proj.loader.main_object.get_symbol('mem2chunk_offset') var_dict['mem2chunk_addr'] = mem2chunk_var.rebased_addr mem2chunk = state.solver.BVV(value=config['mem2chunk_offset'], size=8 * 8) state.memory.store(var_dict['mem2chunk_addr'], mem2chunk, 8, endness='Iend_LE') # Inject symbolic data into memory that can't be resolved var_dict['sdata_addrs'] = [] sdata_var = proj.loader.main_object.get_symbol('sym_data') # check if sym_data exists if sdata_var: for i in range(0, config['sym_data_size'], 8): smem_elem = state.solver.BVS('smem', 8 * 8) state.memory.store(sdata_var.rebased_addr + i, smem_elem, 8, endness='Iend_LE') var_dict['sdata_addrs'].append(sdata_var.rebased_addr + i) # create entry in sym_data state storage var_dict['sym_data_ptr'] = sdata_var.rebased_addr + config[ 'mem2chunk_offset'] state.heaphopper.sym_data_states[var_dict['sym_data_ptr']] = None # add sym_data_size to heap state state.heaphopper.sym_data_size = config['sym_data_size'] # add global_ptr to global_vars: var_dict['global_vars'].append(sdata_var.rebased_addr) # Setup write_target var_dict['wtarget_addrs'] = [] write_target_var = proj.loader.main_object.get_symbol('write_target') for i in range(0, write_target_var.size, 8): write_mem_elem = state.solver.BVS('write_mem', 8 * 8) state.memory.store(write_target_var.rebased_addr + i, write_mem_elem, 8, endness='Iend_LE') var_dict['wtarget_addrs'].append(write_target_var.rebased_addr + i) var_dict['global_vars'].append(write_target_var.rebased_addr + i) # Set header size header_size_var = proj.loader.main_object.get_symbol('header_size') var_dict['header_size_addr'] = header_size_var.rebased_addr header_size = state.solver.BVV(value=config['header_size'], size=8 * 8) state.memory.store(var_dict['header_size_addr'], header_size, 8, endness='Iend_LE') # Set malloc sizes malloc_size_var = proj.loader.main_object.get_symbol('malloc_sizes') var_dict['malloc_size_addrs'] = [ malloc_size_var.rebased_addr + i for i in range(0, malloc_size_var.size, 8) ] var_dict['malloc_size_bvs'] = [] if max(config['malloc_sizes']) != 0: bvs_size = int(math.ceil(math.log(max(config['malloc_sizes']), 2))) + 1 else: bvs_size = 8 num_bytes = int(math.ceil(bvs_size / float(state.arch.byte_width))) bit_diff = num_bytes * state.arch.byte_width - bvs_size for msize in var_dict['malloc_size_addrs']: if len(config['malloc_sizes']) > 1: malloc_var = state.solver.BVS('malloc_size', bvs_size).zero_extend(bit_diff) constraint = claripy.Or(malloc_var == config['malloc_sizes'][0]) for bin_size in config['malloc_sizes'][1:]: constraint = claripy.Or(malloc_var == bin_size, constraint) state.add_constraints(constraint) else: malloc_var = state.solver.BVV(config['malloc_sizes'][0], state.arch.bits) var_dict['malloc_size_bvs'].append(malloc_var) state.memory.store(msize, claripy.BVV(0, 8 * 8), endness='Iend_LE') # zero-fill first just in case state.memory.store(msize, malloc_var, endness='Iend_LE') # Set fill sizes fill_size_var = proj.loader.main_object.get_symbol('fill_sizes') var_dict['fill_size_addrs'] = [ fill_size_var.rebased_addr + i for i in range(0, fill_size_var.size, 8) ] var_dict['fill_size_vars'] = [] if config['chunk_fill_size'] == 'zero': var_dict['fill_size_vars'] = [state.solver.BVV(0, 8 * 8)] * len( var_dict['fill_size_addrs']) if config['chunk_fill_size'] == 'header_size': var_dict['fill_size_vars'] = [header_size] * len( var_dict['fill_size_addrs']) if config['chunk_fill_size'] == 'chunk_size': var_dict['fill_size_vars'] = var_dict['malloc_size_bvs'] if type(config['chunk_fill_size']) in (int, int): var_dict['fill_size_vars'] = [ claripy.BVV(config['chunk_fill_size'], 8 * 8) ] * len(var_dict['fill_size_addrs']) for fsize, fill_var in zip(var_dict['fill_size_addrs'], var_dict['fill_size_vars']): state.memory.store(fsize, claripy.BVV(0, 8 * 8), endness='Iend_LE') # zero-fill first just in case state.memory.store(fsize, fill_var, endness='Iend_LE') # Set overflow sizes overflow_size_var = proj.loader.main_object.get_symbol('overflow_sizes') overflow_size_offset = overflow_size_var.rebased_addr var_dict['overflow_sizes_addrs'] = [ overflow_size_offset + i for i in range(0, overflow_size_var.size, 8) ] if max(config['overflow_sizes']) != 0: bvs_size = int(math.ceil(math.log(max(config['overflow_sizes']), 2))) + 1 else: bvs_size = 8 num_bytes = int(math.ceil(bvs_size / float(state.arch.byte_width))) bit_diff = num_bytes * state.arch.byte_width - bvs_size var_dict['overflow_sizes'] = [] for overflow_size_addr in var_dict['overflow_sizes_addrs']: if len(config['overflow_sizes']) > 1: overflow_var = state.solver.BVS('overflow_size', bvs_size).zero_extend(bit_diff) constraint = claripy.Or( overflow_var == config['overflow_sizes'][0]) for bin_size in config['overflow_sizes'][1:]: constraint = claripy.Or(overflow_var == bin_size, constraint) state.add_constraints(constraint) else: overflow_var = state.solver.BVV(config['overflow_sizes'][0], state.arch.bits) var_dict['overflow_sizes'].append(overflow_var) state.memory.store(overflow_size_addr, overflow_var, endness='Iend_LE') # Get arb_write_offsets var_dict['arb_offset_vars'] = [] arb_write_var = proj.loader.main_object.get_symbol('arw_offsets') for i in range(0, arb_write_var.size, 8): var_dict['arb_offset_vars'].append(arb_write_var.rebased_addr + i) # Get bf_offsets var_dict['bf_offset_vars'] = [] bf_var = proj.loader.main_object.get_symbol('bf_offsets') for i in range(0, bf_var.size, 8): var_dict['bf_offset_vars'].append(bf_var.rebased_addr + i) # get winning addr var_dict['winning_addr'] = proj.loader.main_object.get_symbol( 'winning').rebased_addr return var_dict
def _ail_handle_Const(self, expr): return RichR(claripy.BVV(expr.value, expr.size * self.state.arch.byte_width), typevar=typeconsts.int_type(expr.size * self.state.arch.byte_width))