Exemple #1
0
    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
Exemple #2
0
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 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."
            )
Exemple #4
0
    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
Exemple #5
0
    def state_blank(self, addr=None, **kwargs): # pylint: disable=arguments-differ

        if not kwargs.get('mode', None): kwargs['mode'] = self.project._default_analysis_mode
        if not kwargs.get('arch', None):  kwargs['arch'] = self.arch
        if not kwargs.get('os_name', None): kwargs['os_name'] = self.name
        # enable support for string analysis
        if not kwargs.get('add_options', None): kwargs['add_options'] = []
        kwargs['add_options'] += [options.STRINGS_ANALYSIS, options.COMPOSITE_SOLVER]

        if self.is_javavm_with_jni_support:
            # If the JNI support is enabled (i.e. JNI libs are loaded), the SimState
            # needs to support both the Vex and the Soot engine. Therefore we start with
            # an initialized native state and extend this with the Soot initializations.
            # Note: Setting `addr` to a `native address` (i.e. not an SootAddressDescriptor).
            #       makes sure that the SimState is not in "Soot-mode".
            # TODO: use state_blank function from the native simos and not the super class
            state = super(SimJavaVM, self).state_blank(addr=0, **kwargs)
            native_addr_size = self.native_simos.arch.bits
            # Let the JNIEnv pointer point to the function table
            state.memory.store(addr=self.jni_env,
                               data=BVV(self.jni_function_table, native_addr_size),
                               endness=self.native_arch.memory_endness)
            # Initialize the function table
            # => Each entry usually contains the address of the function, but since we hook all functions
            #    with SimProcedures, we store the address of the corresponding hook instead.
            #    This, by construction, is exactly the address of the function table entry itself.
            for idx in range(len(jni_functions)):
                jni_function_addr = self.jni_function_table + idx * native_addr_size//8
                state.memory.store(addr=jni_function_addr,
                                   data=BVV(jni_function_addr, native_addr_size),
                                   endness=self.native_arch.memory_endness)

        else:
            # w/o JNI support, we can just use a blank state
            state = SimState(project=self.project, **kwargs)

        if not self.project.entry and not addr:
            raise ValueError("Failed to init blank state. Project entry is not set/invalid"
                             "and no address was provided.")

        # init state register
        state.regs._ip = addr if addr else self.project.entry
        state.regs._ip_binary = self.project.loader.main_object
        state.regs._invoke_return_target = None
        state.regs._invoke_return_variable = None

        # add empty stack frame
        state.memory.push_stack_frame()

        # create bottom of callstack
        new_frame = state.callstack.copy()
        new_frame.ret_addr = SootAddressTerminator()
        state.callstack.push(new_frame)

        # initialize class containing the current method
        state.javavm_classloader.get_class(state.addr.method.class_name,
                                           init_class=True, step_func=kwargs.get('step_function', None))

        # initialize the Java environment
        # TODO move this to `state_full_init?
        self.init_static_field(state, "java.lang.System", "in", "java.io.InputStream")
        self.init_static_field(state, "java.lang.System", "out", "java.io.PrintStream")

        return state
Exemple #6
0
    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)
Exemple #7
0
 def _execute(self):
     # TODO: implement simprocedure to throw exception
     self._add_jmp_target(target=SootAddressTerminator(),
                          condition=self.state.solver.true)
Exemple #8
0
    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)