-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathupdate-extractor.py
94 lines (78 loc) · 3.32 KB
/
update-extractor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
from io import BytesIO
from pathlib import Path
from struct import unpack
from argparse import ArgumentParser
from typing import List
MAGIC = b'\x55\xAA\x5A\xA5'
CHUNK_SIZE = 0x400 # 1024 bytes
ALIGNMENT = 4 # Alignment bytes
class Partition:
def __init__(self, start: int, hdr_sz: int, unk1: int, hw_id: int, seq: int,
size: int, date: str, time: str, ftype: str, blank1: bytes,
hdr_crc: int, block_size: int, blank2: bytes, checksum: bytes,
data: bytes, end: int):
self.start = start
self.hdr_sz = hdr_sz
self.unk1 = unk1
self.hw_id = hw_id
self.seq = seq
self.size = size
self.date = date
self.time = time
self.type = ftype
self.blank1 = blank1
self.hdr_crc = hdr_crc
self.block_size = block_size
self.blank2 = blank2
self.checksum = checksum
self.data = data
self.end = end
@classmethod
def from_file(cls, file, offset: int = 0):
hdr_sz, unk1, hw_id, seq, size = unpack('<LLQLL', file.read(24))
date, time, type = file.read(16).decode().strip('\x00'), \
file.read(16).decode().strip('\x00'), file.read(16).decode().strip('\x00')
blank1, hdr_crc, block_size, blank2, checksum = file.read(16), file.read(2).hex(), \
file.read(2).hex(), file.read(2), file.read(hdr_sz - 98)
data = file.read(size) if size > 0 else b''
file.seek((ALIGNMENT - file.tell() % ALIGNMENT) % ALIGNMENT, 1)
return cls(offset, hdr_sz, unk1, hw_id, seq, size, date, time, type, blank1,
hdr_crc, block_size, blank2, checksum, data, file.tell())
class UpdateExtractor:
def __init__(self, package: Path, output: Path):
self.package = package.open('rb')
self.output = output
self.partitions: List[Partition] = []
self.parse_partitions()
def parse_partitions(self):
while True:
buffer = self.package.read(4)
if not buffer: break
if buffer == MAGIC:
self.partitions.append(Partition.from_file(
self.package, self.package.tell()
))
def extract(self, name: str = None):
self.output.mkdir(exist_ok=True)
for partition in self.partitions:
if name is not None and partition.type != name:
continue
with open('%s/%s.img' % (self.output,
partition.type), 'wb') as f:
f.write(partition.data)
def main():
parser = ArgumentParser()
parser.add_argument('package', help='UPDATE.APP package.', type=Path)
parser.add_argument('-e', '--extract', help='Extract partitions to files.', action='store_true')
parser.add_argument('-o', '--output', help='Output folder.', default='output', type=Path)
parser.add_argument('-p', '--partition', help='Partition name to extract.', type=str, default=None)
args = parser.parse_args()
extractor = UpdateExtractor(
args.package, args.output)
for partition in extractor.partitions:
print("%s (%d bytes) @ %s - %s" % (partition.type, partition.size,
hex(partition.start), hex(partition.end)))
if args.extract:
extractor.extract(args.partition)
if __name__ == '__main__':
main()