def _pre_analysis(self): # Call _initialize_cfg() before self.functions is used. self._initialize_cfg() # Initialize variables used during analysis self._pending_jobs = PendingJobs(self.functions, self._deregister_analysis_job) self._traced_addresses = set() self._changed_functions = set() self._updated_nonreturning_functions = set() self._nodes = {} self._nodes_by_addr = defaultdict(list) self._function_returns = defaultdict(set) entry = self.project.entry # type:SootAddressDescriptor entry_func = entry.method obj = self.project.loader.main_object if entry_func is not None: method_inst = obj.get_soot_method( entry_func.name, class_name=entry_func.class_name, params=entry_func.params) else: l.warning('The entry method is unknown. Try to find a main method.') method_inst = next(obj.main_methods, None) if method_inst is not None: entry_func = SootMethodDescriptor(method_inst.class_name, method_inst.name, method_inst.params) else: l.warning('Cannot find any main methods. Start from the first method of the first class.') for cls in obj.classes.values(): method_inst = next(iter(cls.methods), None) if method_inst is not None: break if method_inst is not None: entry_func = SootMethodDescriptor(method_inst.class_name, method_inst.name, method_inst.params) else: raise AngrCFGError('There is no method in the Jar file.') # project.entry is a method # we should get the first block if method_inst.blocks: block_idx = method_inst.blocks[0].idx self._insert_job(CFGJob(SootAddressDescriptor(entry_func, block_idx, 0), entry_func, 'Ijk_Boring')) total_methods = 0 # add all other methods as well for cls in self.project.loader.main_object.classes.values(): for method in cls.methods: total_methods += 1 if method.blocks: method_des = SootMethodDescriptor(cls.name, method.name, method.params) # TODO shouldn't this be idx? block_idx = method.blocks[0].label self._insert_job(CFGJob(SootAddressDescriptor(method_des, block_idx, 0), method_des, 'Ijk_Boring')) self._total_methods = total_methods
def _soot_create_invoke_successors(self, stmt, addr, invoke_expr): method_class = invoke_expr.class_name method_name = invoke_expr.method_name method_params = invoke_expr.method_params method_desc = SootMethodDescriptor(method_class, method_name, method_params) callee_soot_method = self.project.loader.main_object.get_soot_method( method_desc, none_if_missing=True) caller_soot_method = self.project.loader.main_object.get_soot_method( addr.method) if callee_soot_method is None: # this means the called method is external return [(stmt.label, addr, SootAddressDescriptor(method_desc, 0, 0), 'Ijk_Call')] targets = self._soot_class_hierarchy.resolve_invoke( invoke_expr, callee_soot_method, caller_soot_method) successors = [] for target in targets: target_desc = SootMethodDescriptor(target.class_name, target.name, target.params) successors.append( (stmt.label, addr, SootAddressDescriptor(target_desc, 0, 0), 'Ijk_Call')) return successors
def get_entry_state_of_method(project, method_fullname): # get SootAddressDescriptor of method entry soot_method = project.loader.main_object.get_soot_method(method_fullname) method = SootMethodDescriptor.from_soot_method(soot_method) addr = SootAddressDescriptor(method, 0, 0) # create call state return project.factory.blank_state(addr=addr)
def test_java_simple4(): binary_path = os.path.join(self_dir, "simple4.jar") proj = angr.Project(binary_path) print proj.loader.main_object._classes['simple4.Class1'] simgr = proj.factory.simgr() main_method = next(proj.loader.main_object.main_methods) simgr.active[0].ip = SootAddressDescriptor( SootMethodDescriptor.from_method(main_method), 0, 0) simgr.explore() paths = simgr.deadended assert len(paths) == 2 winnning_paths = [] for pp in paths: pp.state.posix.set_pos(0, 0) pp.state.posix.set_pos(1, 0) oo = pp.state.posix.read_from(1, 1) # a winning path is printing 'W' pp.state.add_constraints(oo == pp.state.se.BVV(ord('W'), 8)) if pp.satisfiable(): winnning_paths.append(pp) assert len(winnning_paths) == 1 winning_path = winnning_paths[0] # on the winning path, we ask for the input ii = winning_path.state.posix.read_from(0, 1) solution = chr(winning_path.state.se.eval(ii)) print repr(solution) assert solution == 'F'
def _invoke(self, method_id, obj=None, dynamic_dispatch=True, args_in_array=None): # get invoke target class_name = obj.type if dynamic_dispatch else method_id.class_name invoke_target = resolve_method(self.state, method_id.name, class_name, method_id.params) invoke_addr = SootAddressDescriptor(invoke_target, 0, 0) # get args no_of_args = len(invoke_target.params) if args_in_array is not None: arg_values = self._get_arg_values_from_array( args_in_array, no_of_args) else: arg_values = self._get_arg_values(no_of_args) # setup java args java_args = self._setup_java_args(arg_values, invoke_target, this_ref=obj) # call java method # => after returning, the execution will be continued in _return_result_of_computation self.call(invoke_addr, java_args, "return_from_invocation", cc=SimCCSoot(ArchSoot()))
def get_entry_state_of_method(project, method_fullname): # get SootAddressDescriptor of method entry soot_method = project.loader.main_object.get_soot_method(method_fullname) method = SootMethodDescriptor.from_soot_method(soot_method) addr = SootAddressDescriptor(method, 0, 0) # create call state return project.factory.blank_state(addr=addr, add_options={angr.options.ZERO_FILL_UNCONSTRAINED_MEMORY})
def _get_next_linear_instruction(state, stmt_idx): addr = state.addr.copy() addr.stmt_idx = stmt_idx method = state.regs._ip_binary.get_soot_method(addr.method) current_bb = method.blocks[addr.block_idx] new_stmt_idx = addr.stmt_idx + 1 if new_stmt_idx < len(current_bb.statements): return SootAddressDescriptor(addr.method, addr.block_idx, new_stmt_idx) else: new_bb_idx = addr.block_idx + 1 if new_bb_idx < len(method.blocks): return SootAddressDescriptor(addr.method, new_bb_idx, 0) else: l.warning("falling into a non existing bb: %d in %s", new_bb_idx, SootMethodDescriptor.from_soot_method(method)) raise IncorrectLocationException()
def init_class(self, class_, step_func=None): """ This method simulates the loading of a class by the JVM, during which parts of the class (e.g. static fields) are initialized. For this, we run the class initializer method <clinit> (if available) and update the state accordingly. Note: Initialization is skipped, if the class has already been initialized (or if it's not loaded in CLE). """ if self.is_class_initialized(class_): l.debug("Class %r already initialized.", class_) return l.debug("Initialize class %r.", class_) self.initialized_classes.add(class_) if not class_.is_loaded: l.warning("Class %r is not loaded in CLE. Skip initializiation.", class_) return clinit_method = resolve_method(self.state, '<clinit>', class_.name, include_superclasses=False, init_class=False) if clinit_method.is_loaded: engine = UberEngine(self.state.project) # use a fresh engine, as the default engine instance may be in use at this time javavm_simos = self.state.project.simos clinit_state = javavm_simos.state_call( addr=SootAddressDescriptor(clinit_method, 0, 0), base_state=self.state, ret_addr=SootAddressTerminator()) simgr = self.state.project.factory.simgr(clinit_state) l.info(">" * 15 + " Run class initializer %r ... " + ">" * 15, clinit_method) simgr.run(step_func=step_func, engine=engine) l.debug( "<" * 15 + " Run class initializer %r ... done " + "<" * 15, clinit_method) # The only thing that can be updated during initialization are # static or rather global information, which are either stored on # the heap or in the vm_static_table self.state.memory.vm_static_table = simgr.deadended[ -1].memory.vm_static_table.copy() self.state.memory.heap = simgr.deadended[-1].memory.heap.copy() else: l.debug( "Class initializer <clinit> is not loaded in CLE. Skip initializiation." )
def _get_bb_addr_from_instr(self, instr): """ Returns the address of the methods basic block that contains the given instruction. :param instr: The index of the instruction (within the current method). :rtype: SootAddressDescriptor """ current_method = self.state.addr.method try: bb = current_method.block_by_label[instr] except KeyError: l.error("Possible jump to a non-existing bb %s --> %d", self.state.addr, instr) raise IncorrectLocationException() return SootAddressDescriptor(current_method, bb.idx, 0)
def __init__(self, path, additional_jars=None, additional_jar_roots=None, main_class=None, **kwargs): if not pysoot: raise ImportError( 'Cannot import PySoot. The Soot backend requires PySoot to function. ' 'Please install PySoot first.') if kwargs.get('has_memory', False): raise CLEError( 'The parameter "has_memory" must be False for Soot backend.') super(Soot, self).__init__(path, has_memory=False, **kwargs) if not main_class: # parse main_class from the manifest self.manifest = self.get_manifest() main_class = self.manifest.get('Main-Class', None) # load the classes pysoot_lifter = Lifter( path, additional_jars=additional_jars, additional_jar_roots=additional_jar_roots, # main_class=main_class, ) self._classes = pysoot_lifter.classes # find entry method try: main_method_descriptor = SootMethodDescriptor.from_method( next(self.get_method("main", main_class))) entry = SootAddressDescriptor(main_method_descriptor, 0, 0) except CLEError: _l.warning( 'Failed to identify the entry (the Main method) of this JAR.') entry = None self._entry = entry self.os = 'javavm' self.rebase_addr = None self.set_arch(archinfo.arch_from_id('soot'))
def test_java_simple3(): binary_path = os.path.join(self_dir, "simple3.jar") proj = angr.Project(binary_path) print proj.loader.main_object._classes['simple3.Class1'] simgr = proj.factory.simgr() main_method = next(proj.loader.main_object.main_methods) simgr.active[0].ip = SootAddressDescriptor( SootMethodDescriptor.from_method(main_method), 0, 0) simgr.explore() pp = simgr.deadended[0] pp.state.posix.set_pos(0, 0) pp.state.posix.set_pos(1, 0) ii = pp.state.posix.read_from(0, 1) oo = pp.state.posix.read_from(1, 1) pp.state.add_constraints(oo == pp.state.se.BVV(ord('c'), 8)) print ii, "-->", oo cinput = chr(pp.state.se.eval(ii)) print repr(cinput) assert cinput == "b"
def __init__(self, path, entry_point=None, entry_point_params=(), input_format=None, additional_jars=None, additional_jar_roots=None, jni_libs_ld_path=None, jni_libs=None, android_sdk=None, **kwargs): if not pysoot: raise ImportError( 'Cannot import PySoot. The Soot backend requires PySoot.') if kwargs.get('has_memory', False): raise CLEError( 'The parameter "has_memory" must be False for Soot backend.') super(Soot, self).__init__(path, has_memory=False, **kwargs) # load the classes l.debug("Lifting to Soot IR ...") start_time = time.time() pysoot_lifter = Lifter(path, input_format=input_format, android_sdk=android_sdk, additional_jars=additional_jars, additional_jar_roots=additional_jar_roots) end_time = time.time() l.debug("Lifting completed in %ds", round(end_time - start_time, 2)) self._classes = pysoot_lifter.classes # find entry method if entry_point: try: ep_method = self.get_soot_method(entry_point, params=entry_point_params) ep_method_descriptor = SootMethodDescriptor.from_soot_method( ep_method) self._entry = SootAddressDescriptor(ep_method_descriptor, 0, 0) l.debug("Entry point set to %s", self._entry) except CLEError: l.warning("Couldn't find entry point %s.", entry_point) self._entry = None self.os = 'javavm' self.rebase_addr = None self.set_arch(ArchSoot()) if jni_libs: # native libraries are getting loaded by adding them as a dependency of this object self.deps += [jni_libs] if type(jni_libs) in (str, bytes) else jni_libs # if available, add additional load path(s) if jni_libs_ld_path: path_list = [jni_libs_ld_path] if type(jni_libs_ld_path) in ( str, bytes) else jni_libs_ld_path self.extra_load_path += path_list self.jni_support = True else: self.jni_support = False
def _soot_get_successors(self, addr, function_id, block, cfg_node): # soot method method = self.project.loader.main_object.get_soot_method(function_id) block_id = block.idx if addr.stmt_idx is None: addr = SootAddressDescriptor(addr.method, block_id, 0) successors = [ ] has_default_exit = True next_stmt_id = block.label + len(block.statements) last_stmt_id = method.blocks[-1].label + len(method.blocks[-1].statements) - 1 if next_stmt_id >= last_stmt_id: # there should not be a default exit going to the next block has_default_exit = False # scan through block statements, looking for those that generate new exits for stmt in block.statements[addr.stmt_idx - block.label : ]: if isinstance(stmt, IfStmt): succ = (stmt.label, addr, SootAddressDescriptor(function_id, method.block_by_label[stmt.target].idx, stmt.target), 'Ijk_Boring' ) successors.append(succ) elif isinstance(stmt, InvokeStmt): invoke_expr = stmt.invoke_expr succs = self._soot_create_invoke_successors(stmt, addr, invoke_expr) if succs: successors.extend(succs) has_default_exit = False break elif isinstance(stmt, GotoStmt): target = stmt.target succ = (stmt.label, addr, SootAddressDescriptor(function_id, method.block_by_label[target].idx, target), 'Ijk_Boring') successors.append(succ) # blocks ending with a GoTo should not have a default exit has_default_exit = False break elif isinstance(stmt, AssignStmt): expr = stmt.right_op if isinstance(expr, SootInvokeExpr): succs = self._soot_create_invoke_successors(stmt, addr, expr) if succs: successors.extend(succs) has_default_exit = False break if has_default_exit: successors.append(('default', addr, SootAddressDescriptor(function_id, method.block_by_label[next_stmt_id].idx, next_stmt_id), 'Ijk_Boring' ) ) return successors
def _handle_soot_stmt(self, state, successors, stmt_idx, stmt): # execute statement try: l.debug("Executing statement: %s", stmt) s_stmt = translate_stmt(stmt, state) except SimEngineError as e: l.error("Skipping statement: %s", e) return False # add invoke exit if s_stmt.has_invoke_target: invoke_state = state.copy() # parse invoke expression invoke_expr = s_stmt.invoke_expr method = invoke_expr.method args = invoke_expr.args ret_var = invoke_expr.ret_var if hasattr(invoke_expr, 'ret_var') else None # setup callsite ret_addr = self._get_next_linear_instruction(state, stmt_idx) if 'NATIVE' in method.attrs: # the target of the call is a native function # => we need to setup a native call-site l.debug("Native invoke: %r", method) addr = self.project.simos.get_addr_of_native_method(method) if not addr: # native function could not be found # => skip invocation and continue execution linearly return False invoke_state = self._setup_native_callsite( invoke_state, addr, method, args, ret_addr, ret_var) else: l.debug("Invoke: %r", method) self.setup_callsite(invoke_state, args, ret_addr, ret_var) addr = SootAddressDescriptor(method, 0, 0) # add invoke state as the successor and terminate execution # prematurely, since Soot does not guarantee that an invoke stmt # terminates a block successors.add_successor(invoke_state, addr, state.solver.true, 'Ijk_Call') return True # add jmp exit elif s_stmt.has_jump_targets: for target, condition in s_stmt.jmp_targets_with_conditions: if not target: target = self._get_next_linear_instruction(state, stmt_idx) l.debug("Possible jump: %s -> %s", state._ip, target) successors.add_successor(state.copy(), target, condition, 'Ijk_Boring') return True # add return exit elif isinstance(s_stmt, (SimSootStmt_Return, SimSootStmt_ReturnVoid)): l.debug("Return exit") self._add_return_exit(state, successors, s_stmt.return_value) return True # go on linearly else: return False