Пример #1
0
    def validate(self, operation, odb_file):
        """" Validate the type fits at the given address. """
        vma = operation.vma

        # determine the size of this defined data
        size = operation.size

        # Verify there is not something already defined here
        for dd in odb_file.get_structure_list(DefinedData):
            if dd.overlaps(vma, size):
                raise DefineDataCollisionException(
                    'Data collides with existing data %s' % dd.var_name)

        # Verify there is enough room in the parcel
        parcels = ParcelList(odb_file.get_structure_list(Parcel))
        parcel = parcels.find_parcel_by_vma(vma)

        if not parcel:
            raise ValidationError('Cannot define data at bad address 0x%x' %
                                  vma)
        elif parcel.is_code:
            raise ValidationError('Cannot define data in code region')

        if not parcel.contains_vma(vma + size - 1):
            raise ValidationError('Not enough room to define data')
Пример #2
0
    def operate(self, odb_file):

        self.object_id = odb_file.get_object_id()

        parcels = ParcelList(odb_file.get_structure_list(Parcel))
        parcel = parcels.find_parcel_by_vma(self.vma)
        maxLen = parcel.vma_end - self.vma

        ofd = Ofd(odb_file.binary)
        rawData = ofd.bfd.raw_data(ofd.bfd.sections[parcel.sec_name], self.vma,
                                   maxLen)
        rawData = bytes(rawData)

        # instantiate the type to determine its size
        type_inst = self.instantiate_type(self.type_kind, self.type_name,
                                          odb_file)

        self.size = type_inst.calc_size(rawData)

        if (self.size <= 0):
            raise DefineDataException("Cannot instantiate empty type")

        try:
            FitValidator().validate(self, odb_file)
        except DefineDataCollisionException as e:
            merged = False
            for dd in odb_file.get_structure_list(DefinedData):
                # find the defined data that overlaps
                if dd.overlaps(self.vma, self.size):
                    # instantiate the class
                    dd_inst = self.instantiate_type(dd.type_kind, dd.type_name,
                                                    odb_file)
                    # if we can merge
                    if dd_inst.can_merge(self.type_kind, self.type_name):
                        # delete the data we're going to merge with
                        odb_file.execute(UndefineDataOperation(dd.vma))
                        # run the fit again just to be sure
                        FitValidator().validate(self, odb_file)
                        merged = True

            if not merged:
                raise e

        definedData = odb_file.create_item(
            DefinedData, {
                'type_kind': self.type_kind,
                'type_name': self.type_name,
                'var_name': self.var_name,
                'vma': self.vma,
                'size': self.size,
            })

        odb_file.insert_item(definedData)
Пример #3
0
 def make_code(self, vma):
     parcels = ParcelList(self.odb_file.get_structure_list(Parcel))
     p = parcels.find_parcel_by_vma(vma)
     if p.is_code:
         raise Exception("Cannot make code on a code parcel")
     self.odb_file.execute(ActiveScanOperation(vma))
Пример #4
0
 def make_data(self, vma):
     parcels = ParcelList(self.odb_file.get_structure_list(Parcel))
     p = parcels.find_parcel_by_vma(vma)
     if not p.is_code:
         raise Exception("Cannot make data on non-code parcel")
     self.odb_file.execute(SplitParcelOperation(vma))
Пример #5
0
        def convert_to_code(vma, odb_file, analyzer, the_ofd):

            parcels = ParcelList(odb_file.get_structure_list(Parcel))
            if len(parcels) == 0:
                raise Exception('Invalid operation: No parcels found')

            parcels_to_adjust = [p for p in parcels if p.vma_start > vma]

            p = parcels.find_parcel_by_vma(vma)

            if p is None:
                raise Exception('Invalid address: 0x%08x' % vma)

            # before doing anything, check if the given address starts with at least one valid instruction
            if not self._is_valid_instruction(the_ofd, analyzer, vma,
                                              p.vma_end):
                raise Exception("Failed to make code: invalid opcode")

            bad_code_scanner = BadCodeScanner(analyzer)
            function_scanner = FunctionScanner(
                analyzer, odb_file.get_structure_list(Symbol))
            branch_scanner = BranchScanner(analyzer)
            parcel_scanner = ParcelOffsetScanner()

            scanners = [
                # The parcel scanner must come first here, because we need it to 'push_vma' for the bad instruction, if
                # one is encountered
                parcel_scanner,
                bad_code_scanner,
                function_scanner,
                branch_scanner,
            ]

            # split the data parcel
            odb_file.execute(SplitParcelOperation(vma))

            parcels = ParcelList(odb_file.get_structure_list(Parcel))
            p = parcels.find_parcel_by_vma(vma)
            ldas_before_scan = p.size  # one lda per byte

            def callback(addr, rawData, instr, abfd, self):
                for scanner in scanners:
                    scanner.scan_line(addr, rawData, instr, abfd, p)

            section = the_ofd.get_section_from_addr(vma)
            the_ofd.disassemble(section.name, [],
                                vma,
                                p.vma_end,
                                funcFmtLine=callback,
                                funcFmtLineArgs={
                                    'self': self,
                                })

            ldas_removed = ldas_before_scan - p.num_ldas
            for p in parcels_to_adjust:
                p.lda_start -= ldas_removed

            for scanner in scanners:
                scanner.commit(odb_file)

            bad_addr = bad_code_scanner.bad_addr
            if bad_addr is not None:
                p = parcels.find_parcel_by_vma(bad_addr)
                odb_file.execute(SplitParcelOperation(bad_addr))

            #
            # consolidate the code parcels
            #
            parcels = ParcelList(odb_file.get_structure_list(Parcel))
            transaction = parcels.consolidate()

            # remove dead parcels
            for p in transaction.to_remove:
                odb_file.remove_item(p)
Пример #6
0
    def operate(self, odb_file):

        self.object_id = odb_file.get_object_id()

        analyzer = get_processor(odb_file.get_arch(), odb_file)
        the_ofd = ofd.Ofd(odb_file.get_binary())

        def convert_to_code(vma, odb_file, analyzer, the_ofd):

            parcels = ParcelList(odb_file.get_structure_list(Parcel))
            if len(parcels) == 0:
                raise Exception('Invalid operation: No parcels found')

            parcels_to_adjust = [p for p in parcels if p.vma_start > vma]

            p = parcels.find_parcel_by_vma(vma)

            if p is None:
                raise Exception('Invalid address: 0x%08x' % vma)

            # before doing anything, check if the given address starts with at least one valid instruction
            if not self._is_valid_instruction(the_ofd, analyzer, vma,
                                              p.vma_end):
                raise Exception("Failed to make code: invalid opcode")

            bad_code_scanner = BadCodeScanner(analyzer)
            function_scanner = FunctionScanner(
                analyzer, odb_file.get_structure_list(Symbol))
            branch_scanner = BranchScanner(analyzer)
            parcel_scanner = ParcelOffsetScanner()

            scanners = [
                # The parcel scanner must come first here, because we need it to 'push_vma' for the bad instruction, if
                # one is encountered
                parcel_scanner,
                bad_code_scanner,
                function_scanner,
                branch_scanner,
            ]

            # split the data parcel
            odb_file.execute(SplitParcelOperation(vma))

            parcels = ParcelList(odb_file.get_structure_list(Parcel))
            p = parcels.find_parcel_by_vma(vma)
            ldas_before_scan = p.size  # one lda per byte

            def callback(addr, rawData, instr, abfd, self):
                for scanner in scanners:
                    scanner.scan_line(addr, rawData, instr, abfd, p)

            section = the_ofd.get_section_from_addr(vma)
            the_ofd.disassemble(section.name, [],
                                vma,
                                p.vma_end,
                                funcFmtLine=callback,
                                funcFmtLineArgs={
                                    'self': self,
                                })

            ldas_removed = ldas_before_scan - p.num_ldas
            for p in parcels_to_adjust:
                p.lda_start -= ldas_removed

            for scanner in scanners:
                scanner.commit(odb_file)

            bad_addr = bad_code_scanner.bad_addr
            if bad_addr is not None:
                p = parcels.find_parcel_by_vma(bad_addr)
                odb_file.execute(SplitParcelOperation(bad_addr))

            #
            # consolidate the code parcels
            #
            parcels = ParcelList(odb_file.get_structure_list(Parcel))
            transaction = parcels.consolidate()

            # remove dead parcels
            for p in transaction.to_remove:
                odb_file.remove_item(p)

        convert_to_code(self.vma, odb_file, analyzer, the_ofd)

        # convert newly found functions to code as well
        following_new_funcs = True
        while following_new_funcs:
            funcs = odb_file.get_structure_list(Function)
            parcels = ParcelList(odb_file.get_structure_list(Parcel))

            # assume we are done unless we find at least one function that is within a data parcel
            following_new_funcs = False
            for f in funcs:
                p = parcels.find_parcel_by_vma(f.vma)

                if (  # if this function is at a valid address
                    (p is not None) and
                        # and the function is within a data parcel
                    (not p.is_code) and
                        # and the function looks to be valid code
                        self._is_valid_instruction(the_ofd, analyzer, f.vma,
                                                   p.vma_end)):

                    # convert this function to code
                    convert_to_code(f.vma, odb_file, analyzer, the_ofd)

                    # take another pass, in case we discovered new functions to disassemble
                    following_new_funcs = True

                    # break out of the for loop, but not the while
                    break
Пример #7
0
    def test_split_to_data_single_inst_in_parcel(self):
        odb_file = OdbFile(BinaryFile(
            self.get_test_bin_path('code_split_single_instruction/split_parcel_single'), 'elf64-x86-64', 'i386:x86-64'))
        odb_file.execute(LoadOperation())
        odb_file.execute(PassiveScanOperation())

        parcels_before = ParcelList(odb_file.get_structure_list(Parcel))

        # only one instruction in .single
        p = parcels_before.find_parcel_by_vma(0x400598)
        p_text = parcels_before.find_parcel_by_vma(0x400597)
        p_fini = parcels_before.find_parcel_by_vma(0x4005a0)
        p_got = parcels_before.find_parcel_by_vma(0x600fe0)
        num_ldas_before = p.num_ldas
        num_text_ldas_before = p_text.num_ldas
        num_fini_ldas_before = p_fini.num_ldas
        single_lda_before = p.lda_start
        fini_lda_before = p_fini.lda_start
        text_lda_before = p_text.lda_start
        got_lda_before = p_got.lda_start

        # number of parcels before the split
        self.assertEquals(25, len(parcels_before))

        # execute the split operation
        odb_file.execute(SplitParcelOperation(0x400598))

        # number of parcels after the split (should not have changed)
        parcels_after = ParcelList(odb_file.get_structure_list(Parcel))
        self.assertEquals(25, len(parcels_after))

        # get the parcels
        p = parcels_after.find_parcel_by_vma(0x400598)
        p_text = parcels_after.find_parcel_by_vma(0x400597)
        p_fini = parcels_after.find_parcel_by_vma(0x4005a0)

        # make sure they are all unique
        self.assertNotEqual(p, p_text)
        self.assertNotEqual(p, p_fini)
        self.assertNotEqual(p_text, p_fini)

        # check num_ldas in p (1 instruction become 7 data bytes)
        self.assertEquals(7, p.num_ldas)

        # check num_ldas in text didn't change
        self.assertEquals(num_text_ldas_before, p_text.num_ldas)

        # check num_ldas in fini didn't change
        self.assertEquals(num_fini_ldas_before, p_fini.num_ldas)

        # make sure vmas line up
        self.assertEquals(0x4003d0, p_text.vma_start)
        self.assertEquals(0x400598, p_text.vma_end)
        self.assertEquals(0x400598, p.vma_start)
        self.assertEquals(0x40059f, p.vma_end)
        self.assertEquals(0x4005a0, p_fini.vma_start)
        self.assertEquals(0x4005ae, p_fini.vma_end)

        #
        # make sure vma->lda mappings line up
        #

        # these shouldn't have changed
        self.assertEquals(text_lda_before, parcels_after.vma_to_lda(0x4003d0))
        self.assertEquals(text_lda_before+num_text_ldas_before-1, parcels_after.vma_to_lda(0x400597))
        self.assertEquals(single_lda_before, parcels_after.vma_to_lda(0x400598))
        self.assertEquals(True, p_text.is_code)
        self.assertEquals(True, p_fini.is_code)

        # these changed
        self.assertEquals(single_lda_before+1, parcels_after.vma_to_lda(0x400599))
        self.assertEquals(single_lda_before+2, parcels_after.vma_to_lda(0x40059a))
        self.assertEquals(single_lda_before+3, parcels_after.vma_to_lda(0x40059b))
        self.assertEquals(single_lda_before+4, parcels_after.vma_to_lda(0x40059c))
        self.assertEquals(single_lda_before+5, parcels_after.vma_to_lda(0x40059d))
        self.assertEquals(single_lda_before+6, parcels_after.vma_to_lda(0x40059e))
        self.assertEquals(False, p.is_code)

        # 1 instruction turned into 7 data bytes, so it shifted the following parcels by 6 ldas
        self.assertEquals(fini_lda_before+6, parcels_after.vma_to_lda(0x4005a0))
        self.assertEquals(got_lda_before+6, parcels_after.vma_to_lda(0x600fe0))

        #
        # make sure lda->vma mappings line up
        #

        self.assertEquals(text_lda_before, parcels_after.vma_to_lda(0x4003d0))
        self.assertEquals(text_lda_before+num_text_ldas_before-1, parcels_after.vma_to_lda(0x400597))
        self.assertEquals(single_lda_before, parcels_after.vma_to_lda(0x400598))

        # these shouldn't have changed
        self.assertEquals(0x4003d0, parcels_after.lda_to_vma(text_lda_before))
        self.assertEquals(0x400597, parcels_after.lda_to_vma(text_lda_before+num_text_ldas_before-1))
        self.assertEquals(0x400598, parcels_after.lda_to_vma(single_lda_before))

        # these changed
        self.assertEquals(0x400599, parcels_after.lda_to_vma(single_lda_before+1))
        self.assertEquals(0x40059a, parcels_after.lda_to_vma(single_lda_before+2))
        self.assertEquals(0x40059b, parcels_after.lda_to_vma(single_lda_before+3))
        self.assertEquals(0x40059c, parcels_after.lda_to_vma(single_lda_before+4))
        self.assertEquals(0x40059d, parcels_after.lda_to_vma(single_lda_before+5))
        self.assertEquals(0x40059e, parcels_after.lda_to_vma(single_lda_before+6))

        # 1 instruction turned into 7 data bytes, so it shifted the following parcels by 6 ldas
        self.assertEquals(0x4005a0, parcels_after.lda_to_vma(fini_lda_before+6))
        self.assertEquals(0x600fe0, parcels_after.lda_to_vma(got_lda_before+6))
Пример #8
0
    def test_split_to_data_at_end_of_parcel(self):
        odb_file = OdbFile(BinaryFile(self.get_test_bin_path('ls'), 'elf64-x86-64', 'i386:x86-64'))
        odb_file.execute(LoadOperation())
        odb_file.execute(PassiveScanOperation())

        parcels_before = ParcelList(odb_file.get_structure_list(Parcel))

        # last instruction in .plt
        p = parcels_before.find_parcel_by_vma(0x4029bb)
        p3 = parcels_before.find_parcel_by_vma(0x4029c0)
        num_p1_ldas_before = p.num_ldas
        num_p3_ldas_before = p3.num_ldas
        plt_lda_before = parcels_before.vma_to_lda(0x402320)
        text_lda_before = parcels_before.vma_to_lda(0x4029c0)

        # number of parcels before the split
        self.assertEquals(25, len(parcels_before))

        # execute the split operation
        odb_file.execute(SplitParcelOperation(0x4029bb))

        # number of parcels after the split (should only add one more)
        parcels_after = ParcelList(odb_file.get_structure_list(Parcel))
        self.assertEquals(26, len(parcels_after))

        # get the two parcels (plus the .text one following)
        p1 = parcels_after.find_parcel_by_vma(0x402320)
        p2 = parcels_after.find_parcel_by_vma(0x4029bb)
        p3 = parcels_after.find_parcel_by_vma(0x4029c0)

        # make sure they are all unique
        self.assertNotEqual(p1, p2)
        self.assertNotEqual(p1, p3)
        self.assertNotEqual(p2, p3)

        # check num_ldas in p1 (5 instructions)
        self.assertEquals(num_p1_ldas_before - 1, p1.num_ldas)

        # check num_ldas in p2 (this one instruction has now become 5 data bytes)
        self.assertEquals(5, p2.num_ldas)

        # check num_ldas in p3 (should not have changed)
        self.assertEquals(num_p3_ldas_before, p3.num_ldas)

        # make sure vmas line up
        self.assertEquals(0x402320, p1.vma_start)
        self.assertEquals(0x4029bb, p1.vma_end)
        self.assertEquals(0x4029bb, p2.vma_start)
        self.assertEquals(0x4029c0, p2.vma_end)
        self.assertEquals(0x4029c0, p3.vma_start)
        self.assertEquals(0x412bec, p3.vma_end)

        #
        # make sure vma->lda mappings line up
        #

        # these shouldn't have changed
        self.assertEquals(plt_lda_before, parcels_after.vma_to_lda(0x402320))
        self.assertEquals(plt_lda_before+num_p1_ldas_before-2, parcels_after.vma_to_lda(0x4029b6))
        self.assertEquals(plt_lda_before+num_p1_ldas_before-1, parcels_after.vma_to_lda(0x4029bb))
        self.assertEquals(True, p1.is_code)
        self.assertEquals(True, p3.is_code)

        # these changed
        self.assertEquals(plt_lda_before+num_p1_ldas_before, parcels_after.vma_to_lda(0x4029bc))
        self.assertEquals(plt_lda_before+num_p1_ldas_before+1, parcels_after.vma_to_lda(0x4029bd))
        self.assertEquals(plt_lda_before+num_p1_ldas_before+2, parcels_after.vma_to_lda(0x4029be))
        self.assertEquals(plt_lda_before+num_p1_ldas_before+3, parcels_after.vma_to_lda(0x4029bf))
        self.assertEquals(plt_lda_before+num_p1_ldas_before+4, parcels_after.vma_to_lda(0x4029c0))
        self.assertEquals(False, p2.is_code)

        # 1 instruction turned into 5 data bytes, so it shifted the following parcels by 4 ldas
        self.assertEquals(text_lda_before+4, parcels_after.vma_to_lda(0x4029c0))

        #
        # make sure lda->vma mappings line up
        #

        # these shouldn't have changed
        self.assertEquals(0x402320, parcels_after.lda_to_vma(plt_lda_before))
        self.assertEquals(0x4029b6, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before-2))
        self.assertEquals(0x4029bb, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before-1))

        # these changed
        self.assertEquals(0x4029bc, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before))
        self.assertEquals(0x4029bd, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before+1))
        self.assertEquals(0x4029be, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before+2))
        self.assertEquals(0x4029bf, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before+3))
        self.assertEquals(0x4029c0, parcels_after.lda_to_vma(plt_lda_before+num_p1_ldas_before+4))

        # 1 instruction turned into 5 data bytes, so it shifted the following parcels by 4 ldas
        self.assertEquals(0x4029c0, parcels_after.lda_to_vma(text_lda_before+4))
Пример #9
0
    def test_split_to_data_basic(self):
        odb_file = OdbFile(BinaryFile(self.get_test_bin_path('ls'), 'elf64-x86-64', 'i386:x86-64'))
        odb_file.execute(LoadOperation())
        odb_file.execute(PassiveScanOperation())

        parcels_before = ParcelList(odb_file.get_structure_list(Parcel))
        p = parcels_before.find_parcel_by_vma(0x4029c0)
        num_p1_ldas_before = p.num_ldas
        text_lda_before = parcels_before.vma_to_lda(0x4029c0)
        fini_lda_before = parcels_before.vma_to_lda(0x412bec)

        # number of parcels before the split
        self.assertEquals(25, len(parcels_before))

        # execute the split operation
        odb_file.execute(SplitParcelOperation(0x4029d5))

        # number of parcels after the split
        parcels_after = ParcelList(odb_file.get_structure_list(Parcel))
        self.assertEquals(27, len(parcels_after))

        # get the three parcels
        p1 = parcels_after.find_parcel_by_vma(0x4029c0)
        p2 = parcels_after.find_parcel_by_vma(0x4029d5)
        p3 = parcels_after.find_parcel_by_vma(0x4029da)

        # make sure they are all unique
        self.assertNotEqual(p1, p2)
        self.assertNotEqual(p1, p3)
        self.assertNotEqual(p2, p3)

        # check num_ldas in p1 (5 instructions)
        self.assertEquals(5, p1.num_ldas)

        # check num_ldas in p2 (this one instruction has now become 5 data bytes)
        self.assertEquals(5, p2.num_ldas)

        # check num_ldas in p3 (less 5 from p1 and less 1 from p2 - remember it used to only be 1 lda before the split)
        self.assertEquals(num_p1_ldas_before - 5 - 1, p3.num_ldas)

        # make sure vmas line up
        self.assertEquals(0x4029c0, p1.vma_start)
        self.assertEquals(0x4029d5, p1.vma_end)
        self.assertEquals(0x4029d5, p2.vma_start)
        self.assertEquals(0x4029da, p2.vma_end)
        self.assertEquals(0x4029da, p3.vma_start)
        self.assertEquals(0x412bec, p3.vma_end)

        #
        # make sure vma->lda mappings line up
        #

        # these shouldn't have changed
        self.assertEquals(text_lda_before, parcels_after.vma_to_lda(0x4029c0))
        self.assertEquals(text_lda_before+5, parcels_after.vma_to_lda(0x4029d5))
        self.assertEquals(True, p1.is_code)
        self.assertEquals(True, p3.is_code)

        # these changed
        self.assertEquals(text_lda_before+6, parcels_after.vma_to_lda(0x4029d6))
        self.assertEquals(text_lda_before+7, parcels_after.vma_to_lda(0x4029d7))
        self.assertEquals(text_lda_before+8, parcels_after.vma_to_lda(0x4029d8))
        self.assertEquals(text_lda_before+9, parcels_after.vma_to_lda(0x4029d9))
        self.assertEquals(text_lda_before+10, parcels_after.vma_to_lda(0x4029da))
        self.assertEquals(text_lda_before+11, parcels_after.vma_to_lda(0x4029db))
        self.assertEquals(False, p2.is_code)

        # 1 instruction turned into 5 data bytes, so it shifted the following parcels by 4 ldas
        self.assertEquals(fini_lda_before+4, parcels_after.vma_to_lda(0x412bec))

        #
        # make sure lda->vma mappings line up
        #

        # these shouldn't have changed
        self.assertEquals(0x4029c0, parcels_after.lda_to_vma(text_lda_before))
        self.assertEquals(0x4029d5, parcels_after.lda_to_vma(text_lda_before+5))

        # these changed
        self.assertEquals(0x4029d6, parcels_after.lda_to_vma(text_lda_before+6))
        self.assertEquals(0x4029d7, parcels_after.lda_to_vma(text_lda_before+7))
        self.assertEquals(0x4029d8, parcels_after.lda_to_vma(text_lda_before+8))
        self.assertEquals(0x4029d9, parcels_after.lda_to_vma(text_lda_before+9))
        self.assertEquals(0x4029da, parcels_after.lda_to_vma(text_lda_before+10))
        self.assertEquals(0x4029db, parcels_after.lda_to_vma(text_lda_before+11))

        # 1 instruction turned into 5 data bytes, so it shifted the following parcels by 4 ldas
        self.assertEquals(0x412bec, parcels_after.lda_to_vma(fini_lda_before+4))
Пример #10
0
    def test_split_to_data_at_start_of_parcel(self):
        odb_file = OdbFile(BinaryFile(self.get_test_bin_path('ls'), 'elf64-x86-64', 'i386:x86-64'))
        odb_file.execute(LoadOperation())
        odb_file.execute(PassiveScanOperation())

        parcels_before = ParcelList(odb_file.get_structure_list(Parcel))

        # start of .fini section
        p = parcels_before.find_parcel_by_vma(0x412bec)
        num_p1_ldas_before = p.num_ldas
        fini_lda_before = parcels_before.vma_to_lda(0x412bec)

        # number of parcels before the split
        self.assertEquals(25, len(parcels_before))

        # execute the split operation
        odb_file.execute(SplitParcelOperation(0x412bec))

        # number of parcels after the split (should only add one more)
        parcels_after = ParcelList(odb_file.get_structure_list(Parcel))
        self.assertEquals(26, len(parcels_after))

        # get the two parcels
        p1 = parcels_after.find_parcel_by_vma(0x412bec)
        p2 = parcels_after.find_parcel_by_vma(0x412bf0)

        # make sure they are unique
        self.assertNotEqual(p1, p2)

        # check num_ldas in p1 (4 data bytes)
        self.assertEquals(4, p1.num_ldas)

        # check num_ldas in p2
        self.assertEquals(2, p2.num_ldas)

        # make sure vmas line up
        self.assertEquals(0x412bec, p1.vma_start)
        self.assertEquals(0x412bf0, p1.vma_end)
        self.assertEquals(0x412bf0, p2.vma_start)
        self.assertEquals(0x412bf5, p2.vma_end)

        #
        # make sure vma->lda mappings line up
        #

        # these shouldn't have changed
        self.assertEquals(fini_lda_before, parcels_after.vma_to_lda(0x412bec))
        self.assertEquals(True, p2.is_code)

        # these changed
        self.assertEquals(fini_lda_before+1, parcels_after.vma_to_lda(0x412bed))
        self.assertEquals(fini_lda_before+2, parcels_after.vma_to_lda(0x412bee))
        self.assertEquals(fini_lda_before+3, parcels_after.vma_to_lda(0x412bef))
        self.assertEquals(fini_lda_before+4, parcels_after.vma_to_lda(0x412bf0))
        self.assertEquals(fini_lda_before+5, parcels_after.vma_to_lda(0x412bf4))
        self.assertEquals(False, p1.is_code)

        #
        # make sure lda->vma mappings line up
        #

        # these shouldn't have changed
        self.assertEquals(0x412bec, parcels_after.lda_to_vma(fini_lda_before))

        # these changed
        self.assertEquals(0x412bed, parcels_after.lda_to_vma(fini_lda_before+1))
        self.assertEquals(0x412bee, parcels_after.lda_to_vma(fini_lda_before+2))
        self.assertEquals(0x412bef, parcels_after.lda_to_vma(fini_lda_before+3))
        self.assertEquals(0x412bf0, parcels_after.lda_to_vma(fini_lda_before+4))
        self.assertEquals(0x412bf4, parcels_after.lda_to_vma(fini_lda_before+5))
Пример #11
0
class BranchGraphView(list):
    def __init__(self, odb_file, vma):
        self.odb_file = odb_file
        start = None
        end = None
        self.parcels = ParcelList(odb_file.get_structure_list(Parcel))

        # first, some validation
        p = self.parcels.find_parcel_by_vma(vma)
        if p:
            # initialize to parcel boundaries
            start = p.vma_start
            end = p.vma_end

            # if this is not in a code section
            if not p.is_code:
                raise Exception(
                    "Graph view is not available for data sections!")

        # else, invalid address
        else:
            raise Exception(
                "Graph view is not available for invalid address 0x%x!" % vma)

        if type(odb_file.binary) == BinaryString:
            # TODO: Handle nonzero base address
            start = 0
            end = odb_file.binary.size

        functions = sorted(odb_file.get_structure_list(Function),
                           key=lambda f: f.vma)
        for f in functions:
            if vma >= f.vma:
                start = f.vma
            elif vma < f.vma:
                end = f.vma
                break

        # get all the branches within this function
        # TODO: Deal with indirect branches (i.e., jmp rax)
        branches = filter(
            lambda b: b.srcAddr >= start and b.srcAddr < end and b.
            targetAddr >= start and b.targetAddr < end,
            odb_file.get_structure_list(Branch))

        # dict of nodes, keyed by starting address of the block
        self._nodes = {}
        thisBlock = start

        for b in sorted(branches, key=lambda b: b.srcAddr):
            # address if the branch is not taken (i.e., the addr of the next instruction)
            no = b.srcAddr + b.instrLen

            # address if the branch is taken
            yes = b.targetAddr

            # first node
            self._nodes[thisBlock] = BranchGraphNode(thisBlock, b.srcAddr,
                                                     no - thisBlock, yes, no)

            # advance to next block
            thisBlock = no

        # make last block
        if thisBlock != end:
            end_line = self.parcels.vma_to_lda(end - 1)
            last_addr = self.parcels.lda_to_vma(end_line)
            self._nodes[thisBlock] = BranchGraphNode(thisBlock, last_addr,
                                                     end - thisBlock, None,
                                                     None)

        # for each target 'yes' branch address
        # NOTE: The 'no' branch addresses are guaranteed to be nodes by this point
        # NOTE: We can't iterate over the nodes with iteritems() since we modify the dictionary in the loop
        for addr in [n.yesAddr for n in self._nodes.values()]:
            # if this is a new node
            if addr not in self._nodes:
                # find the node that contains this address (the parent) and split it
                for parent in list(self._nodes.values()):
                    if addr in parent:
                        newNode = parent.split(addr, self.parcels)
                        self._nodes[addr] = newNode

    '''
    {
        nodes:
        [
            {id:X, instructions: [du1, du2, du3, du4 ... }
            {id:Y, instructions: [du9, du10, du11 .... }
            ....
        ]
        links: [
            { from:x, to:y, type: 'taken' }
            { from:y, to: z, type: 'notTaken'}
            { from:o, to: q, type: 'unconditional'}
        ]
    }
    '''

    def getNodes(self):

        dasm = Disassembler(self.odb_file)

        # convert _nodes to representation needed by front end
        nodes = []
        for startAddr, node in self._nodes.items():
            startLine = self.parcels.vma_to_lda(startAddr)
            endLine = self.parcels.vma_to_lda(startAddr + node.size)
            if endLine is None:
                endLine = self.parcels.sum_ldas()
            dus = dasm.display(startAddr, endLine - startLine, False)
            nodes.append({'id': node.id, 'instructions': dus})

        return nodes

    def getLinks(self):
        links = []
        # convert _nodes to representation needed by front end
        for startAddr, node in self._nodes.items():
            try:
                if node.noAddr:
                    links.append({
                        'from': node.id,
                        'to': self._nodes[node.noAddr].id,
                        'type': 'notTaken'
                    })
                    links.append({
                        'from': node.id,
                        'to': self._nodes[node.yesAddr].id,
                        'type': 'taken'
                    })
                elif node.yesAddr:
                    links.append({
                        'from': node.id,
                        'to': self._nodes[node.yesAddr].id,
                        'type': 'unconditional'
                    })
                else:
                    # else, this is the last node in the function, which has no links
                    pass
            except Exception as e:
                # for now, keep going if something goes wrong
                # AVD@9/8/15: An exception can happen right now if the last instruction in the function is a
                #             branch to somewhere in the middle of the function.  The problem is that the "no"
                #             address is not within the function, since it is actually the first instruction of
                #             the next function.  Need to revisit this and see if we can do better than just
                #             skipping this link.
                continue

        return links

    nodes = property(getNodes)
    links = property(getLinks)
Пример #12
0
    def test_basic_active_scan(self):

        # we load this ELF as a raw binary, because it has an invalid opcode fairly early on
        odb_file = OdbFile(
            BinaryFile(self.get_test_bin_path('mkdir'), 'elf64-x86-64',
                       'i386:x86-64'))
        odb_file.execute(LoadOperation())
        odb_file.execute(PassiveScanOperation())

        parcels_before = ParcelList(odb_file.get_structure_list(Parcel))
        p = parcels_before.find_parcel_by_vma(0x4002ce)
        gnu_hash_lda_before = parcels_before.vma_to_lda(0x4002cd)
        dynsym_lda_before = parcels_before.vma_to_lda(0x4002e0)
        dynstr_lda_before = parcels_before.vma_to_lda(0x400a18)

        # number of parcels before the split
        self.assertEquals(24, len(parcels_before))

        # execute the split operation
        odb_file.execute(ActiveScanOperation(0x4002ce))

        # number of parcels after the split
        parcels_after = ParcelList(odb_file.get_structure_list(Parcel))
        self.assertEquals(26, len(parcels_after))

        # get the three parcels
        p1 = parcels_after.find_parcel_by_vma(0x400298)
        p2 = parcels_after.find_parcel_by_vma(0x4002ce)
        p3 = parcels_after.find_parcel_by_vma(0x4002d5)

        # make sure they are all unique
        self.assertNotEqual(p1, p2)
        self.assertNotEqual(p1, p3)
        self.assertNotEqual(p2, p3)

        # check num_ldas in p1 (54 bytes)
        self.assertEquals(54, p1.num_ldas)

        # check num_ldas in p2 (3 instructions)
        self.assertEquals(3, p2.num_ldas)

        # check num_ldas in p3 (7 bytes)
        self.assertEquals(7, p3.num_ldas)

        # make sure vmas line up
        self.assertEquals(0x400298, p1.vma_start)
        self.assertEquals(0x4002ce, p1.vma_end)
        self.assertEquals(0x4002ce, p2.vma_start)
        self.assertEquals(0x4002d5, p2.vma_end)
        self.assertEquals(0x4002d5, p3.vma_start)
        self.assertEquals(0x4002dc, p3.vma_end)

        #
        # make sure vma->lda mappings line up
        #

        # these shouldn't have changed
        self.assertEquals(gnu_hash_lda_before,
                          parcels_after.vma_to_lda(0x4002cd))
        self.assertEquals(gnu_hash_lda_before + 1,
                          parcels_after.vma_to_lda(0x4002ce))
        self.assertEquals(False, p1.is_code)
        self.assertEquals(False, p3.is_code)

        # these changed
        self.assertEquals(gnu_hash_lda_before + 2,
                          parcels_after.vma_to_lda(0x4002d0))
        self.assertEquals(gnu_hash_lda_before + 3,
                          parcels_after.vma_to_lda(0x4002d2))

        self.assertEquals(gnu_hash_lda_before + 4,
                          parcels_after.vma_to_lda(0x4002d5))
        self.assertEquals(gnu_hash_lda_before + 5,
                          parcels_after.vma_to_lda(0x4002d6))
        self.assertEquals(gnu_hash_lda_before + 6,
                          parcels_after.vma_to_lda(0x4002d7))
        self.assertEquals(gnu_hash_lda_before + 7,
                          parcels_after.vma_to_lda(0x4002d8))
        self.assertEquals(gnu_hash_lda_before + 8,
                          parcels_after.vma_to_lda(0x4002d9))
        self.assertEquals(gnu_hash_lda_before + 9,
                          parcels_after.vma_to_lda(0x4002da))
        self.assertEquals(gnu_hash_lda_before + 10,
                          parcels_after.vma_to_lda(0x4002db))
        self.assertEquals(True, p2.is_code)

        # 7 bytes turned into 3 instructions, so it shifted the following parcels by -4 ldas
        self.assertEquals(dynsym_lda_before - 4,
                          parcels_after.vma_to_lda(0x4002e0))
        self.assertEquals(dynstr_lda_before - 4,
                          parcels_after.vma_to_lda(0x400a18))

        #
        # make sure lda->vma mappings line up
        #

        # these shouldn't have changed
        self.assertEquals(0x4002cd,
                          parcels_after.lda_to_vma(gnu_hash_lda_before))
        self.assertEquals(0x4002ce,
                          parcels_after.lda_to_vma(gnu_hash_lda_before + 1))

        # these changed
        self.assertEquals(0x4002d0,
                          parcels_after.lda_to_vma(gnu_hash_lda_before + 2))
        self.assertEquals(0x4002d2,
                          parcels_after.lda_to_vma(gnu_hash_lda_before + 3))

        self.assertEquals(0x4002d5,
                          parcels_after.lda_to_vma(gnu_hash_lda_before + 4))
        self.assertEquals(0x4002d6,
                          parcels_after.lda_to_vma(gnu_hash_lda_before + 5))
        self.assertEquals(0x4002d7,
                          parcels_after.lda_to_vma(gnu_hash_lda_before + 6))
        self.assertEquals(0x4002d8,
                          parcels_after.lda_to_vma(gnu_hash_lda_before + 7))
        self.assertEquals(0x4002d9,
                          parcels_after.lda_to_vma(gnu_hash_lda_before + 8))
        self.assertEquals(0x4002da,
                          parcels_after.lda_to_vma(gnu_hash_lda_before + 9))
        self.assertEquals(0x4002db,
                          parcels_after.lda_to_vma(gnu_hash_lda_before + 10))

        # 7 bytes turned into 3 instructions, so it shifted the following parcels by -4 ldas
        self.assertEquals(0x4002e0,
                          parcels_after.lda_to_vma(dynsym_lda_before - 4))
        self.assertEquals(0x400a18,
                          parcels_after.lda_to_vma(dynstr_lda_before - 4))