Ejemplo n.º 1
0
    def __init__(self, **kwargs):
        """This method initialize the class members
        """
        self._raw = kwargs[cs.UI_ARG_RAW]
        self._verbosity = kwargs[cs.UI_ARG_VERBOSITY_COUNT]
        self._out_file = kwargs[cs.UI_ARG_OUT]
        self._printer = DataPrinter(self._verbosity, self._out_file)
        self._warning_counter = 0
        self._error_counter = 0
        self._notice_counter = 0

        if not self._validate_file_path(kwargs[cs.UI_ARG_DUMP_FILE]):
            raise Exception("No such file '{0}'".format(
                kwargs[cs.UI_ARG_DUMP_FILE]))

        if not self._validate_file_path(kwargs[cs.UI_ARG_ADB_FILE]):
            raise Exception("No such file '{0}'".format(
                kwargs[cs.UI_ARG_ADB_FILE]))

        try:
            self._dumped_segment_db = self._retrieve_dumped_segment_db(
                kwargs[cs.UI_ARG_DUMP_FILE])
        except Exception as _:
            raise Exception("Fail to generate segment db from raw data")

        try:
            self._segment_map = self._retrieve_segment_map(
                kwargs[cs.UI_ARG_ADB_FILE])
        except Exception as _:
            raise Exception("Fail to generate segment map db from adb file")

        self._validate_adb_version_with_notice(kwargs[cs.UI_ARG_ADB_FILE])
Ejemplo n.º 2
0
 def print_data(self):
     """call the data printer with the right configuration for print the menu to screen.
     """
     DataPrinter.print_query_data(self.data)
Ejemplo n.º 3
0
class Parser:
    """This class responsible for parsing the segments according the given raw data
    and adb segments by organize the given inputs and print them after the parsing
    posses.
    """
    def __init__(self, **kwargs):
        """This method initialize the class members
        """
        self._raw = kwargs[cs.UI_ARG_RAW]
        self._verbosity = kwargs[cs.UI_ARG_VERBOSITY_COUNT]
        self._out_file = kwargs[cs.UI_ARG_OUT]
        self._printer = DataPrinter(self._verbosity, self._out_file)
        self._warning_counter = 0
        self._error_counter = 0
        self._notice_counter = 0

        if not self._validate_file_path(kwargs[cs.UI_ARG_DUMP_FILE]):
            raise Exception("No such file '{0}'".format(
                kwargs[cs.UI_ARG_DUMP_FILE]))

        if not self._validate_file_path(kwargs[cs.UI_ARG_ADB_FILE]):
            raise Exception("No such file '{0}'".format(
                kwargs[cs.UI_ARG_ADB_FILE]))

        try:
            self._dumped_segment_db = self._retrieve_dumped_segment_db(
                kwargs[cs.UI_ARG_DUMP_FILE])
        except Exception as _:
            raise Exception("Fail to generate segment db from raw data")

        try:
            self._segment_map = self._retrieve_segment_map(
                kwargs[cs.UI_ARG_ADB_FILE])
        except Exception as _:
            raise Exception("Fail to generate segment map db from adb file")

        self._validate_adb_version_with_notice(kwargs[cs.UI_ARG_ADB_FILE])

    @classmethod
    def _validate_file_path(cls, path):
        return os.path.exists(path)

    def _get_next_warning_counter(self):
        """This method return get the next free index to help generate
         the a uniq key for the warning msg.
        """
        returned_value = self._warning_counter
        self._warning_counter += 1
        return returned_value

    def _get_next_error_counter(self):
        """This method return get the next free index to help generate
        the a uniq key for the error msg.
        """
        returned_value = self._error_counter
        self._error_counter += 1
        return returned_value

    def _get_next_notice_counter(self):
        """This method return get the next free index to help generate
        the a uniq key for the notice msg.
        """
        returned_value = self._notice_counter
        self._notice_counter += 1
        return returned_value

    def parse(self):
        """This method parse the segment according the the dumped input file and the
        current configurations.
        """
        # parse all segments
        seg_parsed_counter = 0
        try:
            for seg in self._dumped_segment_db:
                seg_parsed_counter += 1
                self._parse_segment(seg)
        except Exception as _:
            raise Exception(
                "Fail to parse segments, failure occur at segment number {}".
                format(seg_parsed_counter))

        # print all segments
        self._printer.print_parsed_segment(self._dumped_segment_db)

    def _is_seg_size_match_adb_seg_size(self, seg_size, seg_type):
        """This method check if the dumped segment size is same like in the ADB definitions.
        """
        adb_seg_size = math.ceil(self._segment_map[seg_type].size / 32)
        dumped_data_size = seg_size
        # first 4 dw's at the resource segment is not a part of the ADB definition so we not consider them
        if self._is_resource_segment(seg_type):
            dumped_data_size -= cs.RESOURCE_SEGMENT_START_OFFSET_IN_DW
        if adb_seg_size != dumped_data_size:
            return False
        return True

    def _parse_segment(self, seg):
        """This method responsible for the parsing algorithm that take the raw data of
        each segment and generate his content according the adb map
        """
        seg_for_parse = False
        if seg.get_type() in self._segment_map:
            segment_name = self._build_union_prefix(
                self._segment_map[seg.get_type(
                )].nodeDesc) + self._segment_map[seg.get_type()].name
            if not self._is_seg_size_match_adb_seg_size(
                    len(seg.get_data()), seg.get_type()):
                seg.add_parsed_data(
                    "Warning[{}]".format(self._get_next_warning_counter()),
                    cs.WARNING_SIZE_DOESNT_MATCH.format(
                        len(seg.get_data()) -
                        cs.RESOURCE_SEGMENT_START_OFFSET_IN_DW,
                        math.ceil(self._segment_map[seg.get_type()].size /
                                  32)))
            seg_for_parse = True
        else:
            # reference segment is missing in the adb (this will help the user understand the type)
            if seg.get_type() == cs.RESOURCE_DUMP_SEGMENT_TYPE_REFERENCE:
                segment_name = "segment_reference"
            else:
                segment_name = "UNKNOWN"

        seg.add_parsed_data(20 * " " + "Segment",
                            "{0} ({1})".format(segment_name, seg.get_type()))

        # in case of a resource segment the offset of the adb doesnt consider the header so we need to pass it
        if self._is_resource_segment(seg.get_type()):
            data_start_position = cs.RESOURCE_SEGMENT_START_OFFSET_IN_BYTES
        else:
            data_start_position = 0

        # offset is in bits so converting is to bit array and store it at a member is better then pass it as an argument
        # for a recursive method
        bytes_array = seg.get_data_in_bytes()[data_start_position:]
        self._current_bit_array = ''.join(
            format(x, '08b') for x in bytes_array)

        if seg_for_parse:
            for field in self._segment_map[seg.get_type()].subItems:
                prefix = self._build_union_prefix(field.nodeDesc)
                self._parse_seg_field(field, prefix + field.name, seg)
            if self._raw:
                self._build_and_add_raw_data(seg)
        else:  # if segment not for parse, need to set raw data
            self._build_and_add_raw_data(seg)

    def _parse_seg_field(self, field, field_str, seg):
        """This method is a recursive method that build the inner fields
        """
        if len(field.subItems) > 0:
            for sub_field in field.subItems:
                prefix = self._build_union_prefix(sub_field.nodeDesc)
                self._parse_seg_field(
                    sub_field, field_str + "." + prefix + sub_field.name, seg)
        else:
            field_offset = self._calculate_aligned_to_dword_offset(
                field.offset, field.size)
            if len(self._current_bit_array) >= (field_offset + field.size):
                seg.add_parsed_data(
                    field_str,
                    hex(
                        int(
                            self._current_bit_array[field_offset:field_offset +
                                                    field.size], 2)))

    @classmethod
    def _build_and_add_raw_data(cls, seg):
        """This method build the raw data in the right format and add it to the
        parsed data of the segment.
        """
        hex_list = []
        line_counter = 0
        dw_counter = 0
        seg.add_parsed_data("RAW DATA", "")

        for dw in seg.get_data():
            hex_list.append('0x{0:0{1}X} '.format(dw, 8))
            dw_counter += 1

            if (dw_counter % cs.PARSER_NUM_OF_DW_IN_ROW) == 0:
                seg.add_parsed_data(
                    "{:<15}".format("DWORD [{0}-{1}]".format(
                        line_counter * 4,
                        (line_counter * 4) + (len(hex_list) - 1))),
                    ''.join(hex_list[:]))
                line_counter += 1
                hex_list.clear()

        if len(hex_list) > 1:
            seg.add_parsed_data(
                "{:<15}".format("DWORD [{0}-{1}]".format(
                    line_counter * 4,
                    (line_counter * 4) + (len(hex_list) - 1))),
                ''.join(hex_list[:]))
        elif len(hex_list) is 1:
            seg.add_parsed_data(
                "{:<15}".format("DWORD [{0}]".format(line_counter * 4)),
                ''.join(hex_list[:]))

    @classmethod
    def _calculate_aligned_to_dword_offset(cls, offset, size):
        """This method calculate the new offset inside the dword
        since the data inside has a different bit index
        """
        calculated_offset = offset
        if size < 32:
            calculated_offset = (int(offset / 32) * 32) + 32 - size - (offset %
                                                                       32)
        return calculated_offset

    @classmethod
    def _is_resource_segment(cls, seg_type):
        """This method check if the seg type is resource segment
        """
        return cs.RESOURCE_DUMP_SEGMENT_TYPE_RESOURCE_MAX >= seg_type >= cs.RESOURCE_DUMP_SEGMENT_TYPE_RESOURCE_MIN

    @classmethod
    def _retrieve_dumped_segment_db(cls, dumped_file_path):
        """This method get the segment list generated from the dumped input file by calling
        the RawData class.
        """
        return RawData(dumped_file_path).to_segments()

    @classmethod
    def _build_union_prefix(cls, node_desc):
        """This method build the prefix for the struct in order to give the user
        information indicate if the node is a union
        """
        if not node_desc:
            prefix = ""
        else:
            if node_desc.isUnion:
                prefix = "(UNION)"
            else:
                prefix = ""
        return prefix

    def _retrieve_segment_map(self, adb_file_path):
        """This method get the segment map from the adbdata class.
        """
        self._adb_obj = AdbParser(adb_file_path)
        return self._adb_obj.segment_id_nodes_dict

    def _retrieve_adb_version_from_info_segment(self):
        """This method locate the info segment and if found, return the
        fw_version at the format XX.XX.XXXX, otherwise return empty string.
        """
        for seg in self._dumped_segment_db:
            if str(seg.get_type()).upper(
            ) == cs.RESOURCE_DUMP_SEGMENT_TYPE_INFO.upper():
                return seg.get_version()
        return ""

    def _validate_adb_version_with_notice(self, adb_file_path):
        """This method perform the fw version validation stages and send a proper notice msg
        if needed.
        """
        adb_name = os.path.splitext(os.path.basename(adb_file_path))[0]
        if self._is_adb_name_in_version_format(adb_name):
            adb_fw_version = self._retrieve_adb_version_from_valid_format(
                adb_name)
            dump_fw_version = self._retrieve_adb_version_from_info_segment()
            if adb_fw_version != dump_fw_version:
                self._get_next_notice_counter()
                self._printer.print_notice_before_parse("Notice - {0}".format(
                    "adb fw version {0} is used for parsing while dump fw version is {1}"
                    .format(adb_fw_version, dump_fw_version)))
        else:
            self._get_next_notice_counter()
            self._printer.print_notice_before_parse("Notice - {0}".format(
                "Adb name is not according the version format, can't validate adb fw version"
            ))

    @classmethod
    def _retrieve_adb_version_from_valid_format(cls, adb_name):
        """This method extract the fw_version from the file name assuming that
        file already checked and has the right version format.
        """
        version = re.findall("[0-9]{2}_[0-9]{2}_[0-9]{4}", adb_name)[0]
        version_list = version.split("_")
        return "{0}.{1}.{2}".format(version_list[0], version_list[1],
                                    version_list[2])

    @classmethod
    def _is_adb_name_in_version_format(cls, adb_name):
        """This method check if the adb file name has the version format define by arch.
        """
        match = re.fullmatch("fw-[0-9]+-rel-[0-9]{2}_[0-9]{2}_[0-9]{4}",
                             adb_name)
        if match is not None:
            if match.string == adb_name:
                return True
        return False
Ejemplo n.º 4
0
 def print_data(self):
     """call the data printer with the right configuration for print the dump data to
     screen or in a binary format (choosed by the user).
     """
     DataPrinter.print_dump_data(self.data, self.bin)