sceutils 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sceutils/__init__.py ADDED
@@ -0,0 +1,57 @@
1
+ from __future__ import annotations
2
+
3
+ from unarzl import (
4
+ ArlzBufferOverflowError,
5
+ ArlzError,
6
+ ArlzInvalidDataError,
7
+ arzl_arm_filter,
8
+ arzl_decode,
9
+ arzl_decompress,
10
+ unarzl,
11
+ )
12
+
13
+ from . import keys, module_info, scetypes, sceutils
14
+ from .axfs import AXFS
15
+ from .module_info import read_module_info
16
+ from .pup_fiction import (
17
+ extract_bootimage,
18
+ extract_pup,
19
+ pup_decrypt_packages,
20
+ pup_extract_files,
21
+ slb2_decrypt,
22
+ slb2_extract,
23
+ )
24
+ from .scedecrypt import scedecrypt
25
+ from .sceutils import get_key_type, get_segments, zrif_decode
26
+ from .self2elf import self2elf
27
+ from .selfinfo import read_self_info
28
+
29
+ __version__ = "0.1.0"
30
+
31
+ __all__ = [
32
+ "AXFS",
33
+ "ArlzError",
34
+ "ArlzInvalidDataError",
35
+ "ArlzBufferOverflowError",
36
+ "arzl_decode",
37
+ "arzl_decompress",
38
+ "arzl_arm_filter",
39
+ "extract_bootimage",
40
+ "extract_pup",
41
+ "get_key_type",
42
+ "get_segments",
43
+ "keys",
44
+ "module_info",
45
+ "pup_decrypt_packages",
46
+ "pup_extract_files",
47
+ "read_module_info",
48
+ "read_self_info",
49
+ "scetypes",
50
+ "sceutils",
51
+ "scedecrypt",
52
+ "self2elf",
53
+ "slb2_decrypt",
54
+ "slb2_extract",
55
+ "unarzl",
56
+ "zrif_decode",
57
+ ]
sceutils/__main__.py ADDED
@@ -0,0 +1,57 @@
1
+ import importlib
2
+ import sys
3
+
4
+ MODULES = [
5
+ "axfs",
6
+ "emmc_extract",
7
+ "extract_idstorage",
8
+ "find_imported_by",
9
+ "module_info",
10
+ "pup_fiction",
11
+ "pup_info",
12
+ "scedecrypt",
13
+ "self2elf",
14
+ "selfinfo",
15
+ "ud0_extract_pup",
16
+ "unpack_bootimage_new",
17
+ "unarzl",
18
+ ]
19
+
20
+
21
+ def print_modules() -> None:
22
+ print("usage: sceutils <module> [options]")
23
+ print()
24
+ print("modules:")
25
+ for module in MODULES:
26
+ print(f" {module}")
27
+ print()
28
+ print("Use 'sceutils <module> --help' for module specific options.")
29
+
30
+
31
+ def main(argv: list[str] | None = None) -> int:
32
+ argv = list(sys.argv if argv is None else argv)
33
+
34
+ if len(argv) < 2 or argv[1] in ("-h", "--help"):
35
+ print_modules()
36
+ return 0 if len(argv) >= 2 else 1
37
+
38
+ module_name = argv[1]
39
+
40
+ if module_name == "unarzl":
41
+ from unarzl.__main__ import main as unarzl_main
42
+ return unarzl_main(argv[2:])
43
+
44
+ if module_name not in MODULES:
45
+ print(f"Unknown module: {module_name}")
46
+ print()
47
+ print_modules()
48
+ return 1
49
+
50
+ sys.argv = argv[1:]
51
+ module = importlib.import_module(f"sceutils.{module_name}")
52
+ result = module.main(sys.argv)
53
+ return result if isinstance(result, int) else 0
54
+
55
+
56
+ if __name__ == "__main__":
57
+ raise SystemExit(main())
sceutils/axfs.py ADDED
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import argparse
4
+ import os
5
+ import pathlib
6
+ import stat
7
+ import struct
8
+ import tarfile
9
+ import zlib
10
+ import sys
11
+ from dataclasses import dataclass
12
+ from io import BytesIO
13
+ from typing import Literal, Optional, IO
14
+
15
+ PAGE_SHIFT = 12
16
+
17
+
18
+ class ENodeTypes:
19
+ XIP = 0
20
+ Compressed = 1
21
+ Byte_aligned = 2
22
+
23
+
24
+ def MAJOR(dev):
25
+ return dev >> 8
26
+
27
+
28
+ def MINOR(dev):
29
+ return dev & 0xff
30
+
31
+
32
+ @dataclass
33
+ class axfs_super_onmedia:
34
+ magic: bytes # 0x48A0E4CD
35
+ signature: bytes # Advanced XIP FS\x00
36
+ digest: bytes # sha1 digest for checking data integrity (sometimes 0x0)
37
+ cblock_size: int # maximum size of the block being compressed
38
+ files: int # number of inodes/files in fs
39
+ size: int # total image size in bytes
40
+ blocks: bytes # number of nodes in fs
41
+ mmap_size: bytes # size of the memory mapped part of image
42
+ strings: int # offset to strings region descriptor
43
+ xip: int # offset to xip region descriptor
44
+ byte_aligned: int # offset to the byte aligned region desc
45
+ compressed: int # offset to the compressed region desc
46
+ node_type: int # offset to node type region desc
47
+ node_index: int # offset to node index region desc
48
+ cnode_offset: int # offset to cnode offset region desc
49
+ cnode_index: int # offset to cnode index region desc
50
+ banode_offset: int # offset to banode offset region desc
51
+ cblock_offset: int # offset to cblock offset region desc
52
+ inode_file_size: int # offset to inode file size desc
53
+ inode_name_offset: int # offset to inode name region desc
54
+ inode_num_entries: int # offset to inode num_entries region desc
55
+ inode_mode_index: int # offset to inode mode_index region desc
56
+ inode_array_index: int # offset to inode array_index region desc
57
+ modes: int # offset to mode mode region desc
58
+ uids: int # offset to uid index mode region desc
59
+ gids: int # offset to gid index mode region desc
60
+ version_major: int
61
+ version_minor: int
62
+ version_sub: int
63
+ compression_type: int # Identifies type of compression used on FS
64
+ timestamp: int # UNIX time_t of filesystem build time
65
+
66
+
67
+ class axfs_region_desc_onmedia:
68
+ fsoffset: int
69
+ size: int
70
+ compressed_size: int
71
+ max_index: int
72
+ table_byte_depth: int
73
+ incore: int
74
+
75
+
76
+ class axfs_region(axfs_region_desc_onmedia):
77
+ data: bytes
78
+
79
+ def __init__(self, f: IO[bytes]) -> None:
80
+ (
81
+ self.fsoffset, self.size,
82
+ self.compressed_size, self.max_index,
83
+ self.table_byte_depth, self.incore
84
+ ) = struct.unpack(">QQQQBB", f.read(34))
85
+ f.seek(self.fsoffset, os.SEEK_SET)
86
+ self.data = f.read(self.size)
87
+
88
+ def axfs_bytetable_stitch(self, index: int) -> int:
89
+ assert index < self.max_index
90
+ output = 0
91
+ split = self.size // self.table_byte_depth
92
+ for i in range(self.table_byte_depth):
93
+ output += self.data[index + i * split] << (8 * i)
94
+ return output
95
+
96
+
97
+ def offsetBytes(data: bytes, start: int, length: int):
98
+ return data[start:start + length]
99
+
100
+
101
+ def loadRegion(f: IO, offset: int) -> axfs_region:
102
+ f.seek(offset, os.SEEK_SET)
103
+ return axfs_region(f)
104
+
105
+
106
+ class AXFS:
107
+ def __init__(self, f: IO[bytes]):
108
+ self.superblock = axfs_super_onmedia(*struct.unpack('>I16s40sI22Q4BQ', f.read(252)))
109
+ assert self.superblock.signature == b'Advanced XIP FS\x00'
110
+ assert self.superblock.magic == 0x48A0E4CD
111
+ self.xip = loadRegion(f, self.superblock.xip)
112
+ self.strings = loadRegion(f, self.superblock.strings)
113
+ self.compressed = loadRegion(f, self.superblock.compressed)
114
+ self.byte_aligned = loadRegion(f, self.superblock.byte_aligned)
115
+ self.node_type = loadRegion(f, self.superblock.node_type)
116
+ self.node_index = loadRegion(f, self.superblock.node_index)
117
+ self.cnode_offset = loadRegion(f, self.superblock.cnode_offset)
118
+ self.cnode_index = loadRegion(f, self.superblock.cnode_index)
119
+ self.banode_offset = loadRegion(f, self.superblock.banode_offset)
120
+ self.cblock_offset = loadRegion(f, self.superblock.cblock_offset)
121
+ self.inode_file_size = loadRegion(f, self.superblock.inode_file_size)
122
+ self.inode_name_offset = loadRegion(f, self.superblock.inode_name_offset)
123
+ self.inode_num_entries = loadRegion(f, self.superblock.inode_num_entries)
124
+ self.inode_mode_index = loadRegion(f, self.superblock.inode_mode_index)
125
+ self.inode_array_index = loadRegion(f, self.superblock.inode_array_index)
126
+ self.modes = loadRegion(f, self.superblock.modes)
127
+ self.uids = loadRegion(f, self.superblock.uids)
128
+ self.gids = loadRegion(f, self.superblock.gids)
129
+ f.seek(0)
130
+
131
+ def list(self, node_id: int, folder="/"):
132
+ num_files = self.getNumEntries(node_id)
133
+ first = self.getArrayIndex(node_id)
134
+ for i in range(first, first + num_files):
135
+ name = self.getName(i)
136
+ mode = self.getMode(i)
137
+ size = " " * 9
138
+
139
+ if stat.S_ISREG(mode):
140
+ size = str(self.getFileSize(i)).rjust(9)
141
+ if stat.S_ISLNK(mode):
142
+ link_name = self.readFileData(i).decode("utf8")
143
+ name += ' -> ' + link_name
144
+ if stat.S_ISCHR(mode) or stat.S_ISBLK(mode):
145
+ st_rdev = self.getFileSize(i)
146
+ size = f"{str(MAJOR(st_rdev)).rjust(3)} {str(MINOR(st_rdev)).rjust(3)}".rjust(9)
147
+
148
+ print(f"{stat.filemode(mode)} {size} {folder + name}")
149
+
150
+ if stat.S_ISDIR(mode):
151
+ self.list(i, folder + name + "/")
152
+
153
+ def toTar(self, node_id: int, tar: tarfile.TarFile, folder=""):
154
+ num_files = self.getNumEntries(node_id)
155
+ first = self.getArrayIndex(node_id)
156
+ for i in range(first, first + num_files):
157
+ name = self.getName(i)
158
+ mode = self.getMode(i)
159
+
160
+ file = tarfile.TarInfo()
161
+ file.path = folder + name
162
+ file.uid = self.getUid(i)
163
+ file.gid = self.getGid(i)
164
+ file.mode = mode
165
+ file.mtime = self.superblock.timestamp
166
+ data = None
167
+
168
+ if stat.S_ISDIR(mode):
169
+ file.type = tarfile.DIRTYPE
170
+ self.toTar(i, tar, file.path + "/")
171
+
172
+ elif stat.S_ISLNK(mode):
173
+ link_name = self.readFileData(i).decode("utf8")
174
+ file.type = tarfile.SYMTYPE
175
+ file.linkname = link_name
176
+ elif stat.S_ISREG(mode):
177
+ file.type = tarfile.REGTYPE
178
+ file.size = self.getFileSize(i)
179
+ data = self.readFileData(i)
180
+
181
+ elif stat.S_ISCHR(mode):
182
+ file.type = tarfile.CHRTYPE
183
+ elif stat.S_ISBLK(mode):
184
+ file.type = tarfile.BLKTYPE
185
+
186
+ elif stat.S_ISSOCK(mode) or stat.S_ISFIFO(mode):
187
+ pass
188
+ else:
189
+ raise Exception("unkown filetype")
190
+
191
+ if stat.S_ISCHR(mode) or stat.S_ISBLK(mode):
192
+ st_rdev = self.getFileSize(i) # very weird to put that in filesize...
193
+ file.devmajor, file.devminor = MAJOR(st_rdev), MINOR(st_rdev)
194
+
195
+ tar.addfile(file, BytesIO(data))
196
+
197
+ def getName(self, node_id: int) -> int:
198
+ offset = self.inode_name_offset.axfs_bytetable_stitch(node_id)
199
+ ret = self.strings.data[offset:]
200
+ if len(ret):
201
+ ret = ret[:ret.index(b"\00")]
202
+ return ret.decode("utf8")
203
+
204
+ def getFileSize(self, node_id: int) -> int:
205
+ return self.inode_file_size.axfs_bytetable_stitch(node_id)
206
+
207
+ def getMode(self, node_id: int) -> int:
208
+ mode_index = self.inode_mode_index.axfs_bytetable_stitch(node_id)
209
+ return self.modes.axfs_bytetable_stitch(mode_index)
210
+
211
+ def getUid(self, node_id: int) -> int:
212
+ mode_index = self.inode_mode_index.axfs_bytetable_stitch(node_id)
213
+ return self.uids.axfs_bytetable_stitch(mode_index)
214
+
215
+ def getGid(self, node_id: int) -> int:
216
+ mode_index = self.inode_mode_index.axfs_bytetable_stitch(node_id)
217
+ return self.gids.axfs_bytetable_stitch(mode_index)
218
+
219
+ def getNumEntries(self, node_id: int) -> int:
220
+ return self.inode_num_entries.axfs_bytetable_stitch(node_id)
221
+
222
+ def getArrayIndex(self, node_id: int) -> int:
223
+ return self.inode_array_index.axfs_bytetable_stitch(node_id)
224
+
225
+ def getNodeIndex(self, node_id: int) -> int:
226
+ return self.node_index.axfs_bytetable_stitch(node_id)
227
+
228
+ def getNodeType(self, node_id: int) -> int:
229
+ return self.node_type.axfs_bytetable_stitch(node_id)
230
+
231
+ def getByteAlignedOffset(self, node_id: int) -> int:
232
+ return self.banode_offset.axfs_bytetable_stitch(node_id)
233
+
234
+ def readFileData(self, node_id: int) -> bytes:
235
+ file_size = self.getFileSize(node_id)
236
+ array_index = self.getArrayIndex(node_id)
237
+
238
+ length = file_size
239
+ offset = 0
240
+ out = bytearray()
241
+
242
+ while length > 0:
243
+ node_index = self.getNodeIndex(array_index)
244
+ node_type = self.getNodeType(array_index)
245
+
246
+ if node_type == ENodeTypes.Byte_aligned:
247
+ src_offset = self.getByteAlignedOffset(node_index)
248
+ block_size = min(0x1 << PAGE_SHIFT, file_size)
249
+ out[offset:] = offsetBytes(self.byte_aligned.data, src_offset, block_size)
250
+ length -= block_size
251
+ offset += block_size
252
+
253
+ elif node_type == ENodeTypes.XIP:
254
+ out[offset:] = offsetBytes(self.xip.data, node_index << PAGE_SHIFT, 1 << PAGE_SHIFT)
255
+ offset += 1 << PAGE_SHIFT
256
+ length -= 1 << PAGE_SHIFT
257
+
258
+ elif node_type == ENodeTypes.Compressed:
259
+ cnode_offset = self.cnode_offset.axfs_bytetable_stitch(node_index)
260
+ cnode_index = self.cnode_index.axfs_bytetable_stitch(node_index)
261
+ src_offset = self.cblock_offset.axfs_bytetable_stitch(cnode_index)
262
+
263
+ block_length = self.cblock_offset.axfs_bytetable_stitch(cnode_index + 1) - src_offset
264
+ data = zlib.decompress(offsetBytes(self.compressed.data, src_offset, block_length))
265
+ block_length = min(self.superblock.cblock_size - cnode_offset, length)
266
+ out[offset:] = offsetBytes(data, cnode_offset, block_length)
267
+ length -= block_length
268
+ offset += block_length
269
+ else:
270
+ raise Exception("invalid nodeType")
271
+
272
+ array_index += 1
273
+
274
+ return bytes(out)
275
+
276
+
277
+ def main(argv = sys.argv):
278
+ class _args:
279
+ command: Literal["list", "tar"]
280
+ inputfile: pathlib.Path
281
+ output: Optional[pathlib.Path]
282
+
283
+ parser = argparse.ArgumentParser()
284
+ parser.add_argument("command", type=str, choices=["list", "tar"])
285
+ parser.add_argument("inputfile", help="input axfs image", type=pathlib.Path)
286
+ parser.add_argument("output", help="output file name for tar", type=pathlib.Path, required=False) # optional
287
+ args: _args = parser.parse_args(argv[1:])
288
+
289
+ inputfile = args.inputfile.as_posix()
290
+ output = args.output.as_posix() if args.output else None
291
+
292
+ with open(inputfile, "rb") as f:
293
+ fs = AXFS(f)
294
+
295
+ if args.command == "list":
296
+ fs.list(0)
297
+ return
298
+ elif args.command == "tar":
299
+ if not output:
300
+ output = inputfile + ".tar"
301
+ tar = tarfile.open(output, mode="w")
302
+ fs.toTar(0, tar)
303
+ tar.close()
304
+ return
305
+
306
+
307
+ if __name__ == "__main__":
308
+ main()
@@ -0,0 +1,136 @@
1
+ import os
2
+ import struct
3
+ import tarfile
4
+ from io import BytesIO
5
+ from typing import IO
6
+
7
+ from Cryptodome.Cipher import AES
8
+ from Cryptodome.PublicKey import RSA
9
+
10
+ from sceutils import axfs
11
+
12
+ CPUP_MAGIC = 0x43705570
13
+ TRF_MAGIC = 0x23642745
14
+
15
+ CpUp_Public = (0xA7CCAE0F501188527BF3DACCA3E231C8D8701E7B91927390701DE5E7A96327DAD87167A8F01368ADDFE490E325A290533697058FBA775766698010AFD8FD7A3FFD265E0A52FE04928BCE8B4302F4C70FFAC3C9397FD24B106271E57BDA20D2D702298F6F990ECF9B0FE04FF6CCEE170B555304232012D78E6019DAB29763829E6AF5ADA802204FA551631179CBFE6164732662E8576741949BB136456C11DE355F487211D230267DC05E699A2652AD5C6D74B0568326F4F2F5B86AD956E94404D3A65928F4EA2189567CE9989911B04808517F4C76A8B25DF1D6ABBE8595C469BFD7E870C4F00A89610C2C9B79F625A42CA2B4C6B8D37E62CE9EC61A856FD32F, 0x10001)
16
+ FsImage_Public = (0xA9697F9D9343CADE68E04F9E356E6AB6BBC7DE36A4D81B98A83BC12BE3F6DF96ED7A64389456ACA933BEBFBA4FFEF05CF45F2F886F434FBBC3A01348533070C0B7D5E9C21EFE53E95A6019DB51C12C6BAFEB94E992287963448E59606384B99F3FF3E5EB6AA08BF32A4DBA7A312520CEC2B69BB20A6D0640B117170AA2DDA1FB590AEE7ADFC4E80DFCF27FA55DDEC92C07922FDD05AB1618DCB727AA6FF70027A9410BC845E50EAFD46C0FD92FF500672DE56489C669B0AA481FFD75E99E21A8DC2F9F9E87957B46BBF63FB7DDBE8B8CA861BA349A62458E855EE78C3DD6791F92E76422144E51295B1337E15C126DF6FA0C29321BC1D7C00E3C19EEF3A3E7A5, 0x10001)
17
+
18
+
19
+ def decrypt_unpack_cpup(filename: str, dst: str) -> tarfile.TarFile:
20
+ " decrypts a cpup into a tar "
21
+ os.mkdir(dst)
22
+ dst_name = os.path.join(dst, os.path.basename(filename) + ".tar.gz")
23
+
24
+ with open(filename, "rb") as fin:
25
+ data = decrypt(fin)
26
+ with open(dst_name, "wb") as fout:
27
+ fout.write(data)
28
+ try:
29
+ tar = tarfile.open(fileobj=BytesIO(data), mode="r:gz")
30
+ except tarfile.ReadError:
31
+ raise ValueError("CpUp Extraction failed likely wrong keys")
32
+ return tar
33
+
34
+
35
+ def extract_cpup(tar: tarfile.TarFile, dst: str):
36
+ " extracts the cpup from the tar "
37
+ os.mkdir(dst)
38
+
39
+ # decrypt fsimage0
40
+ fsimage0 = tar.extractfile("./fsimage0.trf")
41
+ if fsimage0 is None:
42
+ raise ValueError("fsimage0.trf not found")
43
+ fsimage0.seek(0x120)
44
+ with open(os.path.join(dst, "fsimage0.img"), "wb") as f:
45
+ f.write(fsimage0.read())
46
+
47
+ # read axfs for fsimage0
48
+ fsimage0.seek(0x120)
49
+ axfs_fsimage0 = axfs.AXFS(fsimage0)
50
+ with tarfile.open(os.path.join(dst, "fsimage0.tar"), mode="w") as tar_fsimage0:
51
+ axfs_fsimage0.toTar(0, tar_fsimage0)
52
+
53
+ # decrypt fsimage1
54
+ fsimage1 = tar.extractfile("./fsimage1.trf")
55
+ if fsimage1 is None:
56
+ raise ValueError("fsimage1.trf not found")
57
+ decrypted = decrypt(fsimage1)
58
+ with open(os.path.join(dst, "fsimage1.img"), "wb") as f:
59
+ f.write(decrypted)
60
+
61
+ # read axfs for fsimage1
62
+ axfs_fsimage1 = axfs.AXFS(BytesIO(decrypted))
63
+ with tarfile.open(os.path.join(dst, "fsimage1.tar"), mode="w") as tar_fsimage1:
64
+ axfs_fsimage1.toTar(0, tar_fsimage1)
65
+
66
+ # decrypt vmlinux image
67
+ vmlinux = tar.extractfile("./vmlinux.trf")
68
+ if vmlinux is None:
69
+ raise ValueError("vmlinux.trf not found")
70
+ with open(os.path.join(dst, "vmlinux.img"), "wb") as f:
71
+ f.write(decrypt(vmlinux))
72
+
73
+
74
+ def decrypt(f: IO[bytes], silent=False):
75
+ " decrypts cpupdate "
76
+ is_encrypted = True
77
+
78
+ magic, = struct.unpack("<I", f.read(4))
79
+ if magic == CPUP_MAGIC:
80
+ (
81
+ cp_version,
82
+ fmt_version,
83
+ size,
84
+ data_offset,
85
+ data_size
86
+ ) = struct.unpack("<I8xIIII", f.read(28))
87
+ rsa = RSA.construct(CpUp_Public)
88
+
89
+ magic_str = int.to_bytes(magic, 4, "big").decode("ascii")
90
+ cp_ver_str = '.'.join([str(num) for num in int.to_bytes(cp_version, 4, "big")])
91
+ fmt_ver_str = '.'.join([str(num) for num in int.to_bytes(fmt_version, 4, "big")])
92
+ if not silent:
93
+ print("CpUp Decrypt")
94
+ print(f"magic : {magic_str}\t\t(0x{magic:08X})")
95
+ print(f"cp version : {cp_ver_str}\t\t(0x{cp_version:08X})")
96
+ print(f"fmt version : {fmt_ver_str}\t\t(0x{fmt_version:08X})")
97
+ elif magic == TRF_MAGIC:
98
+ (
99
+ version,
100
+ data_size,
101
+ data_offset,
102
+ size,
103
+ ) = struct.unpack("<IIII", f.read(16))
104
+ rsa = RSA.construct(FsImage_Public)
105
+ if version == 0x1010100:
106
+ is_encrypted = False
107
+
108
+ ver_str = '.'.join([str(num) for num in int.to_bytes(version, 4, "big")])
109
+ if not silent:
110
+ print("Trf Decrypt")
111
+ print(f"magic : {magic:08X}")
112
+ print(f"version : {ver_str}\t\t(0x{version:08X})")
113
+ else:
114
+ raise RuntimeError("Invalid CpUp or trf")
115
+
116
+ if not silent:
117
+ print(f"size : {size}\t\t(0x{size:08X})")
118
+ print(f"data offset : {data_offset}\t\t(0x{data_offset:08X})")
119
+ print(f"data size : {data_size}\t\t(0x{data_size:08X})")
120
+
121
+ # get key and iv from sig
122
+ f.seek(-0x100, 2)
123
+ key_data = f.read()
124
+ sig = rsa._encrypt(int.from_bytes(key_data, "big"))
125
+ sig = int.to_bytes(sig, 0x100, "big")[-0x34:]
126
+ (iv, key, sha1_hash) = struct.unpack("16s16s20s", sig)
127
+
128
+ # read file data and decrypt
129
+ f.seek(data_offset, os.SEEK_SET)
130
+ data = f.read((size - data_offset) - 0x100)
131
+ if is_encrypted:
132
+ aes_key = AES.new(key, AES.MODE_CBC, iv)
133
+ data = aes_key.decrypt(data)
134
+
135
+ print("-" * 80)
136
+ return data[:data_size]