def test_apk_loading(): 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 loading_opts = {'android_sdk': sdk_path, 'entry_point': 'com.example.antoniob.android1.MainActivity.onCreate', 'entry_point_params': ('android.os.Bundle', )} project = angr.Project(os.path.join(test_location, "android1.apk"), main_opts=loading_opts) blank_state = project.factory.blank_state() a1 = SimSootValue_ThisRef.new_object(blank_state, 'com.example.antoniob.android1.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.step() simgr.step() assert simgr.active[0].addr.block_idx == 0 assert simgr.active[0].addr.stmt_idx == 3 simgr.run() assert len(simgr.deadended) == 1 assert type(simgr.deadended[0].addr) is SootAddressTerminator
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 new_object(cls, state, type_, symbolic=False, init_object=False, init_class=False): """ Creates a new object reference. :param state: State associated to the object. :param type_: Class of the object. :param init_object: Whether the objects initializer method should be run. :param init_class: Whether the class initializer method should be run. :return: Reference to the new object. """ # create reference obj_ref = cls(heap_alloc_id=state.memory.get_new_uuid(), type_=type_, symbolic=symbolic) # run initializer if init_object: l.info(">" * 15 + " Initialize object %r ... " + ">" * 15, obj_ref) # find initializer method # TODO: add support for non-default initializing methods init_method = resolve_method(state, '<init>', type_, init_class=init_class).address() # setup init state args = [SootArgument(obj_ref, obj_ref.type, is_this_ref=True)] init_state = state.project.simos.state_call(init_method, *args, base_state=state, ret_addr=SootAddressTerminator()) # run init state simgr = state.project.factory.simgr(init_state) simgr.run() # copy results from initialization to the state state.memory.vm_static_table = simgr.deadended[0].memory.vm_static_table.copy() state.memory.heap = simgr.deadended[0].memory.heap.copy() l.debug("<" * 15 + " Initialize object %r ... done " + "<" * 15, obj_ref) return obj_ref
def get_new_primitive_arg(value, type_): """ Wraps a primitive value so it so it can be used as a parameter for a method :param value: the value of the primitiva value. :type BV :param value: the type of the primitive value (int, boolean, etc.) :type str :return SootArgument """ return SootArgument(value, type_)
def get_new_object_arg(obj_ref, is_this_ref=False): """ Wraps an object reference so it can be passed as a method parameter :param obj_ref: the reference to the object we want to pass as parameter :type SimSootValue_ThisRef :param is_this_ref: indicates if the object reference is a 'this' reference or not :type bool :return SootArgument """ return SootArgument(obj_ref, obj_ref.type, is_this_ref)
def _setup_native_callsite(cls, state, native_addr, java_method, args, ret_addr, ret_var): # Step 1: setup java callsite, but w/o storing arguments in memory cls.setup_callsite(state, None, ret_addr, ret_var) # Step 2: add JNI specific arguments to *args list # get JNI environment pointer jni_env = SootArgument(state.project.simos.jni_env, "JNIEnv") # get reference to the current object or class if args and args[0].is_this_ref: # instance method call # => pass 'this' reference to native code ref = args.pop(0) else: # static method call # => pass 'class' reference to native code class_ = state.javavm_classloader.get_class(java_method.class_name, init_class=True) ref = SootArgument(class_, "Class") # add to args final_args = [jni_env, ref] + args # Step 3: generate C prototype from java_method voidp = parse_type('void*') arg_types = [voidp, voidp] + [ state.project.simos.get_native_type(ty) for ty in java_method.params ] ret_type = state.project.simos.get_native_type(java_method.ret) prototype = SimTypeFunction(args=arg_types, returnty=ret_type) # Step 3: create native invoke state return state.project.simos.state_call(native_addr, *final_args, base_state=state, prototype=prototype, ret_type=java_method.ret)
def test_object_tracking(): binary_dir = os.path.join(test_location, "object_tracking") project = create_project(binary_dir, load_native_libs=False) bootstrap_state = project.factory.blank_state(addr=SootAddressTerminator()) mylib_object = SimSootValue_ThisRef.new_object(bootstrap_state, "MyLib", symbolic=True, init_object=False) soot_method = resolve_method( bootstrap_state, 'testGetterAndSetterConcrete', "MixedJava", ("mylib.MyLib",), init_class=False).address() call_state = project.factory.call_state( soot_method, SootArgument(mylib_object, mylib_object.type, is_this_ref=False), base_state=bootstrap_state, ret_addr=SootAddressTerminator()) call_state.options.add(angr.options.JAVA_IDENTIFY_GETTER_SETTER) call_state.options.add(angr.options.JAVA_TRACK_ATTRIBUTES) simgr = project.factory.simgr(call_state) simgr.run() assert len(simgr.deadended) == 1 final_state = simgr.deadended[0] assert final_state.solver.eval(mylib_object.get_field(final_state, 'myInt', 'int')) == 1 assert final_state.solver.eval(mylib_object.get_field(final_state, 'myShort', 'short')) == 1 assert final_state.solver.eval(mylib_object.get_field(final_state, 'myChar', 'char')) == ord('c') assert final_state.solver.eval(mylib_object.get_field(final_state, 'myLong', 'long')) == 2 assert final_state.solver.eval(mylib_object.get_field(final_state, 'myFloat', 'float')) == 1.5 assert final_state.solver.eval(mylib_object.get_field(final_state, 'myDouble', 'double')) == 1.5 string_ref = mylib_object.get_field(final_state, 'myString', 'java.lang.String') assert final_state.solver.eval(final_state.memory.load(string_ref)) == 'Hello!' array_ref = mylib_object.get_field(final_state, 'myArray', 'int[]') assert final_state.solver.eval(array_ref.size) == 3 object_ref = mylib_object.get_field(final_state, 'myObject', 'java.lang.Object') assert final_state.solver.eval(object_ref.get_field(final_state, 'a', 'int')) == 1 assert ('myInt', 'int') in mylib_object.attributes assert ('myChar', 'char') in mylib_object.attributes assert ('myShort', 'short') in mylib_object.attributes assert ('myLong', 'long') in mylib_object.attributes assert ('myFloat', 'float') in mylib_object.attributes assert ('myDouble', 'double') in mylib_object.attributes assert ('myString', 'java.lang.String') in mylib_object.attributes assert ('myArray', 'int[]') in mylib_object.attributes assert ('myObject', 'java.lang.Object') in mylib_object.attributes
def state_entry(self, *args, **kwargs): # pylint: disable=arguments-differ """ Create an entry state. :param args: List of SootArgument values (optional). """ state = self.state_blank(**kwargs) # for the Java main method `public static main(String[] args)`, # we add symbolic cmdline arguments if not args and state.addr.method.name == 'main' and \ state.addr.method.params[0] == 'java.lang.String[]': cmd_line_args = SimSootExpr_NewArray.new_array(state, "java.lang.String", BVS('argc', 32)) cmd_line_args.add_default_value_generator(self.generate_symbolic_cmd_line_arg) args = [SootArgument(cmd_line_args, "java.lang.String[]")] # for referencing the Java array, we need to know the array reference # => saves it in the globals dict state.globals['cmd_line_args'] = cmd_line_args # setup arguments SimEngineSoot.setup_arguments(state, args) return state
def state_call(self, addr, *args, **kwargs): """ Create a native or a Java call state. :param addr: Soot or native addr of the invoke target. :param args: List of SootArgument values. """ state = kwargs.pop('base_state', None) # check if we need to setup a native or a java callsite if isinstance(addr, SootAddressDescriptor): # JAVA CALLSITE # ret addr precedence: ret_addr kwarg > base_state.addr > terminator ret_addr = kwargs.pop('ret_addr', state.addr if state else SootAddressTerminator()) cc = kwargs.pop('cc', SimCCSoot(self.arch)) if state is None: state = self.state_blank(addr=addr, **kwargs) else: state = state.copy() state.regs.ip = addr cc.setup_callsite(state, ret_addr, args) return state else: # NATIVE CALLSITE # setup native argument values native_arg_values = [] for arg in args: if arg.type in ArchSoot.primitive_types or \ arg.type == "JNIEnv": # the value of primitive types and the JNIEnv pointer # are just getting copied into the native memory native_arg_value = arg.value if self.arch.bits == 32 and arg.type == "long": # On 32 bit architecture, long values (w/ 64 bit) are copied # as two 32 bit integer # TODO is this correct? upper = native_arg_value.get_bytes(0, 4) lower = native_arg_value.get_bytes(4, 4) idx = args.index(arg) args = args[:idx] \ + (SootArgument(upper, 'int'), SootArgument(lower, 'int')) \ + args[idx+1:] native_arg_values += [upper, lower] continue else: # argument has a relative type # => map Java reference to an opaque reference, which the native code # can use to access the Java object through the JNI interface native_arg_value = state.jni_references.create_new_reference(obj=arg.value) native_arg_values += [native_arg_value] # setup native return type ret_type = kwargs.pop('ret_type') native_ret_type = self.get_native_type(ret_type) # setup function prototype, so the SimCC know how to init the callsite arg_types = [self.get_native_type(arg.type) for arg in args] prototype = SimTypeFunction(args=arg_types, returnty=native_ret_type) native_cc = self.get_native_cc(func_ty=prototype) # setup native invoke state return self.native_simos.state_call(addr, *native_arg_values, base_state=state, ret_addr=self.native_return_hook_addr, cc=native_cc, **kwargs)
def state_call(self, addr, *args, **kwargs): """ Create a native or a Java call state. :param addr: Soot or native addr of the invoke target. :param args: List of SootArgument values. """ state = kwargs.pop('base_state', None) # check if we need to setup a native or a java callsite if isinstance(addr, SootAddressDescriptor): # JAVA CALLSITE # ret addr precedence: ret_addr kwarg > base_state.addr > terminator ret_addr = kwargs.pop( 'ret_addr', state.addr if state else SootAddressTerminator()) cc = kwargs.pop('cc', SimCCSoot(self.arch)) if state is None: state = self.state_blank(addr=addr, **kwargs) else: state = state.copy() state.regs.ip = addr cc.setup_callsite(state, ret_addr, args, kwargs.pop('prototype', None)) return state else: # NATIVE CALLSITE # setup native return type # TODO roll this into protytype ret_type = kwargs.pop('ret_type') native_ret_type = self.get_native_type(ret_type) # setup function prototype, so the SimCC know how to init the callsite prototype = kwargs.pop('prototype', None) if prototype is None: arg_types = [self.get_native_type(arg.type) for arg in args] prototype = SimTypeFunction(args=arg_types, returnty=native_ret_type) native_cc = kwargs.pop('cc', None) if native_cc is None: native_cc = self.get_native_cc() # setup native argument values native_arg_values = [] for arg, arg_ty in zip(args, prototype.args): if arg.type in ArchSoot.primitive_types or \ arg.type == "JNIEnv": # the value of primitive types and the JNIEnv pointer # are just getting copied into the native memory native_arg_value = arg.value if self.arch.bits == 32 and arg.type == "long": # On 32 bit architecture, long values (w/ 64 bit) are copied # as two 32 bit integer # TODO I _think_ all this logic can go away as long as the cc knows how to store large values # TODO this has been mostly implemented 11 Dec 2021 # unfortunately no test cases hit this branch so I don't wanna touch it :( upper = native_arg_value.get_bytes(0, 4) lower = native_arg_value.get_bytes(4, 4) idx = args.index(arg) args = args[:idx] \ + (SootArgument(upper, 'int'), SootArgument(lower, 'int')) \ + args[idx+1:] native_arg_values += [upper, lower] continue if type(arg.value) is BV and len(arg.value) > arg_ty.size: # hack??? all small primitives are passed around as 32bit but cc won't like that native_arg_value = native_arg_value[arg_ty.size - 1:0] else: # argument has a relative type # => map Java reference to an opaque reference, which the native code # can use to access the Java object through the JNI interface native_arg_value = state.jni_references.create_new_reference( obj=arg.value) native_arg_values += [native_arg_value] # setup native invoke state return self.native_simos.state_call( addr, *native_arg_values, base_state=state, ret_addr=self.native_return_hook_addr, cc=native_cc, prototype=prototype, **kwargs)