diff --git a/bin/Darwin/X86_64/gettype b/bin/Darwin/X86_64/gettype deleted file mode 100755 index 53e4bc8..0000000 Binary files a/bin/Darwin/X86_64/gettype and /dev/null differ diff --git a/bin/Darwin/X86_64/lpunpack.py b/bin/Darwin/X86_64/lpunpack.py deleted file mode 100755 index fbe1985..0000000 --- a/bin/Darwin/X86_64/lpunpack.py +++ /dev/null @@ -1,951 +0,0 @@ -import argparse -import copy -import enum -import io -import json -import re -import struct -import sys -from dataclasses import dataclass, field -import os -from string import Template -from typing import IO, Dict, List, TypeVar, cast, BinaryIO, Tuple -from timeit import default_timer as dti -from argparse import ArgumentParser - -SPARSE_HEADER_MAGIC = 0xED26FF3A -SPARSE_HEADER_SIZE = 28 -SPARSE_CHUNK_HEADER_SIZE = 12 - -LP_PARTITION_RESERVED_BYTES = 4096 -LP_METADATA_GEOMETRY_MAGIC = 0x616c4467 -LP_METADATA_GEOMETRY_SIZE = 4096 -LP_METADATA_HEADER_MAGIC = 0x414C5030 -LP_SECTOR_SIZE = 512 - -LP_TARGET_TYPE_LINEAR = 0 -LP_TARGET_TYPE_ZERO = 1 - -LP_PARTITION_ATTR_READONLY = (1 << 0) -LP_PARTITION_ATTR_SLOT_SUFFIXED = (1 << 1) -LP_PARTITION_ATTR_UPDATED = (1 << 2) -LP_PARTITION_ATTR_DISABLED = (1 << 3) - -LP_BLOCK_DEVICE_SLOT_SUFFIXED = (1 << 0) - -LP_GROUP_SLOT_SUFFIXED = (1 << 0) - -PLAIN_TEXT_TEMPLATE = """Slot 0: -Metadata version: $metadata_version -Metadata size: $metadata_size bytes -Metadata max size: $metadata_max_size bytes -Metadata slot count: $metadata_slot_count -Header flags: $header_flags -Partition table: ------------------------- -$partitions ------------------------- -Super partition layout: ------------------------- -$layouts ------------------------- -Block device table: ------------------------- -$blocks ------------------------- -Group table: ------------------------- -$groups -""" - - -def build_attribute_string(attributes: int) -> str: - if attributes & LP_PARTITION_ATTR_READONLY: - result = "readonly" - elif attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED: - result = "slot-suffixed" - elif attributes & LP_PARTITION_ATTR_UPDATED: - result = "updated" - elif attributes & LP_PARTITION_ATTR_DISABLED: - result = "disabled" - else: - result = "none" - return result - - -def build_block_device_flag_string(flags: int) -> str: - return "slot-suffixed" if (flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED) else "none" - - -def build_group_flag_string(flags: int) -> str: - return "slot-suffixed" if (flags & LP_GROUP_SLOT_SUFFIXED) else "none" - - -class FormatType(enum.Enum): - TEXT = "text" - JSON = "json" - - -class EnumAction(argparse.Action): - """Argparse action for handling Enums""" - - def __init__(self, **kwargs): - enum_type = kwargs.pop("type", None) - if enum_type is None: - raise ValueError("Type must be assigned an Enum when using EnumAction") - - if not issubclass(enum_type, enum.Enum): - raise TypeError("Type must be an Enum when using EnumAction") - - kwargs.setdefault("choices", tuple(e.value for e in enum_type)) - - super(EnumAction, self).__init__(**kwargs) - self._enum = enum_type - - def __call__(self, parser, namespace, values, option_string=None): - value = self._enum(values) - setattr(namespace, self.dest, value) - - -class ShowJsonInfo(json.JSONEncoder): - def __init__(self, ignore_keys: List[str], **kwargs): - super().__init__(**kwargs) - self._ignore_keys = ignore_keys - - def _remove_ignore_keys(self, data: Dict): - _data = copy.deepcopy(data) - for field_key, v in data.items(): - if field_key in self._ignore_keys: - _data.pop(field_key) - continue - - if v == 0: - _data.pop(field_key) - continue - - if isinstance(v, int) and not isinstance(v, bool): - _data.update({field_key: str(v)}) - return _data - - def encode(self, data: Dict) -> str: - result = { - "partitions": list(map(self._remove_ignore_keys, data["partition_table"])), - "groups": list(map(self._remove_ignore_keys, data["group_table"])), - "block_devices": list(map(self._remove_ignore_keys, data["block_devices"])) - } - return super().encode(result) - - -class SparseHeader(object): - def __init__(self, buffer): - fmt = ' raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */ - self.reserved, - self.chunk_sz, # in blocks in output image * / - self.total_sz, # in bytes of chunk input file including chunk header and data * / - ) = struct.unpack(fmt, buffer[0:struct.calcsize(fmt)]) - - -class LpMetadataBase: - _fmt = None - - @classmethod - @property - def size(cls) -> int: - return struct.calcsize(cls._fmt) - - -class LpMetadataGeometry(LpMetadataBase): - """ - Offset 0: Magic signature - - Offset 4: Size of the `LpMetadataGeometry` - - Offset 8: SHA256 checksum - - Offset 40: Maximum amount of space a single copy of the metadata can use - - Offset 44: Number of copies of the metadata to keep - - Offset 48: Logical block size - """ - - _fmt = '<2I32s3I' - - def __init__(self, buffer): - ( - self.magic, - self.struct_size, - self.checksum, - self.metadata_max_size, - self.metadata_slot_count, - self.logical_block_size - - ) = struct.unpack(self._fmt, buffer[0:struct.calcsize(self._fmt)]) - # self.size - - -class LpMetadataTableDescriptor(LpMetadataBase): - """ - Offset 0: Location of the table, relative to end of the metadata header. - - Offset 4: Number of entries in the table. - - Offset 8: Size of each entry in the table, in bytes. - """ - - _fmt = '<3I' - - def __init__(self, buffer): - ( - self.offset, - self.num_entries, - self.entry_size - - ) = struct.unpack(self._fmt, buffer[:struct.calcsize(self._fmt)]) - - -class LpMetadataPartition(LpMetadataBase): - """ - Offset 0: Name of this partition in ASCII characters. Any unused characters in - the buffer must be set to 0. Characters may only be alphanumeric or _. - The name must include at least one ASCII character, and it must be unique - across all partition names. The length (36) is the same as the maximum - length of a GPT partition name. - - Offset 36: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). - - Offset 40: Index of the first extent owned by this partition. The extent will - start at logical sector 0. Gaps between extents are not allowed. - - Offset 44: Number of extents in the partition. Every partition must have at least one extent. - - Offset 48: Group this partition belongs to. - """ - - _fmt = '<36s4I' - - def __init__(self, buffer): - ( - self.name, - self.attributes, - self.first_extent_index, - self.num_extents, - self.group_index - - ) = struct.unpack(self._fmt, buffer[0:struct.calcsize(self._fmt)]) - - self.name = self.name.decode("utf-8").strip('\x00') - - @property - def filename(self) -> str: - return f'{self.name}.img' - - -class LpMetadataExtent(LpMetadataBase): - """ - Offset 0: Length of this extent, in 512-byte sectors. - - Offset 8: Target type for device-mapper (see LP_TARGET_TYPE_* values). - - Offset 12: Contents depends on target_type. LINEAR: The sector on the physical partition that this extent maps onto. - ZERO: This field must be 0. - - Offset 20: Contents depends on target_type. LINEAR: Must be an index into the block devices table. - """ - - _fmt = ' Dict: - return self._get_info() - - @property - def metadata_region(self) -> int: - if self.geometry is None: - return 0 - - return LP_PARTITION_RESERVED_BYTES + ( - LP_METADATA_GEOMETRY_SIZE + self.geometry.metadata_max_size * self.geometry.metadata_slot_count - ) * 2 - - def _get_extents_string(self, partition: LpMetadataPartition) -> List[str]: - result = [] - first_sector = 0 - for extent_number in range(partition.num_extents): - index = partition.first_extent_index + extent_number - extent = self.extents[index] - - _base = f"{first_sector} .. {first_sector + extent.num_sectors - 1}" - first_sector += extent.num_sectors - - if extent.target_type == LP_TARGET_TYPE_LINEAR: - result.append( - f"{_base} linear {self.block_devices[extent.target_source].partition_name} {extent.target_data}" - ) - elif extent.target_type == LP_TARGET_TYPE_ZERO: - result.append(f"{_base} zero") - - return result - - def _get_partition_layout(self) -> List[str]: - result = [] - - for partition in self.partitions: - for extent_number in range(partition.num_extents): - index = partition.first_extent_index + extent_number - extent = self.extents[index] - - block_device_name = "" - - if extent.target_type == LP_TARGET_TYPE_LINEAR: - block_device_name = self.block_devices[extent.target_source].partition_name - - result.append( - f"{block_device_name}: {extent.target_data} .. {extent.target_data + extent.num_sectors}: " - f"{partition.name} ({extent.num_sectors} sectors)" - ) - - return result - - def get_offsets(self, slot_number: int = 0) -> List[int]: - base = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) - _tmp_offset = self.geometry.metadata_max_size * slot_number - primary_offset = base + _tmp_offset - backup_offset = base + self.geometry.metadata_max_size * self.geometry.metadata_slot_count + _tmp_offset - return [primary_offset, backup_offset] - - def _get_info(self) -> Dict: - # TODO 25.01.2023: Liblp version 1.2 build_header_flag_string check header version 1.2 - result = {} - try: - result = { - "metadata_version": f"{self.header.major_version}.{self.header.minor_version}", - "metadata_size": self.header.header_size + self.header.tables_size, - "metadata_max_size": self.geometry.metadata_max_size, - "metadata_slot_count": self.geometry.metadata_slot_count, - "header_flags": "none", - "block_devices": [ - { - "name": item.partition_name, - "first_sector": item.first_logical_sector, - "size": item.block_device_size, - "block_size": self.geometry.logical_block_size, - "flags": build_block_device_flag_string(item.flags), - "alignment": item.alignment, - "alignment_offset": item.alignment_offset - } for item in self.block_devices - ], - "group_table": [ - { - "name": self.groups[index].name, - "maximum_size": self.groups[index].maximum_size, - "flags": build_group_flag_string(self.groups[index].flags) - } for index in range(0, self.header.groups.num_entries) - ], - "partition_table": [ - { - "name": item.name, - "group_name": self.groups[item.group_index].name, - "is_dynamic": True, - "size": self.extents[item.first_extent_index].num_sectors * LP_SECTOR_SIZE, - "attributes": build_attribute_string(item.attributes), - "extents": self._get_extents_string(item) - } for item in self.partitions - ], - "partition_layout": self._get_partition_layout() - } - except Exception: - pass - finally: - return result - - def to_json(self) -> str: - data = self._get_info() - if not data: - return "" - - return json.dumps( - data, - indent=1, - cls=ShowJsonInfo, - ignore_keys=[ - 'metadata_version', 'metadata_size', 'metadata_max_size', 'metadata_slot_count', 'header_flags', - 'partition_layout', - 'attributes', 'extents', 'flags', 'first_sector' - ]) - - def __str__(self): - data = self._get_info() - if not data: - return "" - - template = Template(PLAIN_TEXT_TEMPLATE) - layouts = "\n".join(data["partition_layout"]) - partitions = "------------------------\n".join( - [ - " Name: {}\n Group: {}\n Attributes: {}\n Extents:\n {}\n".format( - item["name"], - item["group_name"], - item["attributes"], - "\n".join(item["extents"]) - ) for item in data["partition_table"] - ] - )[:-1] - blocks = "\n".join( - [ - " Partition name: {}\n First sector: {}\n Size: {} bytes\n Flags: {}".format( - item["name"], - item["first_sector"], - item["size"], - item["flags"] - ) - for item in data["block_devices"] - ] - ) - groups = "------------------------\n".join( - [ - " Name: {}\n Maximum size: {} bytes\n Flags: {}\n".format( - item["name"], - item["maximum_size"], - item["flags"] - ) for item in data["group_table"] - ] - )[:-1] - return template.substitute(partitions=partitions, layouts=layouts, blocks=blocks, groups=groups, **data) - - -class LpUnpackError(Exception): - """Raised any error unpacking""" - - def __init__(self, message): - self.message = message - - def __str__(self): - return self.message - - -@dataclass -class UnpackJob: - name: str - geometry: LpMetadataGeometry - parts: List[Tuple[int, int]] = field(default_factory=list) - total_size: int = field(default=0) - - -class SparseImage: - def __init__(self, fd): - self._fd = fd - self.header = None - - def check(self): - self._fd.seek(0) - self.header = SparseHeader(self._fd.read(SPARSE_HEADER_SIZE)) - return False if self.header.magic != SPARSE_HEADER_MAGIC else True - - def _read_data(self, chunk_data_size: int): - if self.header.chunk_hdr_sz > SPARSE_CHUNK_HEADER_SIZE: - self._fd.seek(self.header.chunk_hdr_sz - SPARSE_CHUNK_HEADER_SIZE, 1) - - return self._fd.read(chunk_data_size) - - def unsparse(self): - if not self.header: - self._fd.seek(0) - self.header = SparseHeader(self._fd.read(SPARSE_HEADER_SIZE)) - chunks = self.header.total_chunks - self._fd.seek(self.header.file_hdr_sz - SPARSE_HEADER_SIZE, 1) - unsparse_file_dir = os.path.dirname(self._fd.name) - unsparse_file = os.path.join(unsparse_file_dir, - "{}.unsparse.img".format(os.path.splitext(os.path.basename(self._fd.name))[0])) - with open(str(unsparse_file), 'wb') as out: - sector_base = 82528 - output_len = 0 - while chunks > 0: - chunk_header = SparseChunkHeader(self._fd.read(SPARSE_CHUNK_HEADER_SIZE)) - sector_size = (chunk_header.chunk_sz * self.header.blk_sz) >> 9 - chunk_data_size = chunk_header.total_sz - self.header.chunk_hdr_sz - if chunk_header.chunk_type == 0xCAC1: - data = self._read_data(chunk_data_size) - len_data = len(data) - if len_data == (sector_size << 9): - out.write(data) - output_len += len_data - sector_base += sector_size - else: - if chunk_header.chunk_type == 0xCAC2: - data = self._read_data(chunk_data_size) - len_data = sector_size << 9 - out.write(struct.pack("B", 0) * len_data) - output_len += len(data) - sector_base += sector_size - else: - if chunk_header.chunk_type == 0xCAC3: - data = self._read_data(chunk_data_size) - len_data = sector_size << 9 - out.write(struct.pack("B", 0) * len_data) - output_len += len(data) - sector_base += sector_size - else: - len_data = sector_size << 9 - out.write(struct.pack("B", 0) * len_data) - sector_base += sector_size - chunks -= 1 - return unsparse_file - - -T = TypeVar('T') - - -class LpUnpack(object): - def __init__(self, **kwargs): - self._partition_name = kwargs.get('NAME') - self._show_info = kwargs.get('SHOW_INFO', True) - self._show_info_format = kwargs.get('SHOW_INFO_FORMAT', FormatType.TEXT) - self._config = kwargs.get('CONFIG', None) - self._slot_num = None - self._fd: BinaryIO = open(kwargs.get('SUPER_IMAGE'), 'rb') - self._out_dir = kwargs.get('OUTPUT_DIR', None) - - def _check_out_dir_exists(self): - if self._out_dir is None: - return - - if not os.path.exists(self._out_dir): - os.makedirs(self._out_dir, exist_ok=True) - - def _extract_partition(self, unpack_job: UnpackJob): - self._check_out_dir_exists() - start = dti() - print(f'Extracting partition [{unpack_job.name}]') - out_file = os.path.join(self._out_dir, f'{unpack_job.name}.img') - with open(str(out_file), 'wb') as out: - for part in unpack_job.parts: - offset, size = part - self._write_extent_to_file(out, offset, size, unpack_job.geometry.logical_block_size) - - print('Done:[%s]' % (dti() - start)) - - def _extract(self, partition, metadata): - unpack_job = UnpackJob(name=partition.name, geometry=metadata.geometry) - - if partition.num_extents != 0: - for extent_number in range(partition.num_extents): - index = partition.first_extent_index + extent_number - extent = metadata.extents[index] - - if extent.target_type != LP_TARGET_TYPE_LINEAR: - raise LpUnpackError(f'Unsupported target type in extent: {extent.target_type}') - - offset = extent.target_data * LP_SECTOR_SIZE - size = extent.num_sectors * LP_SECTOR_SIZE - unpack_job.parts.append((offset, size)) - unpack_job.total_size += size - - self._extract_partition(unpack_job) - - def _get_data(self, count: int, size: int, clazz: T) -> List[T]: - result = [] - while count > 0: - result.append(clazz(self._fd.read(size))) - count -= 1 - return result - - def _read_chunk(self, block_size): - while True: - data = self._fd.read(block_size) - if not data: - break - yield data - - def _read_metadata_header(self, metadata: Metadata): - offsets = metadata.get_offsets() - for index, offset in enumerate(offsets): - self._fd.seek(offset, io.SEEK_SET) - header = LpMetadataHeader(self._fd.read(80)) - header.partitions = LpMetadataTableDescriptor(self._fd.read(12)) - header.extents = LpMetadataTableDescriptor(self._fd.read(12)) - header.groups = LpMetadataTableDescriptor(self._fd.read(12)) - header.block_devices = LpMetadataTableDescriptor(self._fd.read(12)) - - if header.magic != LP_METADATA_HEADER_MAGIC: - check_index = index + 1 - if check_index > len(offsets): - raise LpUnpackError('Logical partition metadata has invalid magic value.') - else: - print(f'Read Backup header by offset 0x{offsets[check_index]:x}') - continue - - metadata.header = header - self._fd.seek(offset + header.header_size, io.SEEK_SET) - - def _read_metadata(self): - self._fd.seek(LP_PARTITION_RESERVED_BYTES, io.SEEK_SET) - metadata = Metadata(geometry=self._read_primary_geometry()) - - if metadata.geometry.magic != LP_METADATA_GEOMETRY_MAGIC: - raise LpUnpackError('Logical partition metadata has invalid geometry magic signature.') - - if metadata.geometry.metadata_slot_count == 0: - raise LpUnpackError('Logical partition metadata has invalid slot count.') - - if metadata.geometry.metadata_max_size % LP_SECTOR_SIZE != 0: - raise LpUnpackError('Metadata max size is not sector-aligned.') - - self._read_metadata_header(metadata) - - metadata.partitions = self._get_data( - metadata.header.partitions.num_entries, - metadata.header.partitions.entry_size, - LpMetadataPartition - ) - - metadata.extents = self._get_data( - metadata.header.extents.num_entries, - metadata.header.extents.entry_size, - LpMetadataExtent - ) - - metadata.groups = self._get_data( - metadata.header.groups.num_entries, - metadata.header.groups.entry_size, - LpMetadataPartitionGroup - ) - - metadata.block_devices = self._get_data( - metadata.header.block_devices.num_entries, - metadata.header.block_devices.entry_size, - LpMetadataBlockDevice - ) - - try: - super_device: LpMetadataBlockDevice = cast(LpMetadataBlockDevice, iter(metadata.block_devices).__next__()) - if metadata.metadata_region > super_device.first_logical_sector * LP_SECTOR_SIZE: - raise LpUnpackError('Logical partition metadata overlaps with logical partition contents.') - except StopIteration: - raise LpUnpackError('Metadata does not specify a super device.') - - return metadata - - def _read_primary_geometry(self) -> LpMetadataGeometry: - geometry = LpMetadataGeometry(self._fd.read(LP_METADATA_GEOMETRY_SIZE)) - if geometry is not None: - return geometry - else: - return LpMetadataGeometry(self._fd.read(LP_METADATA_GEOMETRY_SIZE)) - - def _write_extent_to_file(self, fd: IO, offset: int, size: int, block_size: int): - self._fd.seek(offset) - for block in self._read_chunk(block_size): - if size == 0: - break - - fd.write(block) - - size -= block_size - - def unpack(self): - try: - if SparseImage(self._fd).check(): - print('Sparse image detected.') - print('Process conversion to non sparse image...') - unsparse_file = SparseImage(self._fd).unsparse() - self._fd.close() - self._fd = open(str(unsparse_file), 'rb') - print('Result:[ok]') - - self._fd.seek(0) - metadata = self._read_metadata() - - if self._partition_name: - filter_partition = [] - for index, partition in enumerate(metadata.partitions): - if partition.name in self._partition_name: - filter_partition.append(partition) - - if not filter_partition: - raise LpUnpackError(f'Could not find partition: {self._partition_name}') - - metadata.partitions = filter_partition - - if self._slot_num: - if self._slot_num > metadata.geometry.metadata_slot_count: - raise LpUnpackError(f'Invalid metadata slot number: {self._slot_num}') - - if self._show_info: - if self._show_info_format == FormatType.TEXT: - print(metadata) - elif self._show_info_format == FormatType.JSON: - print(f"{metadata.to_json()}\n") - - if not self._show_info and self._out_dir is None: - raise LpUnpackError(message=f'Not specified directory for extraction') - - if self._out_dir: - for partition in metadata.partitions: - self._extract(partition, metadata) - - except LpUnpackError as e: - print(e.message) - sys.exit(1) - - finally: - self._fd.close() - - -def create_parser(): - _parser = argparse.ArgumentParser( - description=f'{os.path.basename(sys.argv[0])} - command-line tool for extracting partition images from super' - ) - _parser.add_argument( - '-p', - '--partition', - dest='NAME', - type=lambda x: re.split("\\w+", x), - help='Extract the named partition. This can be specified multiple times or through the delimiter ["," ":"]' - ) - _parser.add_argument( - '-S', - '--slot', - dest='NUM', - type=int, - help=' !!! No implementation yet !!! Slot number (default is 0).' - ) - - if sys.version_info >= (3, 9): - _parser.add_argument( - '--info', - dest='SHOW_INFO', - default=False, - action=argparse.BooleanOptionalAction, - help='Displays pretty-printed partition metadata' - ) - else: - _parser.add_argument( - '--info', - dest='SHOW_INFO', - action='store_true', - help='Displays pretty-printed partition metadata' - ) - _parser.add_argument( - '--no-info', - dest='SHOW_INFO', - action='store_false' - ) - _parser.set_defaults(SHOW_INFO=False) - - _parser.add_argument( - '-f', - '--format', - dest='SHOW_INFO_FORMAT', - type=FormatType, - action=EnumAction, - default=FormatType.TEXT, - help='Choice the format for printing info' - ) - _parser.add_argument('SUPER_IMAGE') - _parser.add_argument( - 'OUTPUT_DIR', - type=str, - nargs='?', - ) - return _parser - - -def unpack(file: str, out: str): - _parser = argparse.ArgumentParser() - _parser.add_argument('--SUPER_IMAGE', default=file) - _parser.add_argument('--OUTPUT_DIR', default=out) - _parser.add_argument('--SHOW_INFO', default=False) - namespace = _parser.parse_args() - if not os.path.exists(namespace.SUPER_IMAGE): - raise FileNotFoundError("%s Cannot Find" % namespace.SUPER_IMAGE) - else: - LpUnpack(**vars(namespace)).unpack() - - -def main(): - parser = create_parser() - namespace = parser.parse_args() - if len(sys.argv) >= 2: - if not os.path.exists(namespace.SUPER_IMAGE): - return 2 - LpUnpack(**vars(namespace)).unpack() - else: - return 1 - - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/bin/Linux/aarch64/gettype b/bin/Linux/aarch64/gettype deleted file mode 100755 index 029fd67..0000000 Binary files a/bin/Linux/aarch64/gettype and /dev/null differ diff --git a/bin/Linux/aarch64/vbmeta-disable-verification b/bin/Linux/aarch64/vbmeta-disable-verification deleted file mode 100755 index efdc19d..0000000 Binary files a/bin/Linux/aarch64/vbmeta-disable-verification and /dev/null differ diff --git a/bin/Linux/x86_64/gettype b/bin/Linux/x86_64/gettype deleted file mode 100755 index 0ac0c58..0000000 Binary files a/bin/Linux/x86_64/gettype and /dev/null differ diff --git a/bin/Linux/x86_64/imjtool b/bin/Linux/x86_64/imjtool deleted file mode 100755 index 652b6bf..0000000 Binary files a/bin/Linux/x86_64/imjtool and /dev/null differ diff --git a/bin/Linux/x86_64/lpunpack b/bin/Linux/x86_64/lpunpack deleted file mode 100755 index 6b0c14b..0000000 Binary files a/bin/Linux/x86_64/lpunpack and /dev/null differ diff --git a/bin/Linux/x86_64/sdat2img.py b/bin/Linux/x86_64/sdat2img.py deleted file mode 100755 index 71384f9..0000000 --- a/bin/Linux/x86_64/sdat2img.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -#==================================================== -# FILE: sdat2img.py -# AUTHORS: xpirt - luxi78 - howellzhu -# DATE: 2018-10-27 10:33:21 CEST -#==================================================== - -from __future__ import print_function -import sys, os, errno - -def main(TRANSFER_LIST_FILE, NEW_DATA_FILE, OUTPUT_IMAGE_FILE): - __version__ = '1.2' - - if sys.hexversion < 0x02070000: - print >> sys.stderr, "Python 2.7 or newer is required." - try: - input = raw_input - except NameError: pass - input('Press ENTER to exit...') - sys.exit(1) - else: - print('sdat2img binary - version: {}\n'.format(__version__)) - - def rangeset(src): - src_set = src.split(',') - num_set = [int(item) for item in src_set] - if len(num_set) != num_set[0]+1: - print('Error on parsing following data to rangeset:\n{}'.format(src), file=sys.stderr) - sys.exit(1) - - return tuple ([ (num_set[i], num_set[i+1]) for i in range(1, len(num_set), 2) ]) - - def parse_transfer_list_file(path): - trans_list = open(TRANSFER_LIST_FILE, 'r') - - # First line in transfer list is the version number - version = int(trans_list.readline()) - - # Second line in transfer list is the total number of blocks we expect to write - new_blocks = int(trans_list.readline()) - - if version >= 2: - # Third line is how many stash entries are needed simultaneously - trans_list.readline() - # Fourth line is the maximum number of blocks that will be stashed simultaneously - trans_list.readline() - - # Subsequent lines are all individual transfer commands - commands = [] - for line in trans_list: - line = line.split(' ') - cmd = line[0] - if cmd in ['erase', 'new', 'zero']: - commands.append([cmd, rangeset(line[1])]) - else: - # Skip lines starting with numbers, they are not commands anyway - if not cmd[0].isdigit(): - print('Command "{}" is not valid.'.format(cmd), file=sys.stderr) - trans_list.close() - sys.exit(1) - - trans_list.close() - return version, new_blocks, commands - - BLOCK_SIZE = 4096 - - version, new_blocks, commands = parse_transfer_list_file(TRANSFER_LIST_FILE) - - if version == 1: - print('Android Lollipop 5.0 detected!\n') - elif version == 2: - print('Android Lollipop 5.1 detected!\n') - elif version == 3: - print('Android Marshmallow 6.x detected!\n') - elif version == 4: - print('Android Nougat 7.x / Oreo 8.x detected!\n') - else: - print('Unknown Android version!\n') - - # Don't clobber existing files to avoid accidental data loss - try: - output_img = open(OUTPUT_IMAGE_FILE, 'wb') - except IOError as e: - if e.errno == errno.EEXIST: - print('Error: the output file "{}" already exists'.format(e.filename), file=sys.stderr) - print('Remove it, rename it, or choose a different file name.', file=sys.stderr) - sys.exit(e.errno) - else: - raise - - new_data_file = open(NEW_DATA_FILE, 'rb') - all_block_sets = [i for command in commands for i in command[1]] - max_file_size = max(pair[1] for pair in all_block_sets)*BLOCK_SIZE - - for command in commands: - if command[0] == 'new': - for block in command[1]: - begin = block[0] - end = block[1] - block_count = end - begin - print('Copying {} blocks into position {}...'.format(block_count, begin)) - - # Position output file - output_img.seek(begin*BLOCK_SIZE) - - # Copy one block at a time - while(block_count > 0): - output_img.write(new_data_file.read(BLOCK_SIZE)) - block_count -= 1 - else: - print('Skipping command {}...'.format(command[0])) - - # Make file larger if necessary - if(output_img.tell() < max_file_size): - output_img.truncate(max_file_size) - - output_img.close() - new_data_file.close() - print('Done! Output image: {}'.format(os.path.realpath(output_img.name))) - -if __name__ == '__main__': - try: - TRANSFER_LIST_FILE = str(sys.argv[1]) - NEW_DATA_FILE = str(sys.argv[2]) - except IndexError: - print('\nUsage: sdat2img.py [system_img]\n') - print(' : transfer list file') - print(' : system new dat file') - print(' [system_img]: output system image\n\n') - print('Visit xda thread for more information.\n') - try: - input = raw_input - except NameError: pass - input('Press ENTER to exit...') - sys.exit() - - try: - OUTPUT_IMAGE_FILE = str(sys.argv[3]) - except IndexError: - OUTPUT_IMAGE_FILE = 'system.img' - - main(TRANSFER_LIST_FILE, NEW_DATA_FILE, OUTPUT_IMAGE_FILE) diff --git a/bin/Linux/x86_64/vbmeta-disable-verification b/bin/Linux/x86_64/vbmeta-disable-verification deleted file mode 100755 index e74165c..0000000 Binary files a/bin/Linux/x86_64/vbmeta-disable-verification and /dev/null differ diff --git a/bin/gettype.py b/bin/gettype.py new file mode 100644 index 0000000..f7443cf --- /dev/null +++ b/bin/gettype.py @@ -0,0 +1,130 @@ +import struct +import sys +import os + +formats = ([b'PK', "zip"], [b'OPPOENCRYPT!', "ozip"], [b'7z', "7z"], [b'\x53\xef', 'ext', 1080], + [b'\x3a\xff\x26\xed', "sparse"], [b'\xe2\xe1\xf5\xe0', "erofs", 1024], [b"CrAU", "payload"], + [b"AVB0", "vbmeta"], [b'\xd7\xb7\xab\x1e', "dtbo"], + [b'\xd0\x0d\xfe\xed', "dtb"], [b"MZ", "exe"], [b".ELF", 'elf'], + [b"ANDROID!", "boot"], [b"VNDRBOOT", "vendor_boot"], + [b'AVBf', "avb_foot"], [b'BZh', "bzip2"], + [b'CHROMEOS', 'chrome'], [b'\x1f\x8b', "gzip"], + [b'\x1f\x9e', "gzip"], [b'\x02\x21\x4c\x18', "lz4_legacy"], + [b'\x03\x21\x4c\x18', 'lz4'], [b'\x04\x22\x4d\x18', 'lz4'], + [b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\x03', "zopfli"], [b'\xfd7zXZ', 'xz'], + [b']\x00\x00\x00\x04\xff\xff\xff\xff\xff\xff\xff\xff', 'lzma'], [b'\x02!L\x18', 'lz4_lg'], + [b'\x89PNG', 'png'], [b"LOGO!!!!", 'logo', 4000], [b'\x28\xb5\x2f\xfd', 'zstd']) + + +def gettype(file) -> str: + if not os.path.exists(file): + return "fne" + + def compare(header: bytes, number: int = 0) -> int: + with open(file, 'rb') as f: + f.seek(number) + return f.read(len(header)) == header + + def is_super(fil) -> any: + with open(fil, 'rb') as file_: + buf = bytearray(file_.read(4)) + if len(buf) < 4: + return False + file_.seek(0, 0) + + while buf[0] == 0x00: + buf = bytearray(file_.read(1)) + try: + file_.seek(-1, 1) + except BaseException or Exception: + return False + buf += bytearray(file_.read(4)) + return buf[1:] == b'\x67\x44\x6c\x61' + + try: + if is_super(file): + return 'super' + except IndexError: + ... + for f_ in formats: + if len(f_) == 2: + if compare(f_[0]): + return f_[1] + elif len(f_) == 3: + if compare(f_[0], f_[2]): + return f_[1] + try: + if LOGO_DUMPER(file, str(None)).check_img(file): + return 'logo' + except AssertionError: + ... + except struct.error: + ... + return "unknown" + + +class DUMPCFG: + blksz = 0x1 << 0xc + headoff = 0x4000 + magic = b"LOGO!!!!" + imgnum = 0 + imgblkoffs = [] + imgblkszs = [] + + +class BMPHEAD: + def __init__(self, buf: bytes = None): # Read bytes buf and use this struct to parse + assert buf is not None, f"buf Should be bytes not {type(buf)}" + # print(buf) + self.structstr = "/dev/null 2>&1 - sudo python3 ${tools_dir}/sdat2img.py build/baserom/$i.transfer.list build/baserom/$i.new.dat build/baserom/images/$i.img >/dev/null 2>&1 + sudo python3 ${work_dir}/bin/sdat2img.py build/baserom/$i.transfer.list build/baserom/$i.new.dat build/baserom/images/$i.img >/dev/null 2>&1 rm -rf build/baserom/$i.new.dat* build/baserom/$i.transfer.list build/baserom/$i.patch.* done fi for part in system system_dlkm system_ext product product_dlkm mi_ext ;do if [[ -f build/baserom/images/${part}.img ]];then - if [[ $($tools_dir/gettype -i build/baserom/images/${part}.img) == "ext" ]];then + if [[ $(python3 $work_dir/bin/gettype.py build/baserom/images/${part}.img) == "ext" ]];then blue "正在分解底包 ${part}.img [ext]" "Extracing ${part}.img [ext] from BASEROM" sudo python3 bin/imgextractor/imgextractor.py build/baserom/images/${part}.img build/baserom/images/ >/dev/null 2>&1 blue "分解底包 [${part}.img] 完成" "BASEROM ${part}.img [ext] extracted." rm -rf build/baserom/images/${part}.img - elif [[ $($tools_dir/gettype -i build/baserom/images/${part}.img) == "erofs" ]]; then + elif [[ $(python3 $work_dir/bin/gettype.py build/baserom/images/${part}.img) == "erofs" ]]; then pack_type=EROFS blue "正在分解底包 ${part}.img [erofs]" "Extracing ${part}.img [erofs] from BASEROM" extract.erofs -x -i build/baserom/images/${part}.img -o build/baserom/images/ > /dev/null 2>&1 || error "分解 ${part}.img 失败" "Extracting ${part}.img failed." @@ -244,13 +244,13 @@ for part in ${super_list};do if [ -f "${work_dir}/build/portrom/images/${part}.img" ];then blue "开始提取 ${part}.img" "Extracting ${part}.img" - if [[ $($tools_dir/gettype -i build/portrom/images/${part}.img) == "ext" ]];then + if [[ $(python3 $work_dir/bin/gettype.py build/portrom/images/${part}.img) == "ext" ]];then pack_type=EXT python3 bin/imgextractor/imgextractor.py build/portrom/images/${part}.img build/portrom/images/ > /dev/null 2>&1 || error "提取${part}失败" "Extracting partition ${part} failed" mkdir -p build/portrom/images/${part}/lost+found rm -rf build/portrom/images/${part}.img green "提取 [${part}] [ext]镜像完毕" "Extracting [${part}].img [ext] done" - elif [[ $(gettype -i build/portrom/images/${part}.img) == "erofs" ]];then + elif [[ $(python3 $work_dir/bin/gettype.py build/portrom/images/${part}.img) == "erofs" ]];then pack_type=EROFS green "移植包为 [erofs] 文件系统" "PORTROM filesystem: [erofs]. " [ "${repackext4}" = "true" ] && pack_type=EXT