Beispiel #1
0
 def __init__(self, reader: StructReader):
     reader.bigendian = True
     entry_start_offset = reader.tell()
     self.size_of_entry = reader.i32()
     self.offset = reader.i32()
     self.size_of_compressed_data = reader.i32()
     self.size_od_uncompressed_data = reader.i32()
     self.is_compressed = bool(reader.read_byte())
     entry_type = bytes(reader.read(1))
     name_length = self.size_of_entry - reader.tell() + entry_start_offset
     if name_length > 0x1000:
         raise RuntimeError(
             F'Refusing to process TOC entry with name of size {name_length}.'
         )
     name, *_ = bytes(reader.read(name_length)).partition(B'\0')
     try:
         name = name.decode('utf8', 'backslashreplace')
     except Exception:
         name = None
     if not all(part.isprintable() for part in re.split('\\s*', name)):
         raise RuntimeError(
             'Refusing to process TOC entry with non-printable name.')
     name = name or str(uuid.uuid4())
     if entry_type == B'Z':
         entry_type = B'z'
     try:
         self.type = PiType(entry_type)
     except ValueError:
         xtpyi.logger.error(F'unknown type {entry_type!r} in field {name}')
         self.type = PiType.UNKNOWN
     self.name = name
Beispiel #2
0
 def __init__(self, reader: StructReader):
     reader.bigendian = True
     self.max_stack = reader.u16()
     self.max_locals = reader.u16()
     self.disassembly: List[JvOpCode] = []
     with StructReader(reader.read(reader.u32())) as code:
         code.bigendian = True
         while not code.eof:
             self.disassembly.append(JvOpCode(code, pool=self.pool))
     self.exceptions = [JvException(reader) for _ in range(reader.u16())]
     self.attributes = [JvAttribute(reader) for _ in range(reader.u16())]
Beispiel #3
0
 def __init__(self, reader: StructReader, version: str):
     reader.bigendian = True
     self.base = reader.tell()
     signature = reader.read(4)
     if signature != self.MagicSignature:
         raise ValueError('invalid magic')
     magic = bytes(reader.read(4))
     with contextlib.suppress(KeyError):
         version = xtpyi._xdis.magics.versions[magic]
     vtuple = version2tuple(version)
     padding_size = 4
     if vtuple >= (3, 3):
         padding_size += 4
     if vtuple >= (3, 7):
         padding_size += 4
     self.version = version
     self.magic = magic + padding_size * b'\0'
     self.toc_offset = reader.i32()
     self.reader = reader
     self.entries: List[PiMeta] = []
Beispiel #4
0
    def __init__(self, reader: StructReader):
        reader.bigendian = True
        if reader.read(4).hex() != 'cafebabe':
            raise ValueError('class file magic missing.')
        minor = reader.u16()
        major = reader.u16()
        self.version = (major, minor)

        self.pool: List[Union[Struct, int, float, str]] = []
        self._read_pool(reader)

        self.strings: List[str] = {
            s.value for s in self.pool if isinstance(s, Struct) and s.tag == JvConstType.String}

        self.access = JvAccessFlags(reader)

        self.this = reader.u16()
        self.parent = reader.u16()

        try:
            self.interfaces = [self.pool[reader.u16()]
                for _ in range(reader.u16())]
        except IndexError:
            raise ValueError('Failed parsing Interfaces.')
        try:
            self.fields = [JvClassMember(reader, pool=self.pool)
                for _ in range(reader.u16())]
        except IndexError:
            raise ValueError('Failed parsing Fields.')
        try:
            self.methods = [JvClassMember(reader, pool=self.pool)
                for _ in range(reader.u16())]
        except IndexError:
            raise ValueError('Failed parsing Methods.')
        try:
            self.attributes = [JvAttribute(reader, pool=self.pool)
                for _ in range(reader.u16())]
        except IndexError:
            raise ValueError('Failed parsing Attributes.')
Beispiel #5
0
    def __init__(self, reader: StructReader, calculate_checks=False):
        if reader.read(4) != b'dex\n':
            raise ValueError('Invalid Signature')
        with StreamDetour(reader, 0x28):
            endian_test_data = reader.u32()
            if endian_test_data == 0x78563412:
                reader.bigendian = True
        self.version = reader.read(4).rstrip(b'\0')

        self.checksum = reader.u32()
        if calculate_checks:
            with StreamDetour(reader):
                self.calculated_checksum = zlib.adler32(reader.read())
        else:
            self.calculated_checksum = None

        self.signature = reader.read(20)
        if calculate_checks:
            with StreamDetour(reader):
                self.calculated_signature = hashlib.sha1(
                    reader.read()).digest()
        else:
            self.calculated_signature = None

        self.size_of_file = reader.u32()
        self.size_of_header = reader.u32()

        if reader.u32() != 0x12345678:
            raise ValueError('Invalid Endian Tag')

        self.link_size = reader.u32()
        self.link_offset = reader.u32()
        self.map_offset = reader.u32()

        self.strings: List[str] = list(
            self._read_strings(reader, reader.u32(), reader.u32()))
Beispiel #6
0
    def __init__(self,
                 reader: StructReader,
                 offset: int,
                 unmarshal: Unmarshal = Unmarshal.No):
        reader.bigendian = True
        reader.seekset(offset)
        self.reader = reader
        signature = reader.read_bytes(8)
        if signature != self.MagicSignature:
            raise ValueError(
                F'offset 0x{offset:X} has invalid signature {signature.hex().upper()}; '
                F'should be {self.MagicSignature.hex().upper()}')
        self.size = reader.i32()
        toc_offset = reader.i32()
        toc_length = reader.i32()
        self.py_version = '.'.join(str(reader.u32()))
        self.py_libname = self._read_libname(reader)
        self.offset = reader.tell() - self.size

        self.toc: Dict[str, PiTOCEntry] = {}
        toc_end = self.offset + toc_offset + toc_length
        reader.seekset(self.offset + toc_offset)
        while reader.tell() < toc_end:
            try:
                entry = PiTOCEntry(reader)
            except EOF:
                xtpyi.logger.warning('end of file while reading TOC')
                break
            except Exception as error:
                xtpyi.logger.warning(
                    F'unexpected error while reading TOC: {error!s}')
                break
            if entry.name in self.toc:
                raise KeyError(F'duplicate name {entry.name}')
            self.toc[entry.name] = entry

        self.files: Dict[str, PiMeta] = {}
        no_pyz_found = True
        pyz_entries: Dict[str, PYZ] = {}

        for entry in list(self.toc.values()):
            if entry.type is not PiType.PYZ:
                continue
            no_pyz_found = False
            name, xt = os.path.splitext(entry.name)
            name_pyz = F'{name}.pyz'
            if name == entry.name:
                del self.toc[name]
                self.toc[name_pyz] = entry
                entry.name = name_pyz
            reader.seekset(self.offset + entry.offset)
            if entry.is_compressed:
                data = self.extract(entry.name).unpack()
            else:
                data = reader
            pyz_entries[name] = PYZ(data, self.py_version)

        magics = {pyz.magic for pyz in pyz_entries.values()}

        if not magics:
            if not no_pyz_found:
                xtpyi.logger.warning(
                    'no magic signature could be recovered from embedded pyzip archives; this is '
                    'unsual and means that there is no way to guess the missing magic for source '
                    'file entries and it will likely not be possible to decompile them.'
                )
            return
        elif len(magics) > 1:
            xtpyi.logger.warning(
                'more than one magic signature was recovered; this is unusual.'
            )

        magics = list(magics)
        keys: Set[bytes] = set()

        for entry in self.toc.values():
            extracted = self.extract(entry.name)
            if entry.type not in (PiType.SOURCE, PiType.MODULE):
                self.files[entry.name] = extracted
                continue
            data = extracted.unpack()
            name, _ = os.path.splitext(extracted.name)
            del self.files[extracted.name]
            extracted.name = F'{name}.pyc'
            self.files[extracted.name] = extracted

            if len(magics) == 1 and data[:4] != magics[0]:
                extracted.data = magics[0] + data
            decompiled = make_decompiled_item(name, data, *magics)
            if entry.type is PiType.SOURCE:
                decompiled.type = PiType.USERCODE
            self.files[F'{name}.py'] = decompiled
            if name.endswith('crypto_key'):
                for key in decompiled.unpack() | carve('string', decode=True):
                    if len(key) != 0x10:
                        continue
                    xtpyi.logger.info(F'found key: {key.decode(xtpyi.codec)}')
                    keys.add(key)

        if unmarshal is Unmarshal.No:
            return

        if not keys:
            key = None
        else:
            key = next(iter(keys))

        for name, pyz in pyz_entries.items():
            pyz.unpack(unmarshal is Unmarshal.YesAndDecompile, key)
            for unpacked in pyz.entries:
                unpacked.name = path = F'{name}/{unpacked.name}'
                if path in self.files:
                    raise ValueError(F'duplicate file name: {path}')
                self.files[path] = unpacked