Пример #1
0
    def test_transitive_closure_of_a_node_on_a_graph_with_loops_should_still_terminate(
            self):
        dep_graph = DepGraph()

        # A -> B, B -> C, C -> D, D -> A
        A = _a_mock_definition()
        B = _a_mock_definition()
        C = _a_mock_definition()
        D = _a_mock_definition()
        uses = [
            (A, B),
            (B, C),
            (C, D),
            (D, A),
        ]

        for use in uses:
            dep_graph.add_edge(*use)

        result = dep_graph.transitive_closure(C)
        result_nodes = set(result.nodes)
        result_edges = set(result.edges)

        self.assertSetEqual(result_nodes, {A, B, C, D})
        self.assertSetEqual(result_edges, {(A, B), (B, C), (C, D), (D, A)})
Пример #2
0
    def test_delegate_nodes_to_the_underlying_graph_object(self):
        with mock.patch.object(networkx.DiGraph,
                               'nodes') as digraph_nodes_mock:
            dep_graph = DepGraph()
            dep_graph.nodes()

            digraph_nodes_mock.assert_called_once()
Пример #3
0
    def test_dep_graph(self):
        project = angr.Project(_binary_path('true'), auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        main = cfg.functions['main']

        # build a def-use graph for main() of /bin/true without tmps.
        # check that the only dependency of the first block's
        # guard is the four cc registers
        rda = project.analyses.ReachingDefinitions(subject=main,
                                                   track_tmps=False,
                                                   dep_graph=DepGraph())
        guard_use = list(
            filter(
                lambda def_: type(def_.atom) is GuardUse and def_.codeloc.
                block_addr == main.addr, rda.dep_graph._graph.nodes()))[0]
        preds = list(rda.dep_graph._graph.pred[guard_use])
        self.assertEqual(len(preds), 1)
        self.assertIsInstance(preds[0].atom, Register)
        self.assertEqual(
            preds[0].atom.reg_offset,
            project.arch.registers['rdi'][0],
        )

        # build a def-use graph for main() of /bin/true. check that t7 in the first block is only used by the guard
        rda = project.analyses.ReachingDefinitions(subject=main,
                                                   track_tmps=True,
                                                   dep_graph=DepGraph())
        tmp_7 = list(
            filter(
                lambda def_: type(def_.atom) is Tmp and def_.atom.tmp_idx == 7
                and def_.codeloc.block_addr == main.addr,
                rda.dep_graph._graph.nodes()))[0]
        self.assertEqual(len(rda.dep_graph._graph.succ[tmp_7]), 1)
        self.assertEqual(type(list(rda.dep_graph._graph.succ[tmp_7])[0].atom),
                         GuardUse)
Пример #4
0
def test_delegate_add_node_to_the_underlying_graph_object(
        digraph_add_node_mock):
    definition = _a_mock_definition()
    dep_graph = DepGraph()
    dep_graph.add_node(definition)

    digraph_add_node_mock.assert_called_once_with(definition)
Пример #5
0
def test_delegate_add_edge_to_the_underlying_graph_object(digraph_add_edge_mock):
    use = (_a_mock_definition(), _a_mock_definition())
    labels = { 'attribute1': 'value1', 'attribute2': 'value2' }

    dep_graph = DepGraph()
    dep_graph.add_edge(*use, **labels)

    digraph_add_edge_mock.assert_called_once_with(*use, **labels)
Пример #6
0
    def test_delegate_add_node_to_the_underlying_graph_object(self):
        with mock.patch.object(networkx.DiGraph,
                               'add_node') as digraph_add_node_mock:
            definition = _a_mock_definition()
            dep_graph = DepGraph()
            dep_graph.add_node(definition)

            digraph_add_node_mock.assert_called_once_with(definition)
Пример #7
0
    def test_delegate_predecessors_to_the_underlying_graph_object(self):
        with mock.patch.object(networkx.DiGraph,
                               'predecessors') as digraph_predecessors_mock:
            definition = _a_mock_definition()
            dep_graph = DepGraph()
            dep_graph.predecessors(definition)

            digraph_predecessors_mock.assert_called_once_with(definition)
Пример #8
0
    def test_delegate_add_edge_to_the_underlying_graph_object(self):
        with mock.patch.object(networkx.DiGraph,
                               'add_edge') as digraph_add_edge_mock:
            use = (_a_mock_definition(), _a_mock_definition())
            labels = {'attribute1': 'value1', 'attribute2': 'value2'}

            dep_graph = DepGraph()
            dep_graph.add_edge(*use, **labels)

            digraph_add_edge_mock.assert_called_once_with(*use, **labels)
Пример #9
0
def test_transitive_closure_of_a_node_should_copy_labels_from_original_graph():
    dep_graph = DepGraph()

    # A -> B
    A = _a_mock_definition()
    B = _a_mock_definition()
    uses = [(A, B)]

    for use in uses:
        dep_graph.add_edge(*use, label='some data')

    result = dep_graph.transitive_closure(B).get_edge_data(A, B)['label']

    nose.tools.assert_equals(result, 'some data')
Пример #10
0
    def test_add_dependencies_for_concrete_pointers_of_fails_if_the_given_definition_is_not_in_the_graph(
            self):
        dependency_graph = DepGraph()

        definition = Definition(Register(0, 4), CodeLocation(0x42, 0),
                                DataSet(UNDEFINED, 4))

        with self.assertRaises(AssertionError) as cm:
            dependency_graph.add_dependencies_for_concrete_pointers_of(
                definition, None, None)

        ex = cm.exception
        self.assertEqual(
            str(ex),
            'The given Definition must be present in the given graph.')
Пример #11
0
    def test_contains_atom_returns_false_if_the_dependency_graph_does_not_contain_a_definition_of_the_given_atom(
            self):
        dep_graph = DepGraph()

        # A -> B
        A = _a_mock_definition()
        B = _a_mock_definition()

        uses = [(A, B)]

        for use in uses:
            dep_graph.add_edge(*use)

        result = dep_graph.contains_atom(Register(8, 4))
        self.assertFalse(result)
Пример #12
0
    def test_contains_atom_returns_true_if_the_dependency_graph_contains_a_definition_of_the_given_atom(
            self):
        dep_graph = DepGraph()

        r0 = Register(8, 4)

        # A -> B
        A = _a_mock_definition(r0)
        B = _a_mock_definition()

        uses = [(A, B)]

        for use in uses:
            dep_graph.add_edge(*use)

        result = dep_graph.contains_atom(r0)
        self.assertTrue(result)
Пример #13
0
    def test_add_dependencies_for_concrete_pointers_of_adds_a_definition_for_data_pointed_to_by_given_definition(
            self):
        arch = self.ArchMock()
        loader = self.LoaderMock(self.MainObjectMock(self.SectionMock(True)))

        memory_datum = self.MemoryDataMock(self.memory_address,
                                           str.encode(self.string_in_memory),
                                           len(self.string_in_memory),
                                           'string')
        cfg = self.CFGMock({self.memory_address: memory_datum})

        register_definition = Definition(
            Register(0, 4),
            None,
        )

        dependency_graph = DepGraph()
        dependency_graph.add_node(register_definition)

        dependency_graph.add_dependencies_for_concrete_pointers_of(
            [claripy.BVV(self.memory_address, arch.bits)], register_definition,
            cfg, loader)

        memory_definition = Definition(
            MemoryLocation(self.memory_address, self.string_in_memory_length),
            ExternalCodeLocation(),
        )

        nodes = list(dependency_graph.nodes())
        predecessors = list(
            dependency_graph.graph.predecessors(register_definition))
        self.assertEqual(nodes, [register_definition, memory_definition])
        self.assertListEqual(predecessors, [memory_definition])
Пример #14
0
    def test_add_dependencies_for_concrete_pointers_of_create_memory_location_with_undefined_data_if_data_pointed_to_by_definition_is_not_known(
            self):
        arch = self.ArchMock()
        cfg = self.CFGMock({})
        loader = self.LoaderMock(self.MainObjectMock(self.SectionMock(True)))

        datum_content = None
        datum_size = 0x4242
        memory_datum = self.MemoryDataMock(self.memory_address, datum_content,
                                           datum_size, 'unknown')
        cfg = self.CFGMock({self.memory_address: memory_datum})

        memory_definition = Definition(
            MemoryLocation(self.memory_address, datum_size),
            ExternalCodeLocation(), DataSet(UNDEFINED, datum_size * 8))

        register_definition = Definition(
            Register(0, 4), CodeLocation(0x42, 0),
            DataSet(self.memory_address, arch.bits))

        dependency_graph = DepGraph()
        dependency_graph.add_node(register_definition)

        dependency_graph.add_dependencies_for_concrete_pointers_of(
            register_definition, cfg, loader)

        nodes = list(dependency_graph.nodes())
        predecessors = list(
            dependency_graph.graph.predecessors(register_definition))
        self.assertEqual(nodes, [register_definition, memory_definition])
        self.assertListEqual(predecessors, [memory_definition])
Пример #15
0
    def _dependencies(self, subject, sink_atoms: List[Tuple['Atom',SimType]], kb, project, max_depth: int,
                      excluded_funtions: Set[int]) -> Generator[Tuple[int,int,'ReachingDefinitionsAnalysis'], None, None]:
        Handler = handler_factory([
            StdioHandlers,
            StdlibHandlers,
            StringHandlers,
        ])

        if isinstance(subject, Function):
            sink = subject
        else:
            raise TypeError('Unsupported type of subject %s.' % type(subject))

        # peek into the callgraph and discover all functions reaching the sink within N layers of calls, which is determined
        # by the depth parameter
        queue: List[Tuple[CallTrace, int]] = [(CallTrace(sink.addr), 0)]
        starts: Set[CallTrace] = set()
        encountered: Set[int] = set(excluded_funtions)
        while queue:
            trace, curr_depth = queue.pop(0)
            if trace.current_function_address() in starts:
                continue
            caller_func_addr = trace.current_function_address()
            callers: Set[int] = set(kb.functions.callgraph.predecessors(caller_func_addr))
            # remove the functions that we already came across - essentially bypassing recursive function calls
            callers = set(addr for addr in callers if addr not in encountered)
            caller_depth = curr_depth + 1
            if caller_depth >= max_depth:
                # reached the depth limit. add them to potential analysis starts
                starts |= set(map(lambda caller_addr: trace.step_back(caller_addr, None, caller_func_addr), callers))
            else:
                # add them to the queue
                for item in map(lambda caller_addr: (trace.step_back(caller_addr, None, caller_func_addr),
                                                     caller_depth),
                                callers
                                ):
                    queue.append(item)
            encountered |= callers

        l.info("Discovered %d function starts at call-depth %d for sink %r.",
               len(starts),
               max_depth,
               sink
               )

        for idx, start in enumerate(starts):
            handler = Handler(project, False, sink_function=sink, sink_atoms=sink_atoms)
            rda = project.analyses.ReachingDefinitions(
                subject=CallTraceSubject(start, kb.functions[start.current_function_address()]),
                observe_all=True,
                function_handler=handler,
                kb=kb,
                dep_graph=DepGraph()
            )
            yield idx, len(starts), rda
Пример #16
0
def test_top_predecessors():
    dep_graph = DepGraph()

    # A -> B, B -> D, C -> D
    A = _a_mock_definition()
    B = _a_mock_definition()
    C = _a_mock_definition()
    D = _a_mock_definition()
    uses = [
        (A, B),
        (B, D),
        (C, D),
    ]

    for use in uses:
        dep_graph.add_edge(*use)

    result = dep_graph.top_predecessors(D)

    nose.tools.assert_list_equal(result, [A, C])
Пример #17
0
    def test_add_dependencies_for_concrete_pointers_of_does_nothing_if_data_pointed_to_by_definition_is_already_in_dependency_graph(
            self):
        arch = self.ArchMock()
        loader = self.LoaderMock(self.MainObjectMock(self.SectionMock(True)))

        memory_datum = self.MemoryDataMock(self.memory_address,
                                           str.encode(self.string_in_memory),
                                           len(self.string_in_memory),
                                           'string')
        cfg = self.CFGMock({self.memory_address: memory_datum})

        memory_location_definition = Definition(
            MemoryLocation(self.memory_address, self.string_in_memory_length),
            CodeLocation(0, 0),
        )

        register_definition = Definition(
            Register(0, 4),
            CodeLocation(0x42, 0),
        )

        dependency_graph = DepGraph(
            networkx.DiGraph([(memory_location_definition, register_definition)
                              ]))

        nodes_before_call = dependency_graph.nodes()

        dependency_graph.add_dependencies_for_concrete_pointers_of(
            [claripy.BVV(self.memory_address, arch.bits)], register_definition,
            cfg, loader)

        self.assertEqual(nodes_before_call, dependency_graph.nodes())
Пример #18
0
    def test_add_dependencies_for_concrete_pointers_of_adds_a_definition_with_codelocation_in_binary_if_data_in_readonly_memory(
            self):
        arch = self.ArchMock()

        writable = False
        loader = self.LoaderMock(
            self.MainObjectMock(self.SectionMock(writable)))

        memory_datum = self.MemoryDataMock(self.memory_address,
                                           str.encode(self.string_in_memory),
                                           len(self.string_in_memory),
                                           'string')
        cfg = self.CFGMock({self.memory_address: memory_datum})

        register_definition = Definition(
            Register(0, 4),
            CodeLocation(0x42, 0),
        )

        dependency_graph = DepGraph()
        dependency_graph.add_node(register_definition)

        dependency_graph.add_dependencies_for_concrete_pointers_of(
            [claripy.BVV(self.memory_address, arch.bits)],
            register_definition,
            cfg,
            loader,
        )

        origin_codelocation = CodeLocation(0, 0, info={'readonly': True})

        predecessor = list(
            dependency_graph.graph.predecessors(register_definition))[0]
        self.assertEqual(predecessor.codeloc, origin_codelocation)
Пример #19
0
    def test_transitive_closure_of_a_node(self):
        dep_graph = DepGraph()

        # A -> B, B -> D, C -> D
        A = _a_mock_definition()
        B = _a_mock_definition()
        C = _a_mock_definition()
        D = _a_mock_definition()
        uses = [
            (A, B),
            (B, D),
            (C, D),
        ]

        for use in uses:
            dep_graph.add_edge(*use)

        result = dep_graph.transitive_closure(D)
        result_nodes = set(result.nodes)
        result_edges = set(result.edges)

        self.assertSetEqual(result_nodes, {D, B, C, A})
        self.assertSetEqual(result_edges, {(B, D), (C, D), (A, B)})
Пример #20
0
    def test_add_dependencies_for_concrete_pointers_of_does_nothing_if_pointer_is_not_concrete(
            self):
        arch = self.ArchMock()
        cfg = self.CFGMock({})
        loader = self.LoaderMock(self.MainObjectMock(self.SectionMock(True)))

        register_definition = Definition(Register(0, 4), CodeLocation(0x42, 0),
                                         DataSet(UNDEFINED, arch.bits))

        dependency_graph = DepGraph()
        dependency_graph.add_node(register_definition)

        nodes_before_call = dependency_graph.nodes()

        dependency_graph.add_dependencies_for_concrete_pointers_of(
            register_definition, cfg, loader)

        self.assertEqual(nodes_before_call, dependency_graph.nodes())
Пример #21
0
    def test_dep_graph_stack_variables(self):
        bin_path = _binary_path('fauxware')
        project = angr.Project(bin_path, auto_load_libs=False)
        arch = project.arch
        cfg = project.analyses.CFGFast()
        main = cfg.functions['authenticate']

        rda = project.analyses.ReachingDefinitions(subject=main,
                                                   track_tmps=False,
                                                   dep_graph=DepGraph())
        dep_graph = rda.dep_graph
        open_rdi = next(
            iter(
                filter(
                    lambda def_: isinstance(def_.atom, Register) and def_.atom.
                    reg_offset == arch.registers['rdi'][
                        0] and def_.codeloc.ins_addr == 0x4006a2,
                    dep_graph._graph.nodes())))

        # 4006A2     mov  rdi, rax
        preds = list(dep_graph._graph.predecessors(open_rdi))
        self.assertEqual(len(preds), 1)
        rax: Definition = preds[0]
        self.assertIsInstance(rax.atom, Register)
        self.assertEqual(rax.atom.reg_offset, arch.registers['rax'][0])
        self.assertEqual(rax.codeloc.ins_addr, 0x400699)

        # 400699     mov  rax, [rbp+file]
        preds = list(dep_graph._graph.predecessors(rax))
        self.assertEqual(len(preds), 1)
        file_var: Definition = preds[0]
        self.assertIsInstance(file_var.atom, MemoryLocation)
        self.assertIsInstance(file_var.atom.addr, SpOffset)
        self.assertEqual(file_var.atom.addr.offset, -32)
        self.assertEqual(file_var.codeloc.ins_addr, 0x40066c)

        # 40066C     mov  [rbp+file], rdi
        preds = list(dep_graph._graph.predecessors(file_var))
        self.assertEqual(len(preds), 1)
        rdi: Definition = preds[0]
        self.assertIsInstance(rdi.atom, Register)
        self.assertEqual(rdi.atom.reg_offset, arch.registers['rdi'][0])
        self.assertIsInstance(rdi.codeloc, ExternalCodeLocation)
Пример #22
0
def test_dep_graph_stack_variables():
    bin_path = os.path.join(TESTS_LOCATION, 'x86_64', 'fauxware')
    project = angr.Project(bin_path, auto_load_libs=False)
    arch = project.arch
    cfg = project.analyses.CFGFast()
    main = cfg.functions['authenticate']

    rda = project.analyses.ReachingDefinitions(subject=main,
                                               track_tmps=False,
                                               dep_graph=DepGraph())
    dep_graph = rda.dep_graph
    open_rdi = next(
        iter(
            filter(
                lambda def_: isinstance(def_.atom, Register) and def_.atom.
                reg_offset == arch.registers['rdi'][
                    0] and def_.codeloc.ins_addr == 0x4006a2,
                dep_graph._graph.nodes())))

    # 4006A2     mov  rdi, rax
    preds = list(dep_graph._graph.predecessors(open_rdi))
    assert len(preds) == 1
    rax: Definition = preds[0]
    assert isinstance(rax.atom, Register)
    assert rax.atom.reg_offset == arch.registers['rax'][0]
    assert rax.codeloc.ins_addr == 0x400699

    # 400699     mov  rax, [rbp+file]
    preds = list(dep_graph._graph.predecessors(rax))
    assert len(preds) == 1
    file_var: Definition = preds[0]
    assert isinstance(file_var.atom, MemoryLocation)
    assert isinstance(file_var.atom.addr, SpOffset)
    assert file_var.atom.addr.offset == -32
    assert file_var.codeloc.ins_addr == 0x40066c

    # 40066C     mov  [rbp+file], rdi
    preds = list(dep_graph._graph.predecessors(file_var))
    assert len(preds) == 1
    rdi: Definition = preds[0]
    assert isinstance(rdi.atom, Register)
    assert rdi.atom.reg_offset == arch.registers['rdi'][0]
    assert isinstance(rdi.codeloc, ExternalCodeLocation)
Пример #23
0
    def test_transitive_closure_includes_beginning_node_with_memoized_content(
            self):
        dep_graph = DepGraph()
        # A -> B
        # B -> C
        # C -> D
        A = _a_mock_definition()
        B = _a_mock_definition()
        C = _a_mock_definition()
        D = _a_mock_definition()
        uses = [(A, B), (B, C), (C, D)]
        for use in uses:
            dep_graph.add_edge(*use)

        closure_0 = dep_graph.transitive_closure(C)
        self.assertNotIn(D, closure_0)

        closure_1 = dep_graph.transitive_closure(D)
        self.assertIn(D, closure_1)
        self.assertTrue(closure_1.has_edge(A, B))
        self.assertTrue(closure_1.has_edge(B, C))
        self.assertTrue(closure_1.has_edge(C, D))
Пример #24
0
 def test_refuses_to_add_edge_between_non_definition_nodes(self):
     dep_graph = DepGraph()
     self.assertRaises(TypeError, dep_graph.add_edge, 1, 2)
Пример #25
0
def test_dep_graph_has_a_default_graph():
    dep_graph = DepGraph()
    nose.tools.assert_equal(isinstance(dep_graph.graph, networkx.DiGraph),
                            True)
Пример #26
0
def test_refuses_to_add_edge_between_non_definition_nodes():
    dep_graph = DepGraph()
    nose.tools.assert_raises(TypeError, dep_graph.add_edge, 1, 2)
Пример #27
0
def test_dep_graph():
    project = angr.Project(os.path.join(TESTS_LOCATION, 'x86_64', 'true'), auto_load_libs=False)
    cfg = project.analyses.CFGFast()
    main = cfg.functions['main']

    # build a def-use graph for main() of /bin/true without tmps. check that the only dependency of the first block's guard is the four cc registers
    rda = project.analyses.ReachingDefinitions(subject=main, track_tmps=False, dep_graph=DepGraph())
    guard_use = list(filter(
        lambda def_: type(def_.atom) is GuardUse and def_.codeloc.block_addr == main.addr,
        rda.dep_graph._graph.nodes()
    ))[0]
    nose.tools.assert_equal(
        len(rda.dep_graph._graph.pred[guard_use]),
        4
    )
    nose.tools.assert_equal(
        all(type(def_.atom) is Register for def_ in rda.dep_graph._graph.pred[guard_use]),
        True
    )
    nose.tools.assert_equal(
        set(def_.atom.reg_offset for def_ in rda.dep_graph._graph.pred[guard_use]),
        {reg.vex_offset for reg in project.arch.register_list if reg.name.startswith('cc_')}
    )

    # build a def-use graph for main() of /bin/true. check that t7 in the first block is only used by the guard
    rda = project.analyses.ReachingDefinitions(subject=main, track_tmps=True, dep_graph=DepGraph())
    tmp_7 = list(filter(
        lambda def_: type(def_.atom) is Tmp and def_.atom.tmp_idx == 7 and def_.codeloc.block_addr == main.addr,
        rda.dep_graph._graph.nodes()
    ))[0]
    nose.tools.assert_equal(
        len(rda.dep_graph._graph.succ[tmp_7]),
        1
    )
    nose.tools.assert_equal(
        type(list(rda.dep_graph._graph.succ[tmp_7])[0].atom),
        GuardUse
    )
Пример #28
0
 def test_dep_graph_has_a_default_graph(self):
     dep_graph = DepGraph()
     self.assertEqual(isinstance(dep_graph.graph, networkx.DiGraph), True)