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 _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 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_androidnative1(): sdk_path = os.path.join(os.path.expanduser("~"), "Android/Sdk/platforms/") if not os.path.exists(sdk_path): print( "cannot run test_apk_loading since there is no Android SDK folder") return apk_location = os.path.join(file_dir, "androidnative1.apk") loading_opts = { 'android_sdk': sdk_path, 'entry_point': 'com.angr.nativetest1.MainActivity.onCreate', 'entry_point_params': ('android.os.Bundle', ), 'supported_jni_archs': ['x86'] } project = angr.Project(apk_location, main_opts=loading_opts) project.hook( SootMethodDescriptor(class_name="java.lang.String", name="valueOf", params=('int', )).address(), Dummy_String_valueOf()) blank_state = project.factory.blank_state() a1 = SimSootValue_ThisRef.new_object( blank_state, 'com.angr.androidnative1.MainActivity') a2 = SimSootValue_ThisRef.new_object(blank_state, 'android.os.Bundle', symbolic=True) args = [SootArgument(arg, arg.type) for arg in [a1, a2]] entry = project.factory.entry_state(args=args) simgr = project.factory.simgr(entry) simgr.run() int_result = simgr.deadended[0].solver.eval(result) assert int_result == 221
def _resolve_invoke_target(self, expr, state): # get the type of the base object base = translate_expr(self.expr.base, self.state).expr if base is not None: base_type = base.type else: # the base is not set, for example if we process an invocation of an # unloaded library function # => fallback: use the statically retrieved type base_type = self.expr.class_name # based on the class of the base object, we resolve the invoke target try: return resolve_method(state=self.state, method_name=self.expr.method_name, class_name=base_type, params=self.expr.method_params, ret_type=self.expr.type, raise_exception_if_not_found=True) except SootMethodNotLoadedException: # in case that the method is not loaded, continue with the infos # available from the invoke expression return SootMethodDescriptor(self.expr.class_name, self.expr.method_name, self.expr.method_params, ret_type=self.expr.type)
def resolve_method(state, method_name, class_name, params=(), ret_type=None, include_superclasses=True, init_class=True, raise_exception_if_not_found=False): """ Resolves the method based on the given characteristics (name, class and params) The method may be defined in one of the superclasses of the given class (TODO: support interfaces). :rtype: archinfo.arch_soot.SootMethodDescriptor """ base_class = state.javavm_classloader.get_class(class_name) if include_superclasses: class_hierarchy = state.javavm_classloader.get_class_hierarchy(base_class) else: class_hierarchy = [base_class] # walk up in class hierarchy, until method is found for class_descriptor in class_hierarchy: java_binary = state.project.loader.main_object soot_method = java_binary.get_soot_method(method_name, class_descriptor.name, params, none_if_missing=True) if soot_method is not None: # init the class if init_class: state.javavm_classloader.init_class(class_descriptor) return SootMethodDescriptor.from_soot_method(soot_method) # method could not be found # => we are executing code that is not loaded (typically library code) # => fallback: continue with infos available from the invocation, so we # still can use SimProcedures if raise_exception_if_not_found: raise SootMethodNotLoadedException() else: return SootMethodDescriptor(class_name, method_name, params, ret_type=ret_type)
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 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 resolve_method(state, method_name, class_name, params=(), ret_type=None, include_superclasses=True, init_class=True, raise_exception_if_not_found=False): """ Resolves the method based on the given characteristics (name, class and params) The method may be defined in one of the superclasses of the given class (TODO: support interfaces). :rtype: archinfo.arch_soot.SootMethodDescriptor """ base_class = state.javavm_classloader.get_class(class_name) if include_superclasses: class_hierarchy = state.javavm_classloader.get_class_hierarchy( base_class) else: class_hierarchy = [base_class] # walk up in class hierarchy, until method is found for class_descriptor in class_hierarchy: java_binary = state.project.loader.main_object soot_method = java_binary.get_soot_method(method_name, class_descriptor.name, params, none_if_missing=True) if soot_method is not None: # init the class if init_class: state.javavm_classloader.init_class(class_descriptor) return SootMethodDescriptor.from_soot_method(soot_method) # method could not be found # => we are executing code that is not loaded (typically library code) # => fallback: continue with infos available from the invocation, so we # still can use SimProcedures if raise_exception_if_not_found: raise SootMethodNotLoadedException() else: return SootMethodDescriptor(class_name, method_name, params, ret_type=ret_type)
def run(self, ptr_env, class_, ptr_method_name, ptr_method_sig): method_class = self.state.jni_references.lookup(class_) method_name = self._load_string_from_native_memory(ptr_method_name) method_sig = self._load_string_from_native_memory(ptr_method_sig) # derive parameter type from signature params, _ = ArchSoot.decode_method_signature(method_sig) # create method ID and return reference to it method_id = SootMethodDescriptor(method_class.name, method_name, params) return self.state.jni_references.create_new_reference(method_id)
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 setup_caller(self, caller): # returns true if the caller itself adds entropy params = tuple(caller[2]) method_name = caller[0] + '.' + caller[1] soot_method = self.angr_p.loader.main_object.get_soot_method( method_name, params=params) target_method = SootMethodDescriptor.from_soot_method(soot_method) base_state = self.angr_p.factory.blank_state() base_state.ip = SootAddressTerminator() args_target_method, caller_args = self._get_initialized_method_args( base_state, soot_method) return self.angr_p.factory.call_state( target_method.addr, *args_target_method, base_state=base_state), caller_args
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 test_fauxware(): # create project binary_path = os.path.join(test_location, "fauxware_java_jni", "fauxware.jar") jni_options = {'jni_libs': ['libfauxware.so']} project = angr.Project(binary_path, main_opts=jni_options) entry = project.factory.entry_state() simgr = project.factory.simgr(entry) # find path to `accepted()` method accepted_method = SootMethodDescriptor.from_string('Fauxware.accepted()').address() simgr.explore(find=lambda s: s.addr == accepted_method) state = simgr.found[0] # eval password cmd_line_args = project.simos.get_cmd_line_args(state) password = state.solver.eval(cmd_line_args[0]) assert password == "SOSNEAKY"
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 solve_given_numbers_angr(numbers): global fake_input_fd, fake_output_fd binary_path = os.path.join(self_dir, "bin/service.jar") jni_options = {'jni_libs': ['libnotfun.so']} project = angr.Project(binary_path, main_opts=jni_options) # hooks project.hook( SootMethodDescriptor(class_name="java.util.Random", name="nextInt", params=('int', )).address(), Random_nextInt()) project.hook( SootMethodDescriptor(class_name="java.lang.Integer", name="valueOf", params=('int', )).address(), Dummy_valueOf()) project.hook( SootMethodDescriptor(class_name="NotFun", name="print", params=('java.lang.Object', )).address(), Custom_Print()) project.hook( SootMethodDescriptor(class_name="NotFun", name="getInt", params=()).address(), Custom_getInt()) # set entry point to the 'game' method game_method = [ m for m in project.loader.main_object.classes['NotFun'].methods if m.name == "game" ][0] game_entry = SootMethodDescriptor.from_soot_method(game_method).address() entry = project.factory.blank_state(addr=game_entry) simgr = project.factory.simgr(entry) # Create a fake file with what it is going to be printed to the user (concrete) fake_output_fd = entry.posix.open(b"/fake/output", Flags.O_RDWR) ff = entry.posix.fd[fake_output_fd] tstr = b"".join([bytes(str(n), 'utf-8') + b"\n" for n in numbers]) ff.write_data(tstr, len(tstr)) ff.seek(0) # Create a fake file with what the user as to insert (symbolic) fake_input_fd = entry.posix.open(b"/fake/input", Flags.O_RDWR) ff = entry.posix.fd[fake_input_fd] solutions = [claripy.BVS("solution%d" % (i), 32) for i in range(3)] for s in solutions: ff.write_data(s, 4) ff.seek(0) print("=" * 10 + " SYMBOLIC EXECUTION STARTED") while (len(simgr.active) > 0): simgr.step() print("===== " + str(simgr)) print("===== " + ",".join([ str(a.addr) for a in simgr.active if type(a.addr) == SootAddressDescriptor ])) # If we reach block_idx 30, it means that we solved 1 round of the game --> we stash the state # If we reach the gameFail() method, it means that we failed --> we prune the state simgr.move( 'active', 'stashed', lambda a: type(a.addr) == SootAddressDescriptor and a.addr.method == SootMethodDescriptor("NotFun", "game", ()) and a.addr.block_idx == 30) simgr.move( 'active', 'pruned', lambda a: type(a.addr) == SootAddressDescriptor and a.addr.method == SootMethodDescriptor("NotFun", "gameFail", ())) print("=" * 10 + " SYMBOLIC EXECUTION ENDED") assert len(simgr.stashed) == 1 win_state = simgr.stashed[0] numeric_solutions = [] for s in solutions: es = win_state.solver.eval_atmost(s, 2) assert len(es) == 1 numeric_solutions.append(es[0]) return numeric_solutions