pymobiledevice3 4.27.4__py3-none-any.whl → 5.1.2__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.
- misc/plist_sniffer.py +15 -15
- misc/remotexpc_sniffer.py +29 -28
- pymobiledevice3/__main__.py +123 -98
- pymobiledevice3/_version.py +2 -2
- pymobiledevice3/bonjour.py +351 -117
- pymobiledevice3/ca.py +32 -24
- pymobiledevice3/cli/activation.py +7 -7
- pymobiledevice3/cli/afc.py +19 -19
- pymobiledevice3/cli/amfi.py +4 -4
- pymobiledevice3/cli/apps.py +51 -39
- pymobiledevice3/cli/backup.py +58 -32
- pymobiledevice3/cli/bonjour.py +27 -20
- pymobiledevice3/cli/cli_common.py +112 -81
- pymobiledevice3/cli/companion_proxy.py +4 -4
- pymobiledevice3/cli/completions.py +10 -10
- pymobiledevice3/cli/crash.py +37 -31
- pymobiledevice3/cli/developer.py +601 -519
- pymobiledevice3/cli/diagnostics.py +38 -33
- pymobiledevice3/cli/lockdown.py +82 -72
- pymobiledevice3/cli/mounter.py +84 -67
- pymobiledevice3/cli/notification.py +10 -10
- pymobiledevice3/cli/pcap.py +19 -14
- pymobiledevice3/cli/power_assertion.py +12 -10
- pymobiledevice3/cli/processes.py +10 -10
- pymobiledevice3/cli/profile.py +88 -77
- pymobiledevice3/cli/provision.py +17 -17
- pymobiledevice3/cli/remote.py +188 -111
- pymobiledevice3/cli/restore.py +43 -40
- pymobiledevice3/cli/springboard.py +30 -28
- pymobiledevice3/cli/syslog.py +85 -58
- pymobiledevice3/cli/usbmux.py +21 -20
- pymobiledevice3/cli/version.py +3 -2
- pymobiledevice3/cli/webinspector.py +156 -78
- pymobiledevice3/common.py +1 -1
- pymobiledevice3/exceptions.py +154 -60
- pymobiledevice3/irecv.py +49 -53
- pymobiledevice3/irecv_devices.py +1489 -492
- pymobiledevice3/lockdown.py +400 -251
- pymobiledevice3/lockdown_service_provider.py +5 -7
- pymobiledevice3/osu/os_utils.py +18 -9
- pymobiledevice3/osu/posix_util.py +28 -15
- pymobiledevice3/osu/win_util.py +14 -8
- pymobiledevice3/pair_records.py +19 -19
- pymobiledevice3/remote/common.py +4 -4
- pymobiledevice3/remote/core_device/app_service.py +94 -67
- pymobiledevice3/remote/core_device/core_device_service.py +17 -14
- pymobiledevice3/remote/core_device/device_info.py +5 -5
- pymobiledevice3/remote/core_device/diagnostics_service.py +10 -8
- pymobiledevice3/remote/core_device/file_service.py +47 -33
- pymobiledevice3/remote/remote_service_discovery.py +53 -35
- pymobiledevice3/remote/remotexpc.py +64 -42
- pymobiledevice3/remote/tunnel_service.py +383 -297
- pymobiledevice3/remote/utils.py +14 -13
- pymobiledevice3/remote/xpc_message.py +145 -125
- pymobiledevice3/resources/dsc_uuid_map.py +19 -19
- pymobiledevice3/resources/firmware_notifications.py +16 -16
- pymobiledevice3/restore/asr.py +27 -27
- pymobiledevice3/restore/base_restore.py +90 -47
- pymobiledevice3/restore/consts.py +87 -66
- pymobiledevice3/restore/device.py +11 -11
- pymobiledevice3/restore/fdr.py +46 -46
- pymobiledevice3/restore/ftab.py +19 -19
- pymobiledevice3/restore/img4.py +130 -133
- pymobiledevice3/restore/mbn.py +587 -0
- pymobiledevice3/restore/recovery.py +125 -135
- pymobiledevice3/restore/restore.py +535 -523
- pymobiledevice3/restore/restore_options.py +122 -115
- pymobiledevice3/restore/restored_client.py +25 -22
- pymobiledevice3/restore/tss.py +378 -270
- pymobiledevice3/service_connection.py +50 -46
- pymobiledevice3/services/accessibilityaudit.py +137 -127
- pymobiledevice3/services/afc.py +352 -292
- pymobiledevice3/services/amfi.py +21 -18
- pymobiledevice3/services/companion.py +23 -19
- pymobiledevice3/services/crash_reports.py +61 -47
- pymobiledevice3/services/debugserver_applist.py +3 -3
- pymobiledevice3/services/device_arbitration.py +8 -8
- pymobiledevice3/services/device_link.py +56 -48
- pymobiledevice3/services/diagnostics.py +971 -968
- pymobiledevice3/services/dtfetchsymbols.py +8 -8
- pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
- pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
- pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
- pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
- pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
- pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
- pymobiledevice3/services/dvt/instruments/device_info.py +11 -11
- pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
- pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
- pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
- pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
- pymobiledevice3/services/dvt/instruments/process_control.py +25 -10
- pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
- pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
- pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
- pymobiledevice3/services/file_relay.py +10 -10
- pymobiledevice3/services/heartbeat.py +8 -7
- pymobiledevice3/services/house_arrest.py +12 -15
- pymobiledevice3/services/installation_proxy.py +119 -100
- pymobiledevice3/services/lockdown_service.py +12 -5
- pymobiledevice3/services/misagent.py +22 -19
- pymobiledevice3/services/mobile_activation.py +84 -72
- pymobiledevice3/services/mobile_config.py +331 -301
- pymobiledevice3/services/mobile_image_mounter.py +137 -116
- pymobiledevice3/services/mobilebackup2.py +188 -150
- pymobiledevice3/services/notification_proxy.py +11 -11
- pymobiledevice3/services/os_trace.py +128 -74
- pymobiledevice3/services/pcapd.py +306 -306
- pymobiledevice3/services/power_assertion.py +10 -9
- pymobiledevice3/services/preboard.py +4 -4
- pymobiledevice3/services/remote_fetch_symbols.py +16 -14
- pymobiledevice3/services/remote_server.py +176 -146
- pymobiledevice3/services/restore_service.py +16 -16
- pymobiledevice3/services/screenshot.py +13 -10
- pymobiledevice3/services/simulate_location.py +7 -7
- pymobiledevice3/services/springboard.py +15 -15
- pymobiledevice3/services/syslog.py +5 -5
- pymobiledevice3/services/web_protocol/alert.py +3 -3
- pymobiledevice3/services/web_protocol/automation_session.py +183 -179
- pymobiledevice3/services/web_protocol/cdp_screencast.py +44 -36
- pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
- pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
- pymobiledevice3/services/web_protocol/driver.py +47 -45
- pymobiledevice3/services/web_protocol/element.py +74 -63
- pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
- pymobiledevice3/services/web_protocol/selenium_api.py +3 -3
- pymobiledevice3/services/web_protocol/session_protocol.py +15 -10
- pymobiledevice3/services/web_protocol/switch_to.py +11 -12
- pymobiledevice3/services/webinspector.py +142 -116
- pymobiledevice3/tcp_forwarder.py +35 -22
- pymobiledevice3/tunneld/api.py +20 -15
- pymobiledevice3/tunneld/server.py +310 -193
- pymobiledevice3/usbmux.py +197 -148
- pymobiledevice3/utils.py +14 -11
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/METADATA +1 -2
- pymobiledevice3-5.1.2.dist-info/RECORD +173 -0
- pymobiledevice3-4.27.4.dist-info/RECORD +0 -172
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/WHEEL +0 -0
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
# mbn.py
|
|
2
|
+
# Support for Qualcomm MBN (Modem Binary) formats — Python port
|
|
3
|
+
# Mirrors the logic of mbn.c (v1/v2/BIN headers, ELF detection, and v7 stitching)
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2012 Martin Szulecki
|
|
6
|
+
# Copyright (c) 2012 Nikias Bassen
|
|
7
|
+
# Copyright (c) 2025 Visual Ehrmanntraut <visual@chefkiss.dev>
|
|
8
|
+
#
|
|
9
|
+
# Ported to Python by DoronZ <doron88@gmail.com>. Licensed under LGPL-2.1-or-later (same as original).
|
|
10
|
+
|
|
11
|
+
import io
|
|
12
|
+
import logging
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
from construct import Bytes, ChecksumError, Int16ul, Int32ul, Int64ul, Struct
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
# -----------------------------------------------------------------------------
|
|
20
|
+
# Constants
|
|
21
|
+
# -----------------------------------------------------------------------------
|
|
22
|
+
MBN_V1_MAGIC = b"\x0a\x00\x00\x00"
|
|
23
|
+
MBN_V1_MAGIC_SIZE = 4
|
|
24
|
+
|
|
25
|
+
MBN_V2_MAGIC = b"\xd1\xdc\x4b\x84\x34\x10\xd7\x73"
|
|
26
|
+
MBN_V2_MAGIC_SIZE = 8
|
|
27
|
+
|
|
28
|
+
MBN_BIN_MAGIC = b"\x04\x00\xea\x6c\x69\x48\x55"
|
|
29
|
+
MBN_BIN_MAGIC_SIZE = 7
|
|
30
|
+
MBN_BIN_MAGIC_OFFSET = 1
|
|
31
|
+
|
|
32
|
+
# ELF
|
|
33
|
+
EI_MAG0, EI_MAG1, EI_MAG2, EI_MAG3, EI_CLASS = 0, 1, 2, 3, 4
|
|
34
|
+
EI_NIDENT = 16
|
|
35
|
+
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3 = 0x7F, ord("E"), ord("L"), ord("F")
|
|
36
|
+
ELFCLASSNONE, ELFCLASS32, ELFCLASS64 = 0, 1, 2
|
|
37
|
+
|
|
38
|
+
# -----------------------------------------------------------------------------
|
|
39
|
+
# Construct Structs (little-endian)
|
|
40
|
+
# -----------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
# MBN v1
|
|
43
|
+
MBN_V1 = Struct(
|
|
44
|
+
"type" / Int32ul,
|
|
45
|
+
"unk_0x04" / Int32ul,
|
|
46
|
+
"unk_0x08" / Int32ul,
|
|
47
|
+
"unk_0x0c" / Int32ul,
|
|
48
|
+
"data_size" / Int32ul, # total - sizeof(header)
|
|
49
|
+
"sig_offset" / Int32ul, # real offset = enc_sig_offset & 0xFFFFFF00 (FYI)
|
|
50
|
+
"unk_0x18" / Int32ul,
|
|
51
|
+
"unk_0x1c" / Int32ul,
|
|
52
|
+
"unk_0x20" / Int32ul,
|
|
53
|
+
"unk_0x24" / Int32ul,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# MBN v2
|
|
57
|
+
MBN_V2 = Struct(
|
|
58
|
+
"magic1" / Bytes(8),
|
|
59
|
+
"unk_0x08" / Int32ul,
|
|
60
|
+
"unk_0x0c" / Int32ul, # 0xFFFFFFFF
|
|
61
|
+
"unk_0x10" / Int32ul, # 0xFFFFFFFF
|
|
62
|
+
"header_size" / Int32ul,
|
|
63
|
+
"unk_0x18" / Int32ul,
|
|
64
|
+
"data_size" / Int32ul, # total - sizeof(header)
|
|
65
|
+
"sig_offset" / Int32ul,
|
|
66
|
+
"unk_0x24" / Int32ul,
|
|
67
|
+
"unk_0x28" / Int32ul,
|
|
68
|
+
"unk_0x2c" / Int32ul,
|
|
69
|
+
"unk_0x30" / Int32ul,
|
|
70
|
+
"unk_0x34" / Int32ul, # 0x1
|
|
71
|
+
"unk_0x38" / Int32ul, # 0x1
|
|
72
|
+
"unk_0x3c" / Int32ul, # 0xFFFFFFFF
|
|
73
|
+
"unk_0x40" / Int32ul, # 0xFFFFFFFF
|
|
74
|
+
"unk_0x44" / Int32ul, # 0xFFFFFFFF
|
|
75
|
+
"unk_0x48" / Int32ul, # 0xFFFFFFFF
|
|
76
|
+
"unk_0x4c" / Int32ul, # 0xFFFFFFFF
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# MBN BIN
|
|
80
|
+
MBN_BIN = Struct(
|
|
81
|
+
"magic" / Bytes(8),
|
|
82
|
+
"unk_0x08" / Int32ul,
|
|
83
|
+
"version" / Int32ul,
|
|
84
|
+
"total_size" / Int32ul, # includes header
|
|
85
|
+
"unk_0x14" / Int32ul,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# v7 trailer header used by mbn_mav25_stitch
|
|
89
|
+
MBN_V7 = Struct(
|
|
90
|
+
"reserved" / Int32ul,
|
|
91
|
+
"version" / Int32ul,
|
|
92
|
+
"common_metadata_size" / Int32ul,
|
|
93
|
+
"qti_metadata_size" / Int32ul,
|
|
94
|
+
"oem_metadata_size" / Int32ul,
|
|
95
|
+
"hash_table_size" / Int32ul,
|
|
96
|
+
"qti_signature_size" / Int32ul,
|
|
97
|
+
"qti_certificate_chain_size" / Int32ul,
|
|
98
|
+
"oem_signature_size" / Int32ul,
|
|
99
|
+
"oem_certificate_chain_size" / Int32ul,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# ELF (minimal fields we need)
|
|
103
|
+
ELF32_Ehdr = Struct(
|
|
104
|
+
"e_ident" / Bytes(16),
|
|
105
|
+
"e_type" / Int16ul,
|
|
106
|
+
"e_machine" / Int16ul,
|
|
107
|
+
"e_version" / Int32ul,
|
|
108
|
+
"e_entry" / Int32ul,
|
|
109
|
+
"e_phoff" / Int32ul,
|
|
110
|
+
"e_shoff" / Int32ul,
|
|
111
|
+
"e_flags" / Int32ul,
|
|
112
|
+
"e_ehsize" / Int16ul,
|
|
113
|
+
"e_phentsize" / Int16ul,
|
|
114
|
+
"e_phnum" / Int16ul,
|
|
115
|
+
"e_shentsize" / Int16ul,
|
|
116
|
+
"e_shnum" / Int16ul,
|
|
117
|
+
"e_shstrndx" / Int16ul,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
ELF64_Ehdr = Struct(
|
|
121
|
+
"e_ident" / Bytes(16),
|
|
122
|
+
"e_type" / Int16ul,
|
|
123
|
+
"e_machine" / Int16ul,
|
|
124
|
+
"e_version" / Int32ul,
|
|
125
|
+
"e_entry" / Int64ul,
|
|
126
|
+
"e_phoff" / Int64ul,
|
|
127
|
+
"e_shoff" / Int64ul,
|
|
128
|
+
"e_flags" / Int32ul,
|
|
129
|
+
"e_ehsize" / Int16ul,
|
|
130
|
+
"e_phentsize" / Int16ul,
|
|
131
|
+
"e_phnum" / Int16ul,
|
|
132
|
+
"e_shentsize" / Int16ul,
|
|
133
|
+
"e_shnum" / Int16ul,
|
|
134
|
+
"e_shstrndx" / Int16ul,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
ELF32_Phdr = Struct(
|
|
138
|
+
"p_type" / Int32ul,
|
|
139
|
+
"p_offset" / Int32ul,
|
|
140
|
+
"p_vaddr" / Int32ul,
|
|
141
|
+
"p_paddr" / Int32ul,
|
|
142
|
+
"p_filesz" / Int32ul,
|
|
143
|
+
"p_memsz" / Int32ul,
|
|
144
|
+
"p_flags" / Int32ul,
|
|
145
|
+
"p_align" / Int32ul,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
ELF64_Phdr = Struct(
|
|
149
|
+
"p_type" / Int32ul,
|
|
150
|
+
"p_flags" / Int32ul,
|
|
151
|
+
"p_offset" / Int64ul,
|
|
152
|
+
"p_vaddr" / Int64ul,
|
|
153
|
+
"p_paddr" / Int64ul,
|
|
154
|
+
"p_filesz" / Int64ul,
|
|
155
|
+
"p_memsz" / Int64ul,
|
|
156
|
+
"p_align" / Int64ul,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
# -----------------------------------------------------------------------------
|
|
161
|
+
# Helpers
|
|
162
|
+
# -----------------------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _is_valid_elf_ident(e_ident: bytes) -> bool:
|
|
166
|
+
return (
|
|
167
|
+
len(e_ident) >= EI_NIDENT
|
|
168
|
+
and e_ident[EI_MAG0] == ELFMAG0
|
|
169
|
+
and e_ident[EI_MAG1] == ELFMAG1
|
|
170
|
+
and e_ident[EI_MAG2] == ELFMAG2
|
|
171
|
+
and e_ident[EI_MAG3] == ELFMAG3
|
|
172
|
+
and e_ident[EI_CLASS] != ELFCLASSNONE
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _read_elf_headers(data: bytes):
|
|
177
|
+
if len(data) < EI_NIDENT:
|
|
178
|
+
return None, None
|
|
179
|
+
e_ident = data[:EI_NIDENT]
|
|
180
|
+
if not _is_valid_elf_ident(e_ident):
|
|
181
|
+
return None, None
|
|
182
|
+
if e_ident[EI_CLASS] == ELFCLASS64:
|
|
183
|
+
if len(data) < ELF64_Ehdr.sizeof():
|
|
184
|
+
return None, None
|
|
185
|
+
hdr = ELF64_Ehdr.parse(data[: ELF64_Ehdr.sizeof()])
|
|
186
|
+
return "ELF64", hdr
|
|
187
|
+
elif e_ident[EI_CLASS] == ELFCLASS32:
|
|
188
|
+
if len(data) < ELF32_Ehdr.sizeof():
|
|
189
|
+
return None, None
|
|
190
|
+
hdr = ELF32_Ehdr.parse(data[: ELF32_Ehdr.sizeof()])
|
|
191
|
+
return "ELF32", hdr
|
|
192
|
+
return None, None
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _read_program_headers(data: bytes, kind: str, hdr) -> list:
|
|
196
|
+
phdrs = []
|
|
197
|
+
if hdr.e_phnum == 0:
|
|
198
|
+
logger.error("%s: ELF has no program sections", "_read_program_headers")
|
|
199
|
+
return phdrs
|
|
200
|
+
|
|
201
|
+
table_size = hdr.e_phnum * (ELF64_Phdr.sizeof() if kind == "ELF64" else ELF32_Phdr.sizeof())
|
|
202
|
+
if hdr.e_phoff + table_size > len(data):
|
|
203
|
+
logger.error("%s: Program header table is out of bounds", "_read_program_headers")
|
|
204
|
+
return []
|
|
205
|
+
|
|
206
|
+
table = data[hdr.e_phoff : hdr.e_phoff + table_size]
|
|
207
|
+
bio = io.BytesIO(table)
|
|
208
|
+
for _ in range(hdr.e_phnum):
|
|
209
|
+
ph = ELF64_Phdr.parse_stream(bio) if kind == "ELF64" else ELF32_Phdr.parse_stream(bio)
|
|
210
|
+
phdrs.append(ph)
|
|
211
|
+
return phdrs
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _elf_last_segment_end(data: bytes) -> Optional[int]:
|
|
215
|
+
kind, hdr = _read_elf_headers(data)
|
|
216
|
+
if not hdr:
|
|
217
|
+
return None
|
|
218
|
+
phdrs = _read_program_headers(data, kind, hdr)
|
|
219
|
+
if not phdrs:
|
|
220
|
+
return None
|
|
221
|
+
# last by highest p_offset
|
|
222
|
+
last = max(phdrs, key=lambda p: int(p.p_offset))
|
|
223
|
+
return int(last.p_offset + last.p_filesz)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _mbn_v7_header_sizes_valid(h, sect_size: int) -> bool:
|
|
227
|
+
total = (
|
|
228
|
+
MBN_V7.sizeof()
|
|
229
|
+
+ h.common_metadata_size
|
|
230
|
+
+ h.qti_metadata_size
|
|
231
|
+
+ h.oem_metadata_size
|
|
232
|
+
+ h.hash_table_size
|
|
233
|
+
+ h.qti_signature_size
|
|
234
|
+
+ h.qti_certificate_chain_size
|
|
235
|
+
+ h.oem_signature_size
|
|
236
|
+
+ h.oem_certificate_chain_size
|
|
237
|
+
)
|
|
238
|
+
return total <= sect_size
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _mbn_v7_header_sizes_expected(h) -> bool:
|
|
242
|
+
return (
|
|
243
|
+
(h.qti_metadata_size in (0, 0xE0))
|
|
244
|
+
and (h.oem_metadata_size in (0, 0xE0))
|
|
245
|
+
and (h.oem_signature_size in (0, 0x68))
|
|
246
|
+
and (h.oem_certificate_chain_size in (0, 0xD20))
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _mbn_v7_log(h, func: str, prefix: str) -> None:
|
|
251
|
+
logger.debug(
|
|
252
|
+
"%s: %s header {version=0x%x, common_metadata_size=0x%x, qti_metadata_size=0x%x, "
|
|
253
|
+
"oem_metadata_size=0x%x, hash_table_size=0x%x, qti_signature_size=0x%x, "
|
|
254
|
+
"qti_certificate_chain_size=0x%x, oem_signature_size=0x%x, oem_certificate_chain_size=0x%x}",
|
|
255
|
+
func,
|
|
256
|
+
prefix,
|
|
257
|
+
h.version,
|
|
258
|
+
h.common_metadata_size,
|
|
259
|
+
h.qti_metadata_size,
|
|
260
|
+
h.oem_metadata_size,
|
|
261
|
+
h.hash_table_size,
|
|
262
|
+
h.qti_signature_size,
|
|
263
|
+
h.qti_certificate_chain_size,
|
|
264
|
+
h.oem_signature_size,
|
|
265
|
+
h.oem_certificate_chain_size,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# -----------------------------------------------------------------------------
|
|
270
|
+
# Public API
|
|
271
|
+
# -----------------------------------------------------------------------------
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def mbn_stitch(data: bytes, blob: bytes) -> Optional[bytes]:
|
|
275
|
+
"""
|
|
276
|
+
Overwrite the tail of `data` with `blob`. Format-aware size logging/checks.
|
|
277
|
+
Returns new bytes or None.
|
|
278
|
+
"""
|
|
279
|
+
if data is None:
|
|
280
|
+
logger.error("%s: data is NULL", "mbn_stitch")
|
|
281
|
+
return None
|
|
282
|
+
if not data:
|
|
283
|
+
logger.error("%s: data size is 0", "mbn_stitch")
|
|
284
|
+
return None
|
|
285
|
+
if blob is None:
|
|
286
|
+
logger.error("%s: blob is NULL", "mbn_stitch")
|
|
287
|
+
return None
|
|
288
|
+
if not blob:
|
|
289
|
+
logger.error("%s: blob size is 0", "mbn_stitch")
|
|
290
|
+
return None
|
|
291
|
+
|
|
292
|
+
data_size = len(data)
|
|
293
|
+
blob_size = len(blob)
|
|
294
|
+
parsed_size = 0
|
|
295
|
+
|
|
296
|
+
try:
|
|
297
|
+
# MBN v2
|
|
298
|
+
if data_size > MBN_V2_MAGIC_SIZE and data[:MBN_V2_MAGIC_SIZE] == MBN_V2_MAGIC:
|
|
299
|
+
if data_size < MBN_V2.sizeof():
|
|
300
|
+
logger.error("%s: truncated MBN v2 header", "mbn_stitch")
|
|
301
|
+
return None
|
|
302
|
+
h = MBN_V2.parse(data[: MBN_V2.sizeof()])
|
|
303
|
+
parsed_size = h.data_size + MBN_V2.sizeof()
|
|
304
|
+
logger.debug(
|
|
305
|
+
"%s: encountered MBN v2 image, parsed_size = 0x%x",
|
|
306
|
+
"mbn_stitch",
|
|
307
|
+
parsed_size,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
# MBN v1
|
|
311
|
+
elif data_size > MBN_V1_MAGIC_SIZE and data[:MBN_V1_MAGIC_SIZE] == MBN_V1_MAGIC:
|
|
312
|
+
if data_size < MBN_V1.sizeof():
|
|
313
|
+
logger.error("%s: truncated MBN v1 header", "mbn_stitch")
|
|
314
|
+
return None
|
|
315
|
+
h = MBN_V1.parse(data[: MBN_V1.sizeof()])
|
|
316
|
+
parsed_size = h.data_size + MBN_V1.sizeof()
|
|
317
|
+
logger.debug(
|
|
318
|
+
"%s: encountered MBN v1 image, parsed_size = 0x%x",
|
|
319
|
+
"mbn_stitch",
|
|
320
|
+
parsed_size,
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# BIN
|
|
324
|
+
elif (
|
|
325
|
+
data_size > (MBN_BIN_MAGIC_SIZE + MBN_BIN_MAGIC_OFFSET)
|
|
326
|
+
and data[MBN_BIN_MAGIC_OFFSET : MBN_BIN_MAGIC_OFFSET + MBN_BIN_MAGIC_SIZE] == MBN_BIN_MAGIC
|
|
327
|
+
):
|
|
328
|
+
if data_size < MBN_BIN.sizeof():
|
|
329
|
+
logger.error("%s: truncated MBN BIN header", "mbn_stitch")
|
|
330
|
+
return None
|
|
331
|
+
h = MBN_BIN.parse(data[: MBN_BIN.sizeof()])
|
|
332
|
+
parsed_size = h.total_size
|
|
333
|
+
logger.debug(
|
|
334
|
+
"%s: encountered MBN BIN image, parsed_size = 0x%x",
|
|
335
|
+
"mbn_stitch",
|
|
336
|
+
parsed_size,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
# ELF
|
|
340
|
+
else:
|
|
341
|
+
end = _elf_last_segment_end(data)
|
|
342
|
+
if end is not None:
|
|
343
|
+
parsed_size = end
|
|
344
|
+
logger.debug(
|
|
345
|
+
"%s: encountered ELF image, parsed_size = 0x%x",
|
|
346
|
+
"mbn_stitch",
|
|
347
|
+
parsed_size,
|
|
348
|
+
)
|
|
349
|
+
else:
|
|
350
|
+
logger.warning("Unknown file format passed to %s", "mbn_stitch")
|
|
351
|
+
parsed_size = data_size
|
|
352
|
+
|
|
353
|
+
if parsed_size != data_size:
|
|
354
|
+
logger.warning(
|
|
355
|
+
"%s: size mismatch for MBN data, expected 0x%x, input size 0x%x",
|
|
356
|
+
"mbn_stitch",
|
|
357
|
+
parsed_size,
|
|
358
|
+
data_size,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
stitch_offset = data_size - blob_size
|
|
362
|
+
if stitch_offset < 0 or stitch_offset + blob_size > data_size:
|
|
363
|
+
logger.error(
|
|
364
|
+
"%s: stitch offset (0x%x) + size (0x%x) is larger than the destination (0x%x)",
|
|
365
|
+
"mbn_stitch",
|
|
366
|
+
stitch_offset,
|
|
367
|
+
blob_size,
|
|
368
|
+
data_size,
|
|
369
|
+
)
|
|
370
|
+
return None
|
|
371
|
+
|
|
372
|
+
out = bytearray(data)
|
|
373
|
+
logger.debug(
|
|
374
|
+
"%s: stitching mbn at 0x%x, size 0x%x",
|
|
375
|
+
"mbn_stitch",
|
|
376
|
+
stitch_offset,
|
|
377
|
+
blob_size,
|
|
378
|
+
)
|
|
379
|
+
out[stitch_offset : stitch_offset + blob_size] = blob
|
|
380
|
+
return bytes(out)
|
|
381
|
+
|
|
382
|
+
except ChecksumError:
|
|
383
|
+
logger.exception("mbn_stitch: construct checksum error")
|
|
384
|
+
return None
|
|
385
|
+
except Exception:
|
|
386
|
+
logger.exception("mbn_stitch")
|
|
387
|
+
return None
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def mbn_mav25_stitch(data: bytes, blob: bytes) -> Optional[bytes]:
|
|
391
|
+
"""
|
|
392
|
+
Patch an ELF's last program-section area with a v7 trailer from `blob`.
|
|
393
|
+
- Writes new metadata+hash at section start
|
|
394
|
+
- Writes OEM sig+chain after existing dest qti sig+chain
|
|
395
|
+
Returns new bytes or None.
|
|
396
|
+
"""
|
|
397
|
+
if data is None:
|
|
398
|
+
logger.error("%s: data is NULL", "mbn_mav25_stitch")
|
|
399
|
+
return None
|
|
400
|
+
if not data:
|
|
401
|
+
logger.error("%s: data size is 0", "mbn_mav25_stitch")
|
|
402
|
+
return None
|
|
403
|
+
if blob is None:
|
|
404
|
+
logger.error("%s: blob is NULL", "mbn_mav25_stitch")
|
|
405
|
+
return None
|
|
406
|
+
if not blob:
|
|
407
|
+
logger.error("%s: blob size is 0", "mbn_mav25_stitch")
|
|
408
|
+
return None
|
|
409
|
+
|
|
410
|
+
data_size, blob_size = len(data), len(blob)
|
|
411
|
+
|
|
412
|
+
kind, ehdr = _read_elf_headers(data)
|
|
413
|
+
if not ehdr:
|
|
414
|
+
logger.error("%s: data is not a valid ELF", "mbn_mav25_stitch")
|
|
415
|
+
return None
|
|
416
|
+
|
|
417
|
+
if blob_size < MBN_V7.sizeof():
|
|
418
|
+
logger.error("%s: header is bigger than blob", "mbn_mav25_stitch")
|
|
419
|
+
return None
|
|
420
|
+
|
|
421
|
+
src = MBN_V7.parse(blob[: MBN_V7.sizeof()])
|
|
422
|
+
_mbn_v7_log(src, "mbn_mav25_stitch", "src")
|
|
423
|
+
if src.version != 7:
|
|
424
|
+
logger.error(
|
|
425
|
+
"%s: src header version (0x%x) is incorrect",
|
|
426
|
+
"mbn_mav25_stitch",
|
|
427
|
+
src.version,
|
|
428
|
+
)
|
|
429
|
+
return None
|
|
430
|
+
if not _mbn_v7_header_sizes_expected(src):
|
|
431
|
+
logger.warning(
|
|
432
|
+
"%s: header sizes in header are unexpected (qti_metadata_size=0x%x, oem_metadata_size=0x%x, "
|
|
433
|
+
"oem_signature_size=0x%x, oem_certificate_chain_size=0x%x)",
|
|
434
|
+
"mbn_mav25_stitch",
|
|
435
|
+
src.qti_metadata_size,
|
|
436
|
+
src.oem_metadata_size,
|
|
437
|
+
src.oem_signature_size,
|
|
438
|
+
src.oem_certificate_chain_size,
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
# last PHDR (by index)
|
|
442
|
+
phdrs = _read_program_headers(data, kind, ehdr)
|
|
443
|
+
if not phdrs:
|
|
444
|
+
return None
|
|
445
|
+
last_ph = phdrs[-1]
|
|
446
|
+
sect_off, sect_size = int(last_ph.p_offset), int(last_ph.p_filesz)
|
|
447
|
+
|
|
448
|
+
if sect_off == 0:
|
|
449
|
+
logger.error("%s: section has 0 offset", "mbn_mav25_stitch")
|
|
450
|
+
return None
|
|
451
|
+
if sect_size == 0:
|
|
452
|
+
logger.error("%s: section has 0 size", "mbn_mav25_stitch")
|
|
453
|
+
return None
|
|
454
|
+
if sect_off + sect_size > data_size:
|
|
455
|
+
logger.error(
|
|
456
|
+
"%s: section (0x%x+0x%x) is bigger than the data",
|
|
457
|
+
"mbn_mav25_stitch",
|
|
458
|
+
sect_off,
|
|
459
|
+
sect_size,
|
|
460
|
+
)
|
|
461
|
+
return None
|
|
462
|
+
if sect_size < MBN_V7.sizeof():
|
|
463
|
+
logger.error(
|
|
464
|
+
"%s: dest header is bigger than the section (0x%x)",
|
|
465
|
+
"mbn_mav25_stitch",
|
|
466
|
+
sect_size,
|
|
467
|
+
)
|
|
468
|
+
return None
|
|
469
|
+
|
|
470
|
+
dest = MBN_V7.parse(data[sect_off : sect_off + MBN_V7.sizeof()])
|
|
471
|
+
_mbn_v7_log(dest, "mbn_mav25_stitch", "dest")
|
|
472
|
+
if dest.version != 7:
|
|
473
|
+
logger.error(
|
|
474
|
+
"%s: dest header version (0x%x) is incorrect",
|
|
475
|
+
"mbn_mav25_stitch",
|
|
476
|
+
dest.version,
|
|
477
|
+
)
|
|
478
|
+
return None
|
|
479
|
+
if not _mbn_v7_header_sizes_valid(dest, sect_size):
|
|
480
|
+
logger.error(
|
|
481
|
+
(
|
|
482
|
+
"%s: sizes in dest header are invalid (common_metadata_size=0x%x, qti_metadata_size=0x%x, "
|
|
483
|
+
"oem_metadata_size=0x%x, hash_table_size=0x%x, qti_signature_size=0x%x, "
|
|
484
|
+
"qti_certificate_chain_size=0x%x, oem_signature_size=0x%x, "
|
|
485
|
+
"oem_certificate_chain_size=0x%x)"
|
|
486
|
+
),
|
|
487
|
+
"mbn_mav25_stitch",
|
|
488
|
+
dest.common_metadata_size,
|
|
489
|
+
dest.qti_metadata_size,
|
|
490
|
+
dest.oem_metadata_size,
|
|
491
|
+
dest.hash_table_size,
|
|
492
|
+
dest.qti_signature_size,
|
|
493
|
+
dest.qti_certificate_chain_size,
|
|
494
|
+
dest.oem_signature_size,
|
|
495
|
+
dest.oem_certificate_chain_size,
|
|
496
|
+
)
|
|
497
|
+
return None
|
|
498
|
+
if not _mbn_v7_header_sizes_expected(dest):
|
|
499
|
+
logger.warning(
|
|
500
|
+
"%s: header sizes in dest header are unexpected (qti_metadata_size=0x%x, oem_metadata_size=0x%x, "
|
|
501
|
+
"oem_signature_size=0x%x, oem_certificate_chain_size=0x%x)",
|
|
502
|
+
"mbn_mav25_stitch",
|
|
503
|
+
dest.qti_metadata_size,
|
|
504
|
+
dest.oem_metadata_size,
|
|
505
|
+
dest.oem_signature_size,
|
|
506
|
+
dest.oem_certificate_chain_size,
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
# compute new layout from src
|
|
510
|
+
new_metadata_size = MBN_V7.sizeof() + src.common_metadata_size + src.qti_metadata_size + src.oem_metadata_size
|
|
511
|
+
new_metadata_and_hash_table_size = new_metadata_size + src.hash_table_size
|
|
512
|
+
new_oem_sig_and_cert_chain_size = src.oem_signature_size + src.oem_certificate_chain_size
|
|
513
|
+
|
|
514
|
+
new_oem_sig_and_cert_chain_off = (
|
|
515
|
+
new_metadata_and_hash_table_size + dest.qti_signature_size + dest.qti_certificate_chain_size
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
# bounds
|
|
519
|
+
if new_metadata_and_hash_table_size > blob_size:
|
|
520
|
+
logger.error(
|
|
521
|
+
"%s: new metadata (0x%x) and hash table (0x%x) are bigger than the source (0x%x)",
|
|
522
|
+
"mbn_mav25_stitch",
|
|
523
|
+
new_metadata_size,
|
|
524
|
+
src.hash_table_size,
|
|
525
|
+
blob_size,
|
|
526
|
+
)
|
|
527
|
+
return None
|
|
528
|
+
if new_metadata_and_hash_table_size > sect_size:
|
|
529
|
+
logger.error(
|
|
530
|
+
"%s: new metadata (0x%x) and hash table (0x%x) are bigger than the destination (0x%x)",
|
|
531
|
+
"mbn_mav25_stitch",
|
|
532
|
+
new_metadata_size,
|
|
533
|
+
src.hash_table_size,
|
|
534
|
+
sect_size,
|
|
535
|
+
)
|
|
536
|
+
return None
|
|
537
|
+
if new_metadata_and_hash_table_size + new_oem_sig_and_cert_chain_size > blob_size:
|
|
538
|
+
logger.error(
|
|
539
|
+
"%s: new OEM signature and certificate chain are bigger than the source",
|
|
540
|
+
"mbn_mav25_stitch",
|
|
541
|
+
)
|
|
542
|
+
return None
|
|
543
|
+
if new_oem_sig_and_cert_chain_off + new_oem_sig_and_cert_chain_size > sect_size:
|
|
544
|
+
logger.error(
|
|
545
|
+
"%s: new OEM signature and certificate chain are outside the bounds of the destination",
|
|
546
|
+
"mbn_mav25_stitch",
|
|
547
|
+
)
|
|
548
|
+
return None
|
|
549
|
+
|
|
550
|
+
out = bytearray(data)
|
|
551
|
+
# write metadata + hash
|
|
552
|
+
logger.debug(
|
|
553
|
+
"%s: stitching mbn at 0x%x (0x%x bytes)",
|
|
554
|
+
"mbn_mav25_stitch",
|
|
555
|
+
sect_off,
|
|
556
|
+
new_metadata_and_hash_table_size,
|
|
557
|
+
)
|
|
558
|
+
out[sect_off : sect_off + new_metadata_and_hash_table_size] = blob[:new_metadata_and_hash_table_size]
|
|
559
|
+
|
|
560
|
+
# write OEM sig + chain
|
|
561
|
+
logger.debug(
|
|
562
|
+
"%s: stitching mbn at 0x%x (0x%x bytes)",
|
|
563
|
+
"mbn_mav25_stitch",
|
|
564
|
+
sect_off + new_oem_sig_and_cert_chain_off,
|
|
565
|
+
new_oem_sig_and_cert_chain_size,
|
|
566
|
+
)
|
|
567
|
+
start = new_metadata_and_hash_table_size
|
|
568
|
+
end = start + new_oem_sig_and_cert_chain_size
|
|
569
|
+
|
|
570
|
+
slice_start = sect_off + new_oem_sig_and_cert_chain_off
|
|
571
|
+
slice_end = sect_off + new_oem_sig_and_cert_chain_off + new_oem_sig_and_cert_chain_size
|
|
572
|
+
out[slice_start:slice_end] = blob[start:end]
|
|
573
|
+
|
|
574
|
+
return bytes(out)
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
# -----------------------------------------------------------------------------
|
|
578
|
+
# Tiny helpers (optional)
|
|
579
|
+
# -----------------------------------------------------------------------------
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def mbn_is_valid_elf(buf: bytes) -> bool:
|
|
583
|
+
return len(buf) >= EI_NIDENT and _is_valid_elf_ident(buf[:EI_NIDENT])
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def mbn_is_64bit_elf(buf: bytes) -> bool:
|
|
587
|
+
return len(buf) >= EI_NIDENT and buf[EI_CLASS] == ELFCLASS64
|