pymobiledevice3 4.27.4__py3-none-any.whl → 4.27.6__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.
Potentially problematic release.
This version of pymobiledevice3 might be problematic. Click here for more details.
- pymobiledevice3/_version.py +2 -2
- pymobiledevice3/restore/mbn.py +606 -0
- pymobiledevice3/restore/restore.py +22 -10
- pymobiledevice3/services/afc.py +3 -2
- pymobiledevice3/services/crash_reports.py +1 -1
- pymobiledevice3/services/webinspector.py +19 -5
- pymobiledevice3/tunneld/server.py +3 -2
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-4.27.6.dist-info}/METADATA +1 -1
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-4.27.6.dist-info}/RECORD +13 -12
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-4.27.6.dist-info}/WHEEL +0 -0
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-4.27.6.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-4.27.6.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-4.27.6.dist-info}/top_level.txt +0 -0
pymobiledevice3/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '4.27.
|
|
32
|
-
__version_tuple__ = version_tuple = (4, 27,
|
|
31
|
+
__version__ = version = '4.27.6'
|
|
32
|
+
__version_tuple__ = version_tuple = (4, 27, 6)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,606 @@
|
|
|
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 * (
|
|
202
|
+
ELF64_Phdr.sizeof() if kind == "ELF64" else ELF32_Phdr.sizeof()
|
|
203
|
+
)
|
|
204
|
+
if hdr.e_phoff + table_size > len(data):
|
|
205
|
+
logger.error(
|
|
206
|
+
"%s: Program header table is out of bounds", "_read_program_headers"
|
|
207
|
+
)
|
|
208
|
+
return []
|
|
209
|
+
|
|
210
|
+
table = data[hdr.e_phoff:hdr.e_phoff + table_size]
|
|
211
|
+
bio = io.BytesIO(table)
|
|
212
|
+
for _ in range(hdr.e_phnum):
|
|
213
|
+
if kind == "ELF64":
|
|
214
|
+
ph = ELF64_Phdr.parse_stream(bio)
|
|
215
|
+
else:
|
|
216
|
+
ph = ELF32_Phdr.parse_stream(bio)
|
|
217
|
+
phdrs.append(ph)
|
|
218
|
+
return phdrs
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _elf_last_segment_end(data: bytes) -> Optional[int]:
|
|
222
|
+
kind, hdr = _read_elf_headers(data)
|
|
223
|
+
if not hdr:
|
|
224
|
+
return None
|
|
225
|
+
phdrs = _read_program_headers(data, kind, hdr)
|
|
226
|
+
if not phdrs:
|
|
227
|
+
return None
|
|
228
|
+
# last by highest p_offset
|
|
229
|
+
last = max(phdrs, key=lambda p: int(p.p_offset))
|
|
230
|
+
return int(last.p_offset + last.p_filesz)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _mbn_v7_header_sizes_valid(h, sect_size: int) -> bool:
|
|
234
|
+
total = (
|
|
235
|
+
MBN_V7.sizeof()
|
|
236
|
+
+ h.common_metadata_size
|
|
237
|
+
+ h.qti_metadata_size
|
|
238
|
+
+ h.oem_metadata_size
|
|
239
|
+
+ h.hash_table_size
|
|
240
|
+
+ h.qti_signature_size
|
|
241
|
+
+ h.qti_certificate_chain_size
|
|
242
|
+
+ h.oem_signature_size
|
|
243
|
+
+ h.oem_certificate_chain_size
|
|
244
|
+
)
|
|
245
|
+
return total <= sect_size
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _mbn_v7_header_sizes_expected(h) -> bool:
|
|
249
|
+
return (
|
|
250
|
+
(h.qti_metadata_size in (0, 0xE0))
|
|
251
|
+
and (h.oem_metadata_size in (0, 0xE0))
|
|
252
|
+
and (h.oem_signature_size in (0, 0x68))
|
|
253
|
+
and (h.oem_certificate_chain_size in (0, 0xD20))
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _mbn_v7_log(h, func: str, prefix: str) -> None:
|
|
258
|
+
logger.debug(
|
|
259
|
+
"%s: %s header {version=0x%x, common_metadata_size=0x%x, qti_metadata_size=0x%x, "
|
|
260
|
+
"oem_metadata_size=0x%x, hash_table_size=0x%x, qti_signature_size=0x%x, "
|
|
261
|
+
"qti_certificate_chain_size=0x%x, oem_signature_size=0x%x, oem_certificate_chain_size=0x%x}",
|
|
262
|
+
func,
|
|
263
|
+
prefix,
|
|
264
|
+
h.version,
|
|
265
|
+
h.common_metadata_size,
|
|
266
|
+
h.qti_metadata_size,
|
|
267
|
+
h.oem_metadata_size,
|
|
268
|
+
h.hash_table_size,
|
|
269
|
+
h.qti_signature_size,
|
|
270
|
+
h.qti_certificate_chain_size,
|
|
271
|
+
h.oem_signature_size,
|
|
272
|
+
h.oem_certificate_chain_size,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# -----------------------------------------------------------------------------
|
|
277
|
+
# Public API
|
|
278
|
+
# -----------------------------------------------------------------------------
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def mbn_stitch(data: bytes, blob: bytes) -> Optional[bytes]:
|
|
282
|
+
"""
|
|
283
|
+
Overwrite the tail of `data` with `blob`. Format-aware size logging/checks.
|
|
284
|
+
Returns new bytes or None.
|
|
285
|
+
"""
|
|
286
|
+
if data is None:
|
|
287
|
+
logger.error("%s: data is NULL", "mbn_stitch")
|
|
288
|
+
return None
|
|
289
|
+
if not data:
|
|
290
|
+
logger.error("%s: data size is 0", "mbn_stitch")
|
|
291
|
+
return None
|
|
292
|
+
if blob is None:
|
|
293
|
+
logger.error("%s: blob is NULL", "mbn_stitch")
|
|
294
|
+
return None
|
|
295
|
+
if not blob:
|
|
296
|
+
logger.error("%s: blob size is 0", "mbn_stitch")
|
|
297
|
+
return None
|
|
298
|
+
|
|
299
|
+
data_size = len(data)
|
|
300
|
+
blob_size = len(blob)
|
|
301
|
+
parsed_size = 0
|
|
302
|
+
|
|
303
|
+
try:
|
|
304
|
+
# MBN v2
|
|
305
|
+
if data_size > MBN_V2_MAGIC_SIZE and data[:MBN_V2_MAGIC_SIZE] == MBN_V2_MAGIC:
|
|
306
|
+
if data_size < MBN_V2.sizeof():
|
|
307
|
+
logger.error("%s: truncated MBN v2 header", "mbn_stitch")
|
|
308
|
+
return None
|
|
309
|
+
h = MBN_V2.parse(data[: MBN_V2.sizeof()])
|
|
310
|
+
parsed_size = h.data_size + MBN_V2.sizeof()
|
|
311
|
+
logger.debug(
|
|
312
|
+
"%s: encountered MBN v2 image, parsed_size = 0x%x",
|
|
313
|
+
"mbn_stitch",
|
|
314
|
+
parsed_size,
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# MBN v1
|
|
318
|
+
elif data_size > MBN_V1_MAGIC_SIZE and data[:MBN_V1_MAGIC_SIZE] == MBN_V1_MAGIC:
|
|
319
|
+
if data_size < MBN_V1.sizeof():
|
|
320
|
+
logger.error("%s: truncated MBN v1 header", "mbn_stitch")
|
|
321
|
+
return None
|
|
322
|
+
h = MBN_V1.parse(data[: MBN_V1.sizeof()])
|
|
323
|
+
parsed_size = h.data_size + MBN_V1.sizeof()
|
|
324
|
+
logger.debug(
|
|
325
|
+
"%s: encountered MBN v1 image, parsed_size = 0x%x",
|
|
326
|
+
"mbn_stitch",
|
|
327
|
+
parsed_size,
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
# BIN
|
|
331
|
+
elif (
|
|
332
|
+
data_size > (MBN_BIN_MAGIC_SIZE + MBN_BIN_MAGIC_OFFSET)
|
|
333
|
+
and data[MBN_BIN_MAGIC_OFFSET:MBN_BIN_MAGIC_OFFSET + MBN_BIN_MAGIC_SIZE]
|
|
334
|
+
== MBN_BIN_MAGIC
|
|
335
|
+
):
|
|
336
|
+
if data_size < MBN_BIN.sizeof():
|
|
337
|
+
logger.error("%s: truncated MBN BIN header", "mbn_stitch")
|
|
338
|
+
return None
|
|
339
|
+
h = MBN_BIN.parse(data[: MBN_BIN.sizeof()])
|
|
340
|
+
parsed_size = h.total_size
|
|
341
|
+
logger.debug(
|
|
342
|
+
"%s: encountered MBN BIN image, parsed_size = 0x%x",
|
|
343
|
+
"mbn_stitch",
|
|
344
|
+
parsed_size,
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
# ELF
|
|
348
|
+
else:
|
|
349
|
+
end = _elf_last_segment_end(data)
|
|
350
|
+
if end is not None:
|
|
351
|
+
parsed_size = end
|
|
352
|
+
logger.debug(
|
|
353
|
+
"%s: encountered ELF image, parsed_size = 0x%x",
|
|
354
|
+
"mbn_stitch",
|
|
355
|
+
parsed_size,
|
|
356
|
+
)
|
|
357
|
+
else:
|
|
358
|
+
logger.warning("Unknown file format passed to %s", "mbn_stitch")
|
|
359
|
+
parsed_size = data_size
|
|
360
|
+
|
|
361
|
+
if parsed_size != data_size:
|
|
362
|
+
logger.warning(
|
|
363
|
+
"%s: size mismatch for MBN data, expected 0x%x, input size 0x%x",
|
|
364
|
+
"mbn_stitch",
|
|
365
|
+
parsed_size,
|
|
366
|
+
data_size,
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
stitch_offset = data_size - blob_size
|
|
370
|
+
if stitch_offset < 0 or stitch_offset + blob_size > data_size:
|
|
371
|
+
logger.error(
|
|
372
|
+
"%s: stitch offset (0x%x) + size (0x%x) is larger than the destination (0x%x)",
|
|
373
|
+
"mbn_stitch",
|
|
374
|
+
stitch_offset,
|
|
375
|
+
blob_size,
|
|
376
|
+
data_size,
|
|
377
|
+
)
|
|
378
|
+
return None
|
|
379
|
+
|
|
380
|
+
out = bytearray(data)
|
|
381
|
+
logger.debug(
|
|
382
|
+
"%s: stitching mbn at 0x%x, size 0x%x",
|
|
383
|
+
"mbn_stitch",
|
|
384
|
+
stitch_offset,
|
|
385
|
+
blob_size,
|
|
386
|
+
)
|
|
387
|
+
out[stitch_offset: stitch_offset + blob_size] = blob
|
|
388
|
+
return bytes(out)
|
|
389
|
+
|
|
390
|
+
except ChecksumError as e:
|
|
391
|
+
logger.error("%s: construct checksum error: %s", "mbn_stitch", str(e))
|
|
392
|
+
return None
|
|
393
|
+
except Exception as e:
|
|
394
|
+
logger.error("%s: unexpected error: %s", "mbn_stitch", str(e))
|
|
395
|
+
return None
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def mbn_mav25_stitch(data: bytes, blob: bytes) -> Optional[bytes]:
|
|
399
|
+
"""
|
|
400
|
+
Patch an ELF's last program-section area with a v7 trailer from `blob`.
|
|
401
|
+
- Writes new metadata+hash at section start
|
|
402
|
+
- Writes OEM sig+chain after existing dest qti sig+chain
|
|
403
|
+
Returns new bytes or None.
|
|
404
|
+
"""
|
|
405
|
+
if data is None:
|
|
406
|
+
logger.error("%s: data is NULL", "mbn_mav25_stitch")
|
|
407
|
+
return None
|
|
408
|
+
if not data:
|
|
409
|
+
logger.error("%s: data size is 0", "mbn_mav25_stitch")
|
|
410
|
+
return None
|
|
411
|
+
if blob is None:
|
|
412
|
+
logger.error("%s: blob is NULL", "mbn_mav25_stitch")
|
|
413
|
+
return None
|
|
414
|
+
if not blob:
|
|
415
|
+
logger.error("%s: blob size is 0", "mbn_mav25_stitch")
|
|
416
|
+
return None
|
|
417
|
+
|
|
418
|
+
data_size, blob_size = len(data), len(blob)
|
|
419
|
+
|
|
420
|
+
kind, ehdr = _read_elf_headers(data)
|
|
421
|
+
if not ehdr:
|
|
422
|
+
logger.error("%s: data is not a valid ELF", "mbn_mav25_stitch")
|
|
423
|
+
return None
|
|
424
|
+
|
|
425
|
+
if blob_size < MBN_V7.sizeof():
|
|
426
|
+
logger.error("%s: header is bigger than blob", "mbn_mav25_stitch")
|
|
427
|
+
return None
|
|
428
|
+
|
|
429
|
+
src = MBN_V7.parse(blob[: MBN_V7.sizeof()])
|
|
430
|
+
_mbn_v7_log(src, "mbn_mav25_stitch", "src")
|
|
431
|
+
if src.version != 7:
|
|
432
|
+
logger.error(
|
|
433
|
+
"%s: src header version (0x%x) is incorrect",
|
|
434
|
+
"mbn_mav25_stitch",
|
|
435
|
+
src.version,
|
|
436
|
+
)
|
|
437
|
+
return None
|
|
438
|
+
if not _mbn_v7_header_sizes_expected(src):
|
|
439
|
+
logger.warning(
|
|
440
|
+
"%s: header sizes in header are unexpected (qti_metadata_size=0x%x, oem_metadata_size=0x%x, "
|
|
441
|
+
"oem_signature_size=0x%x, oem_certificate_chain_size=0x%x)",
|
|
442
|
+
"mbn_mav25_stitch",
|
|
443
|
+
src.qti_metadata_size,
|
|
444
|
+
src.oem_metadata_size,
|
|
445
|
+
src.oem_signature_size,
|
|
446
|
+
src.oem_certificate_chain_size,
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
# last PHDR (by index)
|
|
450
|
+
phdrs = _read_program_headers(data, kind, ehdr)
|
|
451
|
+
if not phdrs:
|
|
452
|
+
return None
|
|
453
|
+
last_ph = phdrs[-1]
|
|
454
|
+
sect_off, sect_size = int(last_ph.p_offset), int(last_ph.p_filesz)
|
|
455
|
+
|
|
456
|
+
if sect_off == 0:
|
|
457
|
+
logger.error("%s: section has 0 offset", "mbn_mav25_stitch")
|
|
458
|
+
return None
|
|
459
|
+
if sect_size == 0:
|
|
460
|
+
logger.error("%s: section has 0 size", "mbn_mav25_stitch")
|
|
461
|
+
return None
|
|
462
|
+
if sect_off + sect_size > data_size:
|
|
463
|
+
logger.error(
|
|
464
|
+
"%s: section (0x%x+0x%x) is bigger than the data",
|
|
465
|
+
"mbn_mav25_stitch",
|
|
466
|
+
sect_off,
|
|
467
|
+
sect_size,
|
|
468
|
+
)
|
|
469
|
+
return None
|
|
470
|
+
if sect_size < MBN_V7.sizeof():
|
|
471
|
+
logger.error(
|
|
472
|
+
"%s: dest header is bigger than the section (0x%x)",
|
|
473
|
+
"mbn_mav25_stitch",
|
|
474
|
+
sect_size,
|
|
475
|
+
)
|
|
476
|
+
return None
|
|
477
|
+
|
|
478
|
+
dest = MBN_V7.parse(data[sect_off: sect_off + MBN_V7.sizeof()])
|
|
479
|
+
_mbn_v7_log(dest, "mbn_mav25_stitch", "dest")
|
|
480
|
+
if dest.version != 7:
|
|
481
|
+
logger.error(
|
|
482
|
+
"%s: dest header version (0x%x) is incorrect",
|
|
483
|
+
"mbn_mav25_stitch",
|
|
484
|
+
dest.version,
|
|
485
|
+
)
|
|
486
|
+
return None
|
|
487
|
+
if not _mbn_v7_header_sizes_valid(dest, sect_size):
|
|
488
|
+
logger.error(
|
|
489
|
+
(
|
|
490
|
+
"%s: sizes in dest header are invalid (common_metadata_size=0x%x, qti_metadata_size=0x%x, "
|
|
491
|
+
"oem_metadata_size=0x%x, hash_table_size=0x%x, qti_signature_size=0x%x, "
|
|
492
|
+
"qti_certificate_chain_size=0x%x, oem_signature_size=0x%x, "
|
|
493
|
+
"oem_certificate_chain_size=0x%x)"
|
|
494
|
+
),
|
|
495
|
+
"mbn_mav25_stitch",
|
|
496
|
+
dest.common_metadata_size,
|
|
497
|
+
dest.qti_metadata_size,
|
|
498
|
+
dest.oem_metadata_size,
|
|
499
|
+
dest.hash_table_size,
|
|
500
|
+
dest.qti_signature_size,
|
|
501
|
+
dest.qti_certificate_chain_size,
|
|
502
|
+
dest.oem_signature_size,
|
|
503
|
+
dest.oem_certificate_chain_size,
|
|
504
|
+
)
|
|
505
|
+
return None
|
|
506
|
+
if not _mbn_v7_header_sizes_expected(dest):
|
|
507
|
+
logger.warning(
|
|
508
|
+
"%s: header sizes in dest header are unexpected (qti_metadata_size=0x%x, oem_metadata_size=0x%x, "
|
|
509
|
+
"oem_signature_size=0x%x, oem_certificate_chain_size=0x%x)",
|
|
510
|
+
"mbn_mav25_stitch",
|
|
511
|
+
dest.qti_metadata_size,
|
|
512
|
+
dest.oem_metadata_size,
|
|
513
|
+
dest.oem_signature_size,
|
|
514
|
+
dest.oem_certificate_chain_size,
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
# compute new layout from src
|
|
518
|
+
new_metadata_size = (
|
|
519
|
+
MBN_V7.sizeof()
|
|
520
|
+
+ src.common_metadata_size
|
|
521
|
+
+ src.qti_metadata_size
|
|
522
|
+
+ src.oem_metadata_size
|
|
523
|
+
)
|
|
524
|
+
new_metadata_and_hash_table_size = new_metadata_size + src.hash_table_size
|
|
525
|
+
new_oem_sig_and_cert_chain_size = (
|
|
526
|
+
src.oem_signature_size + src.oem_certificate_chain_size
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
new_oem_sig_and_cert_chain_off = (
|
|
530
|
+
new_metadata_and_hash_table_size
|
|
531
|
+
+ dest.qti_signature_size
|
|
532
|
+
+ dest.qti_certificate_chain_size
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
# bounds
|
|
536
|
+
if new_metadata_and_hash_table_size > blob_size:
|
|
537
|
+
logger.error(
|
|
538
|
+
"%s: new metadata (0x%x) and hash table (0x%x) are bigger than the source (0x%x)",
|
|
539
|
+
"mbn_mav25_stitch",
|
|
540
|
+
new_metadata_size,
|
|
541
|
+
src.hash_table_size,
|
|
542
|
+
blob_size,
|
|
543
|
+
)
|
|
544
|
+
return None
|
|
545
|
+
if new_metadata_and_hash_table_size > sect_size:
|
|
546
|
+
logger.error(
|
|
547
|
+
"%s: new metadata (0x%x) and hash table (0x%x) are bigger than the destination (0x%x)",
|
|
548
|
+
"mbn_mav25_stitch",
|
|
549
|
+
new_metadata_size,
|
|
550
|
+
src.hash_table_size,
|
|
551
|
+
sect_size,
|
|
552
|
+
)
|
|
553
|
+
return None
|
|
554
|
+
if new_metadata_and_hash_table_size + new_oem_sig_and_cert_chain_size > blob_size:
|
|
555
|
+
logger.error(
|
|
556
|
+
"%s: new OEM signature and certificate chain are bigger than the source",
|
|
557
|
+
"mbn_mav25_stitch",
|
|
558
|
+
)
|
|
559
|
+
return None
|
|
560
|
+
if new_oem_sig_and_cert_chain_off + new_oem_sig_and_cert_chain_size > sect_size:
|
|
561
|
+
logger.error(
|
|
562
|
+
"%s: new OEM signature and certificate chain are outside the bounds of the destination",
|
|
563
|
+
"mbn_mav25_stitch",
|
|
564
|
+
)
|
|
565
|
+
return None
|
|
566
|
+
|
|
567
|
+
out = bytearray(data)
|
|
568
|
+
# write metadata + hash
|
|
569
|
+
logger.debug(
|
|
570
|
+
"%s: stitching mbn at 0x%x (0x%x bytes)",
|
|
571
|
+
"mbn_mav25_stitch",
|
|
572
|
+
sect_off,
|
|
573
|
+
new_metadata_and_hash_table_size,
|
|
574
|
+
)
|
|
575
|
+
out[sect_off: sect_off + new_metadata_and_hash_table_size] = blob[
|
|
576
|
+
:new_metadata_and_hash_table_size
|
|
577
|
+
]
|
|
578
|
+
|
|
579
|
+
# write OEM sig + chain
|
|
580
|
+
logger.debug(
|
|
581
|
+
"%s: stitching mbn at 0x%x (0x%x bytes)",
|
|
582
|
+
"mbn_mav25_stitch",
|
|
583
|
+
sect_off + new_oem_sig_and_cert_chain_off,
|
|
584
|
+
new_oem_sig_and_cert_chain_size,
|
|
585
|
+
)
|
|
586
|
+
start = new_metadata_and_hash_table_size
|
|
587
|
+
end = start + new_oem_sig_and_cert_chain_size
|
|
588
|
+
|
|
589
|
+
slice_start = sect_off + new_oem_sig_and_cert_chain_off
|
|
590
|
+
slice_end = sect_off + new_oem_sig_and_cert_chain_off + new_oem_sig_and_cert_chain_size
|
|
591
|
+
out[slice_start:slice_end] = blob[start:end]
|
|
592
|
+
|
|
593
|
+
return bytes(out)
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
# -----------------------------------------------------------------------------
|
|
597
|
+
# Tiny helpers (optional)
|
|
598
|
+
# -----------------------------------------------------------------------------
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def mbn_is_valid_elf(buf: bytes) -> bool:
|
|
602
|
+
return len(buf) >= EI_NIDENT and _is_valid_elf_ident(buf[:EI_NIDENT])
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def mbn_is_64bit_elf(buf: bytes) -> bool:
|
|
606
|
+
return len(buf) >= EI_NIDENT and buf[EI_CLASS] == ELFCLASS64
|
|
@@ -23,12 +23,13 @@ from pymobiledevice3.restore.consts import PROGRESS_BAR_OPERATIONS, lpol_file
|
|
|
23
23
|
from pymobiledevice3.restore.device import Device
|
|
24
24
|
from pymobiledevice3.restore.fdr import FDRClient, fdr_type, start_fdr_thread
|
|
25
25
|
from pymobiledevice3.restore.ftab import Ftab
|
|
26
|
+
from pymobiledevice3.restore.mbn import mbn_mav25_stitch, mbn_stitch
|
|
26
27
|
from pymobiledevice3.restore.recovery import Behavior, Recovery
|
|
27
28
|
from pymobiledevice3.restore.restore_options import RestoreOptions
|
|
28
29
|
from pymobiledevice3.restore.restored_client import RestoredClient
|
|
29
30
|
from pymobiledevice3.restore.tss import TSSRequest, TSSResponse
|
|
30
31
|
from pymobiledevice3.service_connection import ServiceConnection
|
|
31
|
-
from pymobiledevice3.utils import plist_access_path
|
|
32
|
+
from pymobiledevice3.utils import asyncio_print_traceback, plist_access_path
|
|
32
33
|
|
|
33
34
|
known_errors = {
|
|
34
35
|
0xFFFFFFFFFFFFFFFF: 'verification error',
|
|
@@ -442,7 +443,7 @@ class Restore(BaseRestore):
|
|
|
442
443
|
await service.aio_send_plist(req)
|
|
443
444
|
|
|
444
445
|
@staticmethod
|
|
445
|
-
def get_bbfw_fn_for_element(elem):
|
|
446
|
+
def get_bbfw_fn_for_element(elem: str, bb_chip_id: Optional[int] = None) -> str:
|
|
446
447
|
bbfw_fn_elem = {
|
|
447
448
|
# ICE3 firmware files
|
|
448
449
|
'RamPSI': 'psi_ram.fls',
|
|
@@ -465,7 +466,16 @@ class Restore(BaseRestore):
|
|
|
465
466
|
# Mav20 Firmware file
|
|
466
467
|
'Misc': 'multi_image.mbn',
|
|
467
468
|
}
|
|
468
|
-
|
|
469
|
+
|
|
470
|
+
bbfw_fn_elem_mav25 = {
|
|
471
|
+
# Mav25 Firmware files
|
|
472
|
+
'Misc': 'multi_image.mbn',
|
|
473
|
+
'RestoreSBL1': 'restorexbl_sc.elf',
|
|
474
|
+
'SBL1': 'xbl_sc.elf',
|
|
475
|
+
'TME': 'signed_firmware_soc_view.elf',
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return bbfw_fn_elem_mav25.get(elem) if bb_chip_id == 0x1F30E1 else bbfw_fn_elem.get(elem)
|
|
469
479
|
|
|
470
480
|
def fls_parse(self, buffer):
|
|
471
481
|
raise NotImplementedError()
|
|
@@ -476,7 +486,8 @@ class Restore(BaseRestore):
|
|
|
476
486
|
def fls_insert_ticket(self, fls, bbticket):
|
|
477
487
|
raise NotImplementedError()
|
|
478
488
|
|
|
479
|
-
def sign_bbfw(self, bbfw_orig, bbtss, bb_nonce
|
|
489
|
+
def sign_bbfw(self, bbfw_orig: bytes, bbtss: TSSResponse, bb_nonce: Optional[bytes],
|
|
490
|
+
bb_chip_id: Optional[int] = None) -> bytes:
|
|
480
491
|
# check for BBTicket in result
|
|
481
492
|
bbticket = bbtss.bb_ticket
|
|
482
493
|
bbfw_dict = bbtss.get('BasebandFirmware')
|
|
@@ -495,7 +506,7 @@ class Restore(BaseRestore):
|
|
|
495
506
|
for key, blob in bbfw_dict.items():
|
|
496
507
|
if key.endswith('-Blob') and isinstance(blob, bytes):
|
|
497
508
|
key = key.split('-', 1)[0]
|
|
498
|
-
signfn = self.get_bbfw_fn_for_element(key)
|
|
509
|
+
signfn = self.get_bbfw_fn_for_element(key, bb_chip_id)
|
|
499
510
|
|
|
500
511
|
if signfn is None:
|
|
501
512
|
raise PyMobileDevice3Exception(
|
|
@@ -507,11 +518,11 @@ class Restore(BaseRestore):
|
|
|
507
518
|
buffer = bbfw_orig.read(signfn)
|
|
508
519
|
|
|
509
520
|
if is_fls:
|
|
510
|
-
|
|
511
|
-
|
|
521
|
+
raise NotImplementedError('is_fls')
|
|
522
|
+
elif bb_chip_id == 0x1F30E1: # Mav25 - Qualcomm Snapdragon X80 5G Modem
|
|
523
|
+
data = mbn_mav25_stitch(buffer, blob)
|
|
512
524
|
else:
|
|
513
|
-
|
|
514
|
-
data = buffer[:parsed_sig_offset] + blob
|
|
525
|
+
data = mbn_stitch(buffer, blob)
|
|
515
526
|
|
|
516
527
|
bbfw_patched.writestr(bbfw_orig.getinfo(signfn), data)
|
|
517
528
|
|
|
@@ -559,6 +570,7 @@ class Restore(BaseRestore):
|
|
|
559
570
|
if tmp_zip_read_name:
|
|
560
571
|
os.remove(tmp_zip_read_name)
|
|
561
572
|
|
|
573
|
+
@asyncio_print_traceback
|
|
562
574
|
async def send_baseband_data(self, message: dict):
|
|
563
575
|
self.logger.info(f'About to send BasebandData: {message}')
|
|
564
576
|
service = await self._get_service_for_data_request(message)
|
|
@@ -608,7 +620,7 @@ class Restore(BaseRestore):
|
|
|
608
620
|
# extract baseband firmware to temp file
|
|
609
621
|
bbfw = self.ipsw.read(bbfwpath)
|
|
610
622
|
|
|
611
|
-
buffer = self.sign_bbfw(bbfw, bbtss, bb_nonce)
|
|
623
|
+
buffer = self.sign_bbfw(bbfw, bbtss, bb_nonce, bb_chip_id)
|
|
612
624
|
|
|
613
625
|
self.logger.info('Sending BasebandData now...')
|
|
614
626
|
await service.aio_send_plist({'BasebandData': buffer})
|
pymobiledevice3/services/afc.py
CHANGED
|
@@ -277,7 +277,7 @@ class AfcService(LockdownService):
|
|
|
277
277
|
except Exception as afc_exception:
|
|
278
278
|
if not ignore_errors:
|
|
279
279
|
raise
|
|
280
|
-
self.logger.warning("(Ignoring) Error:
|
|
280
|
+
self.logger.warning(f"(Ignoring) Error: {afc_exception} occurred during the copy of {src_filename}")
|
|
281
281
|
|
|
282
282
|
@path_to_str()
|
|
283
283
|
def exists(self, filename):
|
|
@@ -620,7 +620,7 @@ class AfcService(LockdownService):
|
|
|
620
620
|
pass
|
|
621
621
|
return status, data
|
|
622
622
|
|
|
623
|
-
def _do_operation(self, opcode
|
|
623
|
+
def _do_operation(self, opcode, data: bytes = b''):
|
|
624
624
|
self._dispatch_packet(opcode, data)
|
|
625
625
|
status, data = self._receive_data()
|
|
626
626
|
|
|
@@ -887,6 +887,7 @@ class AfcShell:
|
|
|
887
887
|
progress_bar : --progress_bar
|
|
888
888
|
Show progress bar
|
|
889
889
|
"""
|
|
890
|
+
|
|
890
891
|
def log(src, dst):
|
|
891
892
|
print(f'{src} --> {dst}')
|
|
892
893
|
|
|
@@ -99,7 +99,7 @@ class CrashReportsManager:
|
|
|
99
99
|
self.afc.rm_single(src, force=True)
|
|
100
100
|
|
|
101
101
|
match = None if match is None else re.compile(match)
|
|
102
|
-
self.afc.pull(entry, out, match, callback=log, progress_bar=progress_bar)
|
|
102
|
+
self.afc.pull(entry, out, match, callback=log, progress_bar=progress_bar, ignore_errors=True)
|
|
103
103
|
|
|
104
104
|
def flush(self) -> None:
|
|
105
105
|
""" Trigger com.apple.crashreportmover to flush all products into CrashReports directory """
|
|
@@ -4,7 +4,7 @@ import logging
|
|
|
4
4
|
import uuid
|
|
5
5
|
from dataclasses import dataclass, fields
|
|
6
6
|
from enum import Enum
|
|
7
|
-
from typing import Optional, Union
|
|
7
|
+
from typing import Any, Coroutine, Optional, Union
|
|
8
8
|
|
|
9
9
|
import nest_asyncio
|
|
10
10
|
|
|
@@ -157,7 +157,7 @@ class WebinspectorService:
|
|
|
157
157
|
self.service = self.await_(self.lockdown.aio_start_lockdown_service(self.service_name))
|
|
158
158
|
self.await_(self._report_identifier())
|
|
159
159
|
try:
|
|
160
|
-
self._handle_recv(self.
|
|
160
|
+
self._handle_recv(self._await_with_timeout(self._recv_message(), timeout))
|
|
161
161
|
except asyncio.TimeoutError as e:
|
|
162
162
|
raise WebInspectorNotEnabledError from e
|
|
163
163
|
self._recv_task = self.loop.create_task(self._receiving_task())
|
|
@@ -225,7 +225,7 @@ class WebinspectorService:
|
|
|
225
225
|
self.await_(self._request_application_launch(bundle))
|
|
226
226
|
self.get_open_pages()
|
|
227
227
|
try:
|
|
228
|
-
return self.
|
|
228
|
+
return self._await_with_timeout(self._wait_for_application(bundle), timeout)
|
|
229
229
|
except TimeoutError:
|
|
230
230
|
raise LaunchingApplicationError()
|
|
231
231
|
|
|
@@ -244,8 +244,22 @@ class WebinspectorService:
|
|
|
244
244
|
def flush_input(self, duration: Union[float, int] = 0):
|
|
245
245
|
return self.await_(asyncio.sleep(duration))
|
|
246
246
|
|
|
247
|
-
def await_(self, awaitable):
|
|
248
|
-
return self.loop.run_until_complete(
|
|
247
|
+
def await_(self, awaitable: Coroutine) -> Any:
|
|
248
|
+
return self.loop.run_until_complete(awaitable)
|
|
249
|
+
|
|
250
|
+
def _await_with_timeout(self, coro: Coroutine, timeout: float = None) -> Any:
|
|
251
|
+
# Create a task explicitly so we're definitely inside a Task
|
|
252
|
+
task = self.loop.create_task(coro)
|
|
253
|
+
done, pending = self.loop.run_until_complete(asyncio.wait({task}, timeout=timeout))
|
|
254
|
+
if not done:
|
|
255
|
+
task.cancel()
|
|
256
|
+
# Give the task a chance to cancel cleanly
|
|
257
|
+
try:
|
|
258
|
+
self.loop.run_until_complete(task)
|
|
259
|
+
except asyncio.CancelledError:
|
|
260
|
+
pass
|
|
261
|
+
raise WebInspectorNotEnabledError()
|
|
262
|
+
return task.result()
|
|
249
263
|
|
|
250
264
|
def _handle_recv(self, plist):
|
|
251
265
|
self.receive_handlers[plist['__selector']](plist['__argument'])
|
|
@@ -15,6 +15,7 @@ with warnings.catch_warnings():
|
|
|
15
15
|
# Ignore: "Core Pydantic V1 functionality isn't compatible with Python 3.14 or greater."
|
|
16
16
|
warnings.simplefilter('ignore', category=UserWarning)
|
|
17
17
|
import fastapi
|
|
18
|
+
|
|
18
19
|
import uvicorn
|
|
19
20
|
from construct import StreamError
|
|
20
21
|
from fastapi import FastAPI
|
|
@@ -23,7 +24,7 @@ from packaging.version import Version
|
|
|
23
24
|
from pymobiledevice3 import usbmux
|
|
24
25
|
from pymobiledevice3.bonjour import REMOTED_SERVICE_NAMES, browse
|
|
25
26
|
from pymobiledevice3.exceptions import ConnectionFailedError, ConnectionFailedToUsbmuxdError, DeviceNotFoundError, \
|
|
26
|
-
GetProhibitedError, InvalidServiceError, LockdownError, MuxException, PairingError
|
|
27
|
+
GetProhibitedError, IncorrectModeError, InvalidServiceError, LockdownError, MuxException, PairingError
|
|
27
28
|
from pymobiledevice3.lockdown import create_using_usbmux, get_mobdev2_lockdowns
|
|
28
29
|
from pymobiledevice3.osu.os_utils import get_os_utils
|
|
29
30
|
from pymobiledevice3.remote.common import TunnelProtocol
|
|
@@ -148,7 +149,7 @@ class TunneldCore:
|
|
|
148
149
|
try:
|
|
149
150
|
service = CoreDeviceTunnelProxy(create_using_usbmux(mux_device.serial))
|
|
150
151
|
except (MuxException, InvalidServiceError, GetProhibitedError, construct.core.StreamError,
|
|
151
|
-
ConnectionAbortedError, DeviceNotFoundError, LockdownError):
|
|
152
|
+
ConnectionAbortedError, DeviceNotFoundError, LockdownError, IncorrectModeError):
|
|
152
153
|
continue
|
|
153
154
|
self.tunnel_tasks[task_identifier] = TunnelTask(
|
|
154
155
|
udid=mux_device.serial,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pymobiledevice3
|
|
3
|
-
Version: 4.27.
|
|
3
|
+
Version: 4.27.6
|
|
4
4
|
Summary: Pure python3 implementation for working with iDevices (iPhone, etc...)
|
|
5
5
|
Author-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
|
|
6
6
|
Maintainer-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
|
|
@@ -8,7 +8,7 @@ misc/understanding_idevice_protocol_layers.md,sha256=8tEqRXWOUPoxOJLZVh7C7H9JGCh
|
|
|
8
8
|
misc/usbmux_sniff.sh,sha256=iWtbucOEQ9_UEFXk9x-2VNt48Jg5zrPsnUbZ_LfZxwA,212
|
|
9
9
|
pymobiledevice3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
pymobiledevice3/__main__.py,sha256=1nv18QRgR_FDq5rE95uhdZGOQ6xkPzNDrcMzQQs8ZZ4,11697
|
|
11
|
-
pymobiledevice3/_version.py,sha256=
|
|
11
|
+
pymobiledevice3/_version.py,sha256=5o71dkMA4e-Z6BEkb9K3FNM6kwzbnHi6uML3NUJ4QSg,706
|
|
12
12
|
pymobiledevice3/bonjour.py,sha256=-Q_TLBGJ6qW3CX_DgBcz-CXfWSwxWVQ2L64hk6PxnDY,5631
|
|
13
13
|
pymobiledevice3/ca.py,sha256=mTvWdSjTZw6Eb-22-IZ323GyA1G6CXYmdPedImTjm3A,10542
|
|
14
14
|
pymobiledevice3/common.py,sha256=-PG6oaUkNFlB3jb7E0finMrX8wqhkS-cuTAfmLvZUmc,329
|
|
@@ -91,17 +91,18 @@ pymobiledevice3/restore/device.py,sha256=TZahPSKHxfrF7A8DC70fwcoQK9bXD6QV1dAr80Y
|
|
|
91
91
|
pymobiledevice3/restore/fdr.py,sha256=KOXZH50oNYseP-PzSkw_uxu1NyGP5d32_DYSoYdbFu8,6269
|
|
92
92
|
pymobiledevice3/restore/ftab.py,sha256=SWNKZRN1pFWzx_qHCkycvsLaHFAqkqQJYRBJYQ_-Wjw,1507
|
|
93
93
|
pymobiledevice3/restore/img4.py,sha256=V03nq1L7FMkdmC15MN7W9Wct4MQx472aDpy5IkuNvII,4888
|
|
94
|
+
pymobiledevice3/restore/mbn.py,sha256=3Xb62vrfCROosvcTYAbAwALW_URxm5wEpctjj82CwIQ,19323
|
|
94
95
|
pymobiledevice3/restore/recovery.py,sha256=1eHQ-I1iJG31TLIAz890fBtGqopBQT3FB--UYESIkEQ,17205
|
|
95
|
-
pymobiledevice3/restore/restore.py,sha256=
|
|
96
|
+
pymobiledevice3/restore/restore.py,sha256=VKro3s0vm3WDrgVAbdcw4SNSPmgtsJ5yEvpMy_eUfPM,58104
|
|
96
97
|
pymobiledevice3/restore/restore_options.py,sha256=qeh_wRa_bi0Ccl0p0JuX9EChEQBqwJt94EBImWcEJ3E,7255
|
|
97
98
|
pymobiledevice3/restore/restored_client.py,sha256=h_yBZ_e1wfaIzi0f9-R8Ky2x6xFTkvDlpWIV561uxoU,3638
|
|
98
99
|
pymobiledevice3/restore/tss.py,sha256=QHq5OTyCn7iIrYhhgOxT5_gpV7wggcy5Faje5160FHQ,29852
|
|
99
100
|
pymobiledevice3/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
100
101
|
pymobiledevice3/services/accessibilityaudit.py,sha256=PtQujwFLL3E1OgOSUjkKHvgmk2e8Ij2XZIp8JicAphs,15547
|
|
101
|
-
pymobiledevice3/services/afc.py,sha256=
|
|
102
|
+
pymobiledevice3/services/afc.py,sha256=bENRiozw5qAqMatXeqQZ1JmT8OZBH2R1UmN3hJSrKo4,36527
|
|
102
103
|
pymobiledevice3/services/amfi.py,sha256=CmH4tbWpwLhfZlX0ukOD7sX8sYhr0exlczQ8tY2NnPw,2511
|
|
103
104
|
pymobiledevice3/services/companion.py,sha256=uKsIqDHIdMkx6QjRd55NE6w8ksXhT_aN2z2cQAcIif0,2694
|
|
104
|
-
pymobiledevice3/services/crash_reports.py,sha256=
|
|
105
|
+
pymobiledevice3/services/crash_reports.py,sha256=vHIDCjlJTz26X6e2rpxb2K9sASNHlvms_mM2eXglxjU,10808
|
|
105
106
|
pymobiledevice3/services/debugserver_applist.py,sha256=YE6mWfEb4IiKmdATOCESto1nI-_uovrbqrkByGektLI,548
|
|
106
107
|
pymobiledevice3/services/device_arbitration.py,sha256=FNcWydgueBqNUVZXwYfRF-EKS3vvQQHQwVzKzc-O8u4,1134
|
|
107
108
|
pymobiledevice3/services/device_link.py,sha256=KCpHjOBvvzIz87JClMpRmK4fAARftaHHK1cT-YSDcXY,8435
|
|
@@ -129,7 +130,7 @@ pymobiledevice3/services/screenshot.py,sha256=sulQnyVGmq_3MJZGQ_GPJYOCj_tmE_d9ZG
|
|
|
129
130
|
pymobiledevice3/services/simulate_location.py,sha256=WsnxaHIeRa0-9LkHbfnfOG0I9GK7D7xgksl91hOfCkI,1166
|
|
130
131
|
pymobiledevice3/services/springboard.py,sha256=-3tqSuRx4p2j-rWRkDl_icvxftyuEkESWANKAS1L97M,2510
|
|
131
132
|
pymobiledevice3/services/syslog.py,sha256=dccKS8sWgPCGOzK2-3f08gyF9UrN5-iHeqHxtpt6P-0,1563
|
|
132
|
-
pymobiledevice3/services/webinspector.py,sha256=
|
|
133
|
+
pymobiledevice3/services/webinspector.py,sha256=p-wazgczgVh07dTuPNtYv9RHB6HgwxLrQMFX_dms96k,16488
|
|
133
134
|
pymobiledevice3/services/dvt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
134
135
|
pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py,sha256=2lhxkZVxV3U3tDKrx2AxX5Ui2QXBoz8_WEY6xbrkkTc,1072
|
|
135
136
|
pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py,sha256=dsaResRZem1ezLkQNfMXvFJoaE0lsbznhYs7q8BLVAw,1179
|
|
@@ -163,10 +164,10 @@ pymobiledevice3/services/web_protocol/session_protocol.py,sha256=7dJkFyivu554K6I
|
|
|
163
164
|
pymobiledevice3/services/web_protocol/switch_to.py,sha256=hDddJUEePbRN-8xlllOeGhnYvE4NEnd8JJIlosLMB9c,2880
|
|
164
165
|
pymobiledevice3/tunneld/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
165
166
|
pymobiledevice3/tunneld/api.py,sha256=EfGKXEWhsMSB__menPmRmL9R6dpazVJDUy7B3pn05MM,2357
|
|
166
|
-
pymobiledevice3/tunneld/server.py,sha256=
|
|
167
|
-
pymobiledevice3-4.27.
|
|
168
|
-
pymobiledevice3-4.27.
|
|
169
|
-
pymobiledevice3-4.27.
|
|
170
|
-
pymobiledevice3-4.27.
|
|
171
|
-
pymobiledevice3-4.27.
|
|
172
|
-
pymobiledevice3-4.27.
|
|
167
|
+
pymobiledevice3/tunneld/server.py,sha256=L_98QatvVuyiXexoHF5rA0V56wC84VLeKhLyiFWwHrc,22960
|
|
168
|
+
pymobiledevice3-4.27.6.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
|
169
|
+
pymobiledevice3-4.27.6.dist-info/METADATA,sha256=9zYKxPeaGYsfbQe6-sf4iQn9lQjw12zx_smTNsWVIVU,17450
|
|
170
|
+
pymobiledevice3-4.27.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
171
|
+
pymobiledevice3-4.27.6.dist-info/entry_points.txt,sha256=jJMlOanHlVwUxcY__JwvKeWPrvBJr_wJyEq4oHIZNKE,66
|
|
172
|
+
pymobiledevice3-4.27.6.dist-info/top_level.txt,sha256=MjZoRqcWPOh5banG-BbDOnKEfsS3kCxqV9cv-nzyg2Q,21
|
|
173
|
+
pymobiledevice3-4.27.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|