def _prepare_chall_resp(self, state): # now we need to find the challenge response stuff # first break constraints at And's constraints = [] for c in state.se.constraints: if c.op == "And": constraints.extend(c.args) else: constraints.append(c) # filter for possible flag constraints filtered_constraints = [] for c in constraints: if any( v.startswith("cgc-flag") or v.startswith("random") for v in c.variables): filtered_constraints.append(c) self.filter_uncontrolled_constraints(state) # now separate into constraints we can probably control, and those we can't controllable_constraints = [] uncontrollable_constraints = [] if not state.has_plugin("chall_resp_info"): # register a blank one state.register_plugin("chall_resp_info", ChallRespInfo()) chall_resp_info = state.get_plugin("chall_resp_info") for c in filtered_constraints: if any(v.startswith("file_/dev/stdin") for v in c.variables) or \ any(v in chall_resp_info.vars_we_added for v in c.variables): controllable_constraints.append(c) elif any(v.startswith("output_var") for v in c.variables): # an output like a leak pass else: # uncontrollable constraints will show up as zen constraints etc uncontrollable_constraints.append(c) if len(controllable_constraints) > 0: l.warning("challenge response detected!") file_1 = state.posix.get_file(1) stdout = file_1.content.load(0, file_1.pos) stdout_len = state.se.eval(file_1.pos) stdout_bvs = [ claripy.BVS("file_stdout_0_%#x" % i, 8, explicit_name=True) for i in range(stdout_len) ] stdout_bv = claripy.Concat(*stdout_bvs) state.add_constraints(stdout == stdout_bv) # we call simplify to separate the contraints/dependencies state.se.simplify() merged_solver = state.se._solver._merged_solver_for( lst=[self._mem] + controllable_constraints) # todo here we can verify that there are actually stdout bytes here, otherwise we have little hope # add the important stdout vars to mem needed_vars = [] for bv in stdout_bvs: if len(bv.variables & merged_solver.variables) != 0: needed_vars.append(bv) # add the str_to_int vars and int_to_str vars for _, v in chall_resp_info.str_to_int_pairs: needed_vars.append(v) for v, _ in chall_resp_info.int_to_str_pairs: needed_vars.append(v) self._mem = claripy.Concat(self._mem, *needed_vars)
def _create_solvers(self, ft, extra_vars_to_solve=None): split_solvers = ft.split() if extra_vars_to_solve is None: extra_vars_to_solve = [] # make sure there is a chall_resp_info plugin if not self.crash.state.has_plugin("chall_resp_info"): # register a blank one self.crash.state.register_plugin("chall_resp_info", ChallRespInfo()) # figure out start indices for all solvers stdin_solver = [] for solver in split_solvers: stdin_indices = self._get_stdin_start_indices(solver) for idx, min_stdout_needed in stdin_indices: stdin_solver.append((idx, min_stdout_needed, solver)) # get an extra solver for the extra_vars_to_solve merged_extra_solver = None if len(extra_vars_to_solve) > 0: extra_vars_to_solve = set(extra_vars_to_solve) important_solvers = [ x for x in split_solvers if len(x.variables & extra_vars_to_solve) > 0 ] if len(important_solvers) > 0: merged_extra_solver = important_solvers[0] for s in important_solvers[1:]: merged_extra_solver = merged_extra_solver.combine(s) # sort them stdin_solver = sorted(stdin_solver, key=lambda x: x[0]) # get int nums self._stdin_int_infos = {} self._stdout_int_infos = {} for solver in split_solvers: for info in self._get_stdin_int_infos(solver): self._stdin_int_infos[info.var_name] = info for info in self._get_stdout_int_infos(solver): self._stdout_int_infos[info.var_name] = info # sort them self._sorted_stdin_int_infos = sorted(self._stdin_int_infos.values(), key=lambda x: x.start) self._sorted_stdout_int_infos = sorted(self._stdout_int_infos.values(), key=lambda x: x.start) # FIXME FLAG THIS WILL NEED TO BE CHANGED if extra_vars_to_solve is not None and len(extra_vars_to_solve) > 0: stdin_solver.append((self.crash.state.se.eval( self.crash.state.posix.get_file(0).pos), self.crash.state.se.eval( self.crash.state.posix.get_file(1).pos), merged_extra_solver)) l.debug("There are %d solvers after splitting", len(stdin_solver)) self._solver_code = "" for i, (min_stdin, min_stdout_needed, solver) in enumerate(stdin_solver): formula = CGCFormula(solver) self._formulas.append(formula) btor_name = "btor_%d" % i formula.name = btor_name solver_code = "" # possibly send more solver_code += self._create_send_stdin(min_stdin, min_stdout_needed) # we need to read until we get the bytes solver_code += self._create_read_bytes(min_stdout_needed) # now we have all the bytes we needed # parse the formula solver_code += self._create_boolector_parse(btor_name, formula) # constrain any "input" variables (regval, addr) solver_code += self._create_constrain_vals(solver, btor_name, formula) # add constraints to any stdin we've already sent solver_code += self._create_constrain_stdin( solver, btor_name, formula) # add constraints to stdout for the bytes we got solver_code += self._create_constrain_stdout( solver, btor_name, formula) # add constraints to any integers we have already used solver_code += self._create_constrain_integers( solver, btor_name, formula) # now create the byte setters solver_code += self._create_byte_setters(solver, btor_name, formula) self._solver_code += solver_code + "\n" # we are done l.debug("done creating solvers")