pymobiledevice3 4.27.0__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 +363 -293
- 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 +64 -50
- pymobiledevice3/tunneld/api.py +20 -15
- pymobiledevice3/tunneld/server.py +315 -193
- pymobiledevice3/usbmux.py +197 -148
- pymobiledevice3/utils.py +14 -11
- {pymobiledevice3-4.27.0.dist-info → pymobiledevice3-5.1.2.dist-info}/METADATA +2 -6
- pymobiledevice3-5.1.2.dist-info/RECORD +173 -0
- pymobiledevice3-4.27.0.dist-info/RECORD +0 -172
- {pymobiledevice3-4.27.0.dist-info → pymobiledevice3-5.1.2.dist-info}/WHEEL +0 -0
- {pymobiledevice3-4.27.0.dist-info → pymobiledevice3-5.1.2.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-4.27.0.dist-info → pymobiledevice3-5.1.2.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-4.27.0.dist-info → pymobiledevice3-5.1.2.dist-info}/top_level.txt +0 -0
|
@@ -6,7 +6,6 @@ import plistlib
|
|
|
6
6
|
import struct
|
|
7
7
|
import tempfile
|
|
8
8
|
import time
|
|
9
|
-
import traceback
|
|
10
9
|
import typing
|
|
11
10
|
import zipfile
|
|
12
11
|
from concurrent.futures import ThreadPoolExecutor
|
|
@@ -17,34 +16,40 @@ from tqdm import tqdm, trange
|
|
|
17
16
|
|
|
18
17
|
from pymobiledevice3.exceptions import ConnectionFailedError, NoDeviceConnectedError, PyMobileDevice3Exception
|
|
19
18
|
from pymobiledevice3.restore.asr import DEFAULT_ASR_SYNC_PORT, ASRClient
|
|
20
|
-
from pymobiledevice3.restore.base_restore import
|
|
21
|
-
|
|
19
|
+
from pymobiledevice3.restore.base_restore import (
|
|
20
|
+
RESTORE_VARIANT_ERASE_INSTALL,
|
|
21
|
+
RESTORE_VARIANT_MACOS_RECOVERY_OS,
|
|
22
|
+
RESTORE_VARIANT_UPGRADE_INSTALL,
|
|
23
|
+
BaseRestore,
|
|
24
|
+
)
|
|
22
25
|
from pymobiledevice3.restore.consts import PROGRESS_BAR_OPERATIONS, lpol_file
|
|
23
26
|
from pymobiledevice3.restore.device import Device
|
|
24
27
|
from pymobiledevice3.restore.fdr import FDRClient, fdr_type, start_fdr_thread
|
|
25
28
|
from pymobiledevice3.restore.ftab import Ftab
|
|
29
|
+
from pymobiledevice3.restore.mbn import mbn_mav25_stitch, mbn_stitch
|
|
26
30
|
from pymobiledevice3.restore.recovery import Behavior, Recovery
|
|
27
31
|
from pymobiledevice3.restore.restore_options import RestoreOptions
|
|
28
32
|
from pymobiledevice3.restore.restored_client import RestoredClient
|
|
29
33
|
from pymobiledevice3.restore.tss import TSSRequest, TSSResponse
|
|
30
34
|
from pymobiledevice3.service_connection import ServiceConnection
|
|
31
|
-
from pymobiledevice3.utils import plist_access_path
|
|
35
|
+
from pymobiledevice3.utils import asyncio_print_traceback, plist_access_path
|
|
32
36
|
|
|
33
37
|
known_errors = {
|
|
34
|
-
0xFFFFFFFFFFFFFFFF:
|
|
35
|
-
6:
|
|
36
|
-
14:
|
|
37
|
-
27:
|
|
38
|
-
50:
|
|
39
|
-
51:
|
|
40
|
-
53:
|
|
41
|
-
1015:
|
|
38
|
+
0xFFFFFFFFFFFFFFFF: "verification error",
|
|
39
|
+
6: "disk failure",
|
|
40
|
+
14: "fail",
|
|
41
|
+
27: "failed to mount filesystems",
|
|
42
|
+
50: "failed to load SEP firmware",
|
|
43
|
+
51: "failed to load SEP firmware",
|
|
44
|
+
53: "failed to recover FDR data",
|
|
45
|
+
1015: "X-Gold Baseband Update Failed. Defective Unit?",
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
|
|
45
49
|
class Restore(BaseRestore):
|
|
46
|
-
def __init__(
|
|
47
|
-
|
|
50
|
+
def __init__(
|
|
51
|
+
self, ipsw: zipfile.ZipFile, device: Device, tss=None, behavior: Behavior = Behavior.Update, ignore_fdr=False
|
|
52
|
+
):
|
|
48
53
|
super().__init__(ipsw, device, tss, behavior)
|
|
49
54
|
self.recovery = Recovery(ipsw, device, tss=tss, behavior=behavior)
|
|
50
55
|
self.bbtss: Optional[TSSResponse] = None
|
|
@@ -73,85 +78,72 @@ class Restore(BaseRestore):
|
|
|
73
78
|
# data request messages are sent by restored whenever it requires
|
|
74
79
|
# files sent to the server by the client. these data requests include
|
|
75
80
|
# SystemImageData, RootTicket, KernelCache, NORData and BasebandData requests
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
"DataRequestMsg": self.handle_data_request_msg,
|
|
82
|
+
"AsyncDataRequestMsg": self.handle_async_data_request_msg,
|
|
79
83
|
# restore logs are available if a previous restore failed
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
"PreviousRestoreLogMsg": self.handle_previous_restore_log_msg,
|
|
82
85
|
# progress notification messages sent by the restored inform the client
|
|
83
86
|
# of it's current operation and sometimes percent of progress is complete
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
"ProgressMsg": self.handle_progress_msg,
|
|
86
88
|
# status messages usually indicate the current state of the restored
|
|
87
89
|
# process or often to signal an error has been encountered
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
"StatusMsg": self.handle_status_msg,
|
|
90
91
|
# checkpoint notifications
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
"CheckpointMsg": self.handle_checkpoint_msg,
|
|
93
93
|
# baseband update message
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
"BBUpdateStatusMsg": self.handle_bb_update_status_msg,
|
|
96
95
|
# baseband updater output data request
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
"BasebandUpdaterOutputData": self.handle_baseband_updater_output_data,
|
|
99
97
|
# report backtrace from restored crash
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
"RestoredCrash": self.handle_restored_crash,
|
|
102
99
|
# report new async contexts
|
|
103
|
-
|
|
104
|
-
|
|
100
|
+
"AsyncWait": self.handle_async_wait,
|
|
105
101
|
# handle attestation
|
|
106
|
-
|
|
102
|
+
"RestoreAttestation": self.handle_restore_attestation,
|
|
107
103
|
}
|
|
108
104
|
|
|
109
105
|
self._data_request_handlers = {
|
|
110
106
|
# this request is sent when restored is ready to receive the filesystem
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
'RecoveryOSLocalPolicy': self.send_restore_local_policy,
|
|
117
|
-
|
|
107
|
+
"SystemImageData": self.send_filesystem,
|
|
108
|
+
"BuildIdentityDict": self.send_buildidentity,
|
|
109
|
+
"PersonalizedBootObjectV3": self.send_personalized_boot_object_v3,
|
|
110
|
+
"SourceBootObjectV4": self.send_source_boot_object_v4,
|
|
111
|
+
"RecoveryOSLocalPolicy": self.send_restore_local_policy,
|
|
118
112
|
# this request is sent when restored is ready to receive the filesystem
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
"RecoveryOSASRImage": self.send_filesystem,
|
|
121
114
|
# Send RecoveryOS RTD
|
|
122
|
-
|
|
123
|
-
|
|
115
|
+
"RecoveryOSRootTicketData": self.send_recovery_os_root_ticket,
|
|
124
116
|
# send RootTicket (== APTicket from the TSS request)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
'FirmwareUpdaterData': self.send_firmware_updater_data,
|
|
131
|
-
|
|
117
|
+
"RootTicket": self.send_root_ticket,
|
|
118
|
+
"NORData": self.send_nor,
|
|
119
|
+
"BasebandData": self.send_baseband_data,
|
|
120
|
+
"FDRTrustData": self.send_fdr_trust_data,
|
|
121
|
+
"FirmwareUpdaterData": self.send_firmware_updater_data,
|
|
132
122
|
# TODO: verify
|
|
133
|
-
|
|
134
|
-
|
|
123
|
+
"FirmwareUpdaterPreflight": self.send_firmware_updater_preflight,
|
|
135
124
|
# Added on iOS 18.0 beta1
|
|
136
|
-
|
|
137
|
-
|
|
125
|
+
"URLAsset": self.send_url_asset,
|
|
126
|
+
"StreamedImageDecryptionKey": self.send_streamed_image_decryption_key,
|
|
138
127
|
}
|
|
139
128
|
|
|
140
129
|
self._data_request_components = {
|
|
141
|
-
|
|
142
|
-
|
|
130
|
+
"KernelCache": self.send_component,
|
|
131
|
+
"DeviceTree": self.send_component,
|
|
143
132
|
}
|
|
144
133
|
|
|
145
134
|
def handle_async_data_request_msg(self, message: dict) -> typing.Coroutine:
|
|
146
|
-
self._tasks.append(
|
|
147
|
-
|
|
135
|
+
self._tasks.append(
|
|
136
|
+
asyncio.create_task(
|
|
137
|
+
self.handle_data_request_msg(message), name=f"AsyncDataRequestMsg-{message['DataType']}"
|
|
138
|
+
)
|
|
139
|
+
)
|
|
148
140
|
return asyncio.sleep(0)
|
|
149
141
|
|
|
150
142
|
async def send_filesystem(self, message: dict) -> None:
|
|
151
|
-
self.logger.info(
|
|
143
|
+
self.logger.info("about to send filesystem...")
|
|
152
144
|
|
|
153
|
-
asr_port = message.get(
|
|
154
|
-
self.logger.info(f
|
|
145
|
+
asr_port = message.get("DataPort", DEFAULT_ASR_SYNC_PORT)
|
|
146
|
+
self.logger.info(f"connecting to ASR on port {asr_port}")
|
|
155
147
|
asr = ASRClient(self._restored.udid)
|
|
156
148
|
while True:
|
|
157
149
|
try:
|
|
@@ -160,46 +152,46 @@ class Restore(BaseRestore):
|
|
|
160
152
|
except ConnectionFailedError:
|
|
161
153
|
pass
|
|
162
154
|
|
|
163
|
-
self.logger.info(
|
|
155
|
+
self.logger.info("connected to ASR")
|
|
164
156
|
|
|
165
157
|
# this step sends requested chunks of data from various offsets to asr, so
|
|
166
158
|
# it can validate the filesystem before installing it
|
|
167
|
-
self.logger.info(
|
|
159
|
+
self.logger.info("validating the filesystem")
|
|
168
160
|
|
|
169
|
-
with self.ipsw.open_path(self.build_identity.get_component_path(
|
|
161
|
+
with self.ipsw.open_path(self.build_identity.get_component_path("OS")) as filesystem:
|
|
170
162
|
await asr.perform_validation(filesystem)
|
|
171
|
-
self.logger.info(
|
|
163
|
+
self.logger.info("filesystem validated")
|
|
172
164
|
|
|
173
165
|
# once the target filesystem has been validated, ASR then requests the
|
|
174
166
|
# entire filesystem to be sent.
|
|
175
|
-
self.logger.info(
|
|
167
|
+
self.logger.info("sending filesystem now...")
|
|
176
168
|
await asr.send_payload(filesystem)
|
|
177
169
|
|
|
178
170
|
await asr.close()
|
|
179
171
|
|
|
180
172
|
def get_build_identity_from_request(self, msg):
|
|
181
|
-
return self.get_build_identity(msg[
|
|
173
|
+
return self.get_build_identity(msg["Arguments"].get("IsRecoveryOS", False))
|
|
182
174
|
|
|
183
175
|
async def send_buildidentity(self, message: dict) -> None:
|
|
184
|
-
self.logger.info(
|
|
176
|
+
self.logger.info("About to send BuildIdentity Dict...")
|
|
185
177
|
service = await self._get_service_for_data_request(message)
|
|
186
|
-
req = {
|
|
187
|
-
arguments = message[
|
|
188
|
-
variant = arguments.get(
|
|
189
|
-
req[
|
|
190
|
-
self.logger.info(
|
|
178
|
+
req = {"BuildIdentityDict": dict(self.get_build_identity_from_request(message))}
|
|
179
|
+
arguments = message["Arguments"]
|
|
180
|
+
variant = arguments.get("Variant", "Erase")
|
|
181
|
+
req["Variant"] = variant
|
|
182
|
+
self.logger.info("Sending BuildIdentityDict now...")
|
|
191
183
|
await service.aio_send_plist(req)
|
|
192
184
|
|
|
193
185
|
async def extract_global_manifest(self) -> dict:
|
|
194
|
-
build_info = self.build_identity.get(
|
|
186
|
+
build_info = self.build_identity.get("Info")
|
|
195
187
|
if build_info is None:
|
|
196
188
|
raise PyMobileDevice3Exception('build identity does not contain an "Info" element')
|
|
197
189
|
|
|
198
|
-
device_class = build_info.get(
|
|
190
|
+
device_class = build_info.get("DeviceClass")
|
|
199
191
|
if device_class is None:
|
|
200
192
|
raise PyMobileDevice3Exception('build identity does not contain an "DeviceClass" element')
|
|
201
193
|
|
|
202
|
-
macos_variant = build_info.get(
|
|
194
|
+
macos_variant = build_info.get("MacOSVariant")
|
|
203
195
|
if macos_variant is None:
|
|
204
196
|
raise PyMobileDevice3Exception('build identity does not contain an "MacOSVariant" element')
|
|
205
197
|
|
|
@@ -207,67 +199,68 @@ class Restore(BaseRestore):
|
|
|
207
199
|
return self.ipsw.get_global_manifest(macos_variant, device_class)
|
|
208
200
|
|
|
209
201
|
async def send_personalized_boot_object_v3(self, message: dict) -> None:
|
|
210
|
-
self.logger.debug(
|
|
202
|
+
self.logger.debug("send_personalized_boot_object_v3")
|
|
211
203
|
service = await self._get_service_for_data_request(message)
|
|
212
|
-
image_name = message[
|
|
204
|
+
image_name = message["Arguments"]["ImageName"]
|
|
213
205
|
component_name = image_name
|
|
214
|
-
self.logger.info(f
|
|
206
|
+
self.logger.info(f"About to send {component_name}...")
|
|
215
207
|
|
|
216
|
-
if image_name ==
|
|
208
|
+
if image_name == "__GlobalManifest__":
|
|
217
209
|
data = self.extract_global_manifest()
|
|
218
|
-
elif image_name ==
|
|
210
|
+
elif image_name == "__RestoreVersion__":
|
|
219
211
|
data = self.ipsw.restore_version
|
|
220
|
-
elif image_name ==
|
|
212
|
+
elif image_name == "__SystemVersion__":
|
|
221
213
|
data = self.ipsw.system_version
|
|
222
214
|
else:
|
|
223
215
|
data = self.get_personalized_data(component_name, tss=self.recovery.tss)
|
|
224
216
|
|
|
225
|
-
self.logger.info(f
|
|
217
|
+
self.logger.info(f"Sending {component_name} now...")
|
|
226
218
|
chunk_size = 8192
|
|
227
219
|
for i in trange(0, len(data), chunk_size, dynamic_ncols=True):
|
|
228
|
-
await service.aio_send_plist({
|
|
220
|
+
await service.aio_send_plist({"FileData": data[i : i + chunk_size]})
|
|
229
221
|
|
|
230
222
|
# Send FileDataDone
|
|
231
|
-
await service.aio_send_plist({
|
|
223
|
+
await service.aio_send_plist({"FileDataDone": True})
|
|
232
224
|
|
|
233
|
-
self.logger.info(f
|
|
225
|
+
self.logger.info(f"Done sending {component_name}")
|
|
234
226
|
|
|
235
227
|
async def send_source_boot_object_v4(self, message: dict) -> None:
|
|
236
|
-
self.logger.debug(
|
|
228
|
+
self.logger.debug("send_source_boot_object_v4")
|
|
237
229
|
service = await self._get_service_for_data_request(message)
|
|
238
|
-
image_name = message[
|
|
230
|
+
image_name = message["Arguments"]["ImageName"]
|
|
239
231
|
component_name = image_name
|
|
240
|
-
self.logger.info(f
|
|
232
|
+
self.logger.info(f"About to send {component_name}...")
|
|
241
233
|
|
|
242
|
-
if image_name ==
|
|
234
|
+
if image_name == "__GlobalManifest__":
|
|
243
235
|
data = self.extract_global_manifest()
|
|
244
|
-
elif image_name ==
|
|
236
|
+
elif image_name == "__RestoreVersion__":
|
|
245
237
|
data = self.ipsw.restore_version
|
|
246
|
-
elif image_name ==
|
|
238
|
+
elif image_name == "__SystemVersion__":
|
|
247
239
|
data = self.ipsw.system_version
|
|
248
240
|
else:
|
|
249
|
-
data = (
|
|
250
|
-
|
|
241
|
+
data = (
|
|
242
|
+
self.get_build_identity_from_request(message).get_component(component_name, tss=self.recovery.tss).data
|
|
243
|
+
)
|
|
251
244
|
|
|
252
|
-
self.logger.info(f
|
|
245
|
+
self.logger.info(f"Sending {component_name} now...")
|
|
253
246
|
chunk_size = 8192
|
|
254
247
|
for i in trange(0, len(data), chunk_size, dynamic_ncols=True):
|
|
255
|
-
chunk = data[i:i + chunk_size]
|
|
256
|
-
await service.aio_send_plist({
|
|
257
|
-
if i == 0 and chunk.startswith(b
|
|
258
|
-
self.logger.debug(
|
|
248
|
+
chunk = data[i : i + chunk_size]
|
|
249
|
+
await service.aio_send_plist({"FileData": chunk})
|
|
250
|
+
if i == 0 and chunk.startswith(b"AEA1"):
|
|
251
|
+
self.logger.debug("First chunk in a AEA")
|
|
259
252
|
try:
|
|
260
253
|
message = await asyncio.wait_for(service.aio_recv_plist(), timeout=3)
|
|
261
|
-
if message[
|
|
254
|
+
if message["MsgType"] != "URLAsset":
|
|
262
255
|
raise asyncio.exceptions.TimeoutError()
|
|
263
256
|
await self.send_url_asset(message)
|
|
264
257
|
except asyncio.exceptions.TimeoutError:
|
|
265
|
-
self.logger.debug(
|
|
258
|
+
self.logger.debug("No URLAsset was requested. Assuming it is not necessary")
|
|
266
259
|
|
|
267
260
|
# Send FileDataDone
|
|
268
|
-
await service.aio_send_plist({
|
|
261
|
+
await service.aio_send_plist({"FileDataDone": True})
|
|
269
262
|
|
|
270
|
-
self.logger.info(f
|
|
263
|
+
self.logger.info(f"Done sending {component_name}")
|
|
271
264
|
|
|
272
265
|
async def get_recovery_os_local_policy_tss_response(self, args, build_identity=None):
|
|
273
266
|
if build_identity is None:
|
|
@@ -275,29 +268,29 @@ class Restore(BaseRestore):
|
|
|
275
268
|
|
|
276
269
|
# populate parameters
|
|
277
270
|
parameters = {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
271
|
+
"ApECID": self.device.ecid,
|
|
272
|
+
"Ap,LocalBoot": True,
|
|
273
|
+
"ApProductionMode": True,
|
|
274
|
+
"ApSecurityMode": True,
|
|
275
|
+
"ApSupportsImg4": True,
|
|
283
276
|
}
|
|
284
277
|
|
|
285
278
|
build_identity.populate_tss_request_parameters(parameters)
|
|
286
279
|
|
|
287
280
|
# Add Ap,LocalPolicy
|
|
288
281
|
lpol = {
|
|
289
|
-
|
|
290
|
-
|
|
282
|
+
"Digest": hashlib.sha384(lpol_file).digest(),
|
|
283
|
+
"Trusted": True,
|
|
291
284
|
}
|
|
292
285
|
|
|
293
|
-
parameters[
|
|
286
|
+
parameters["Ap,LocalPolicy"] = lpol
|
|
294
287
|
|
|
295
|
-
parameters[
|
|
296
|
-
parameters[
|
|
288
|
+
parameters["Ap,NextStageIM4MHash"] = args["Ap,NextStageIM4MHash"]
|
|
289
|
+
parameters["Ap,RecoveryOSPolicyNonceHash"] = args["Ap,RecoveryOSPolicyNonceHash"]
|
|
297
290
|
|
|
298
|
-
vol_uuid = args[
|
|
299
|
-
vol_uuid = binascii.unhexlify(vol_uuid.replace(
|
|
300
|
-
parameters[
|
|
291
|
+
vol_uuid = args["Ap,VolumeUUID"]
|
|
292
|
+
vol_uuid = binascii.unhexlify(vol_uuid.replace("-", ""))
|
|
293
|
+
parameters["Ap,VolumeUUID"] = vol_uuid
|
|
301
294
|
|
|
302
295
|
# create basic request
|
|
303
296
|
request = TSSRequest()
|
|
@@ -305,7 +298,7 @@ class Restore(BaseRestore):
|
|
|
305
298
|
# add common tags from manifest
|
|
306
299
|
request.add_local_policy_tags(parameters)
|
|
307
300
|
|
|
308
|
-
self.logger.info(
|
|
301
|
+
self.logger.info("Requesting SHSH blobs...")
|
|
309
302
|
return await request.send_receive()
|
|
310
303
|
|
|
311
304
|
def get_build_identity(self, is_recovery_os: bool):
|
|
@@ -319,24 +312,25 @@ class Restore(BaseRestore):
|
|
|
319
312
|
return self.ipsw.build_manifest.get_build_identity(self.device.hardware_model, variant=variant)
|
|
320
313
|
|
|
321
314
|
async def send_restore_local_policy(self, message: dict) -> None:
|
|
322
|
-
component =
|
|
315
|
+
component = "Ap,LocalPolicy"
|
|
323
316
|
service = await self._get_service_for_data_request(message)
|
|
324
317
|
|
|
325
318
|
# The Update mode does not have a specific build identity for the recovery os.
|
|
326
319
|
build_identity = self.get_build_identity(self.build_identity.restore_behavior == Behavior.Erase.value)
|
|
327
|
-
tss_localpolicy = await self.get_recovery_os_local_policy_tss_response(
|
|
328
|
-
|
|
320
|
+
tss_localpolicy = await self.get_recovery_os_local_policy_tss_response(
|
|
321
|
+
message["Arguments"], build_identity=build_identity
|
|
322
|
+
)
|
|
329
323
|
|
|
330
324
|
await service.aio_send_plist({
|
|
331
|
-
|
|
332
|
-
|
|
325
|
+
"Ap,LocalPolicy": self.get_personalized_data(component, data=lpol_file, tss=tss_localpolicy)
|
|
326
|
+
})
|
|
333
327
|
|
|
334
328
|
async def send_recovery_os_root_ticket(self, message: dict) -> None:
|
|
335
|
-
self.logger.info(
|
|
329
|
+
self.logger.info("About to send RecoveryOSRootTicket...")
|
|
336
330
|
service = await self._get_service_for_data_request(message)
|
|
337
331
|
|
|
338
332
|
if self.recovery.tss_recoveryos_root_ticket is None:
|
|
339
|
-
raise PyMobileDevice3Exception(
|
|
333
|
+
raise PyMobileDevice3Exception("Cannot send RootTicket without TSS")
|
|
340
334
|
|
|
341
335
|
if self.device.is_image4_supported:
|
|
342
336
|
data = self.recovery.tss_recoveryos_root_ticket.ap_img4_ticket
|
|
@@ -345,73 +339,70 @@ class Restore(BaseRestore):
|
|
|
345
339
|
|
|
346
340
|
req = {}
|
|
347
341
|
if data:
|
|
348
|
-
req[
|
|
342
|
+
req["RootTicketData"] = data
|
|
349
343
|
else:
|
|
350
|
-
self.logger.warning(
|
|
344
|
+
self.logger.warning("not sending RootTicketData (no data present)")
|
|
351
345
|
|
|
352
|
-
self.logger.info(
|
|
346
|
+
self.logger.info("Sending RecoveryOSRootTicket now...")
|
|
353
347
|
await service.aio_send_plist(req)
|
|
354
348
|
|
|
355
349
|
async def send_root_ticket(self, message: dict) -> None:
|
|
356
|
-
self.logger.info(
|
|
350
|
+
self.logger.info("About to send RootTicket...")
|
|
357
351
|
service = await self._get_service_for_data_request(message)
|
|
358
352
|
|
|
359
353
|
if self.recovery.tss is None:
|
|
360
|
-
raise PyMobileDevice3Exception(
|
|
354
|
+
raise PyMobileDevice3Exception("Cannot send RootTicket without TSS")
|
|
361
355
|
|
|
362
|
-
self.logger.info(
|
|
363
|
-
await service.aio_send_plist({
|
|
356
|
+
self.logger.info("Sending RootTicket now...")
|
|
357
|
+
await service.aio_send_plist({"RootTicketData": self.recovery.tss.ap_img4_ticket})
|
|
364
358
|
|
|
365
359
|
async def send_nor(self, message: dict):
|
|
366
|
-
self.logger.info(
|
|
360
|
+
self.logger.info("About to send NORData...")
|
|
367
361
|
service = await self._get_service_for_data_request(message)
|
|
368
362
|
|
|
369
363
|
flash_version_1 = False
|
|
370
|
-
llb_path = self.build_identity.get_component(
|
|
371
|
-
llb_filename_offset = llb_path.find(
|
|
364
|
+
llb_path = self.build_identity.get_component("LLB", tss=self.recovery.tss).path
|
|
365
|
+
llb_filename_offset = llb_path.find("LLB")
|
|
372
366
|
|
|
373
|
-
arguments = message.get(
|
|
367
|
+
arguments = message.get("Arguments")
|
|
374
368
|
if arguments:
|
|
375
|
-
flash_version_1 = arguments.get(
|
|
369
|
+
flash_version_1 = arguments.get("FlashVersion1", False)
|
|
376
370
|
|
|
377
371
|
if llb_filename_offset == -1:
|
|
378
|
-
raise PyMobileDevice3Exception(
|
|
372
|
+
raise PyMobileDevice3Exception("Unable to extract firmware path from LLB filename")
|
|
379
373
|
|
|
380
|
-
firmware_path = llb_path[:llb_filename_offset - 1]
|
|
381
|
-
self.logger.info(f
|
|
374
|
+
firmware_path = llb_path[: llb_filename_offset - 1]
|
|
375
|
+
self.logger.info(f"Found firmware path: {firmware_path}")
|
|
382
376
|
|
|
383
|
-
firmware_files =
|
|
377
|
+
firmware_files = {}
|
|
384
378
|
try:
|
|
385
379
|
firmware = self.ipsw.get_firmware(firmware_path)
|
|
386
380
|
firmware_files = firmware.get_files()
|
|
387
381
|
except KeyError:
|
|
388
|
-
self.logger.info(
|
|
389
|
-
build_id_manifest = self.build_identity[
|
|
382
|
+
self.logger.info("Getting firmware manifest from build identity")
|
|
383
|
+
build_id_manifest = self.build_identity["Manifest"]
|
|
390
384
|
for component, manifest_entry in build_id_manifest.items():
|
|
391
385
|
if isinstance(manifest_entry, dict):
|
|
392
|
-
is_fw = plist_access_path(manifest_entry, (
|
|
393
|
-
loaded_by_iboot = plist_access_path(manifest_entry, (
|
|
394
|
-
is_secondary_fw = plist_access_path(manifest_entry, (
|
|
386
|
+
is_fw = plist_access_path(manifest_entry, ("Info", "IsFirmwarePayload"), bool)
|
|
387
|
+
loaded_by_iboot = plist_access_path(manifest_entry, ("Info", "IsLoadedByiBoot"), bool)
|
|
388
|
+
is_secondary_fw = plist_access_path(manifest_entry, ("Info", "IsSecondaryFirmwarePayload"), bool)
|
|
395
389
|
|
|
396
390
|
if is_fw or (is_secondary_fw and loaded_by_iboot):
|
|
397
|
-
comp_path = plist_access_path(manifest_entry, (
|
|
391
|
+
comp_path = plist_access_path(manifest_entry, ("Info", "Path"))
|
|
398
392
|
if comp_path:
|
|
399
393
|
firmware_files[component] = comp_path
|
|
400
394
|
|
|
401
395
|
if not firmware_files:
|
|
402
|
-
raise PyMobileDevice3Exception(
|
|
396
|
+
raise PyMobileDevice3Exception("Unable to get list of firmware files.")
|
|
403
397
|
|
|
404
|
-
component =
|
|
398
|
+
component = "LLB"
|
|
405
399
|
llb_data = self.get_personalized_data(component, tss=self.recovery.tss, path=llb_path)
|
|
406
|
-
req = {
|
|
400
|
+
req = {"LlbImageData": llb_data}
|
|
407
401
|
|
|
408
|
-
if flash_version_1
|
|
409
|
-
norimage = {}
|
|
410
|
-
else:
|
|
411
|
-
norimage = []
|
|
402
|
+
norimage = {} if flash_version_1 else []
|
|
412
403
|
|
|
413
404
|
for component, comppath in firmware_files.items():
|
|
414
|
-
if component in (
|
|
405
|
+
if component in ("LLB", "RestoreSEP"):
|
|
415
406
|
# skip LLB, it's already passed in LlbImageData
|
|
416
407
|
# skip RestoreSEP, it's passed in RestoreSEPImageData
|
|
417
408
|
continue
|
|
@@ -422,50 +413,59 @@ class Restore(BaseRestore):
|
|
|
422
413
|
norimage[component] = nor_data
|
|
423
414
|
else:
|
|
424
415
|
# make sure iBoot is the first entry in the array
|
|
425
|
-
if component.startswith(
|
|
426
|
-
norimage = [nor_data
|
|
416
|
+
if component.startswith("iBoot"):
|
|
417
|
+
norimage = [nor_data, *norimage]
|
|
427
418
|
else:
|
|
428
419
|
norimage.append(nor_data)
|
|
429
420
|
|
|
430
|
-
req[
|
|
421
|
+
req["NorImageData"] = norimage
|
|
431
422
|
|
|
432
|
-
for component in (
|
|
423
|
+
for component in ("RestoreSEP", "SEP", "SepStage1"):
|
|
433
424
|
if not self.build_identity.has_component(component):
|
|
434
425
|
continue
|
|
435
426
|
comp = self.build_identity.get_component(component, tss=self.recovery.tss)
|
|
436
427
|
if comp.path:
|
|
437
|
-
if component ==
|
|
438
|
-
component =
|
|
439
|
-
req[f
|
|
428
|
+
if component == "SepStage1":
|
|
429
|
+
component = "SEPPatch"
|
|
430
|
+
req[f"{component}ImageData"] = self.get_personalized_data(comp.name, comp.data, self.recovery.tss)
|
|
440
431
|
|
|
441
|
-
self.logger.info(
|
|
432
|
+
self.logger.info("Sending NORData now...")
|
|
442
433
|
await service.aio_send_plist(req)
|
|
443
434
|
|
|
444
435
|
@staticmethod
|
|
445
|
-
def get_bbfw_fn_for_element(elem):
|
|
436
|
+
def get_bbfw_fn_for_element(elem: str, bb_chip_id: Optional[int] = None) -> str:
|
|
446
437
|
bbfw_fn_elem = {
|
|
447
438
|
# ICE3 firmware files
|
|
448
|
-
|
|
449
|
-
|
|
439
|
+
"RamPSI": "psi_ram.fls",
|
|
440
|
+
"FlashPSI": "psi_flash.fls",
|
|
450
441
|
# Trek firmware files
|
|
451
|
-
|
|
452
|
-
|
|
442
|
+
"eDBL": "dbl.mbn",
|
|
443
|
+
"RestoreDBL": "restoredbl.mbn",
|
|
453
444
|
# Phoenix/Mav4 firmware files
|
|
454
|
-
|
|
455
|
-
|
|
445
|
+
"DBL": "dbl.mbn",
|
|
446
|
+
"ENANDPRG": "ENPRG.mbn",
|
|
456
447
|
# Mav5 firmware files
|
|
457
|
-
|
|
458
|
-
|
|
448
|
+
"RestoreSBL1": "restoresbl1.mbn",
|
|
449
|
+
"SBL1": "sbl1.mbn",
|
|
459
450
|
# ICE16 firmware files
|
|
460
|
-
|
|
461
|
-
|
|
451
|
+
"RestorePSI": "restorepsi.bin",
|
|
452
|
+
"PSI": "psi_ram.bin",
|
|
462
453
|
# ICE19 firmware files
|
|
463
|
-
|
|
464
|
-
|
|
454
|
+
"RestorePSI2": "restorepsi2.bin",
|
|
455
|
+
"PSI2": "psi_ram2.bin",
|
|
465
456
|
# Mav20 Firmware file
|
|
466
|
-
|
|
457
|
+
"Misc": "multi_image.mbn",
|
|
467
458
|
}
|
|
468
|
-
|
|
459
|
+
|
|
460
|
+
bbfw_fn_elem_mav25 = {
|
|
461
|
+
# Mav25 Firmware files
|
|
462
|
+
"Misc": "multi_image.mbn",
|
|
463
|
+
"RestoreSBL1": "restorexbl_sc.elf",
|
|
464
|
+
"SBL1": "xbl_sc.elf",
|
|
465
|
+
"TME": "signed_firmware_soc_view.elf",
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return bbfw_fn_elem_mav25.get(elem) if bb_chip_id == 0x1F30E1 else bbfw_fn_elem.get(elem)
|
|
469
469
|
|
|
470
470
|
def fls_parse(self, buffer):
|
|
471
471
|
raise NotImplementedError()
|
|
@@ -476,10 +476,12 @@ class Restore(BaseRestore):
|
|
|
476
476
|
def fls_insert_ticket(self, fls, bbticket):
|
|
477
477
|
raise NotImplementedError()
|
|
478
478
|
|
|
479
|
-
def sign_bbfw(
|
|
479
|
+
def sign_bbfw(
|
|
480
|
+
self, bbfw_orig: bytes, bbtss: TSSResponse, bb_nonce: Optional[bytes], bb_chip_id: Optional[int] = None
|
|
481
|
+
) -> bytes:
|
|
480
482
|
# check for BBTicket in result
|
|
481
483
|
bbticket = bbtss.bb_ticket
|
|
482
|
-
bbfw_dict = bbtss.get(
|
|
484
|
+
bbfw_dict = bbtss.get("BasebandFirmware")
|
|
483
485
|
is_fls = False
|
|
484
486
|
signed_file = []
|
|
485
487
|
|
|
@@ -488,99 +490,100 @@ class Restore(BaseRestore):
|
|
|
488
490
|
tmp_zip_read_name = tmp_zip_read.name
|
|
489
491
|
|
|
490
492
|
try:
|
|
491
|
-
with zipfile.ZipFile(tmp_zip_read_name,
|
|
492
|
-
|
|
493
|
-
bbfw_patched = zipfile.ZipFile(tmp_zip_write, 'w')
|
|
493
|
+
with zipfile.ZipFile(tmp_zip_read_name, "r") as bbfw_orig, tempfile.NamedTemporaryFile() as tmp_zip_write:
|
|
494
|
+
bbfw_patched = zipfile.ZipFile(tmp_zip_write, "w")
|
|
494
495
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
496
|
+
for key, blob in bbfw_dict.items():
|
|
497
|
+
if key.endswith("-Blob") and isinstance(blob, bytes):
|
|
498
|
+
key = key.split("-", 1)[0]
|
|
499
|
+
signfn = self.get_bbfw_fn_for_element(key, bb_chip_id)
|
|
499
500
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
501
|
+
if signfn is None:
|
|
502
|
+
raise PyMobileDevice3Exception(
|
|
503
|
+
f"can't match element name '{key}' to baseband firmware file name."
|
|
504
|
+
)
|
|
503
505
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
+
if signfn.endswith(".fls"):
|
|
507
|
+
is_fls = True
|
|
506
508
|
|
|
507
|
-
|
|
509
|
+
buffer = bbfw_orig.read(signfn)
|
|
508
510
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
511
|
+
if is_fls:
|
|
512
|
+
raise NotImplementedError("is_fls")
|
|
513
|
+
elif bb_chip_id == 0x1F30E1: # Mav25 - Qualcomm Snapdragon X80 5G Modem
|
|
514
|
+
data = mbn_mav25_stitch(buffer, blob)
|
|
515
|
+
else:
|
|
516
|
+
data = mbn_stitch(buffer, blob)
|
|
515
517
|
|
|
516
|
-
|
|
518
|
+
bbfw_patched.writestr(bbfw_orig.getinfo(signfn), data)
|
|
517
519
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
signed_file.append(signfn)
|
|
521
|
-
else:
|
|
520
|
+
if is_fls and (bb_nonce is None):
|
|
521
|
+
if key == "RamPSI":
|
|
522
522
|
signed_file.append(signfn)
|
|
523
|
-
|
|
524
|
-
# remove everything but required files
|
|
525
|
-
for entry in bbfw_orig.filelist:
|
|
526
|
-
keep = False
|
|
527
|
-
filename = entry.filename
|
|
528
|
-
|
|
529
|
-
if filename in signed_file:
|
|
530
|
-
keep = True
|
|
531
|
-
|
|
532
|
-
# check for anything but .mbn and .fls if bb_nonce is set
|
|
533
|
-
if bb_nonce and not keep:
|
|
534
|
-
ext = os.path.splitext(filename)[1]
|
|
535
|
-
keep |= ext in ('.fls', '.mbn', '.elf', '.bin')
|
|
536
|
-
|
|
537
|
-
if keep and (filename not in signed_file):
|
|
538
|
-
bbfw_patched.writestr(bbfw_orig.getinfo(filename), bbfw_orig.read(filename))
|
|
539
|
-
|
|
540
|
-
if bb_nonce:
|
|
541
|
-
if is_fls:
|
|
542
|
-
# add BBTicket to file ebl.fls
|
|
543
|
-
buffer = bbfw_orig.read('ebl.fls')
|
|
544
|
-
fls = self.fls_parse(buffer)
|
|
545
|
-
data = self.fls_insert_ticket(fls, bbticket)
|
|
546
|
-
bbfw_patched.writestr('ebl.fls', data)
|
|
547
523
|
else:
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
524
|
+
signed_file.append(signfn)
|
|
525
|
+
|
|
526
|
+
# remove everything but required files
|
|
527
|
+
for entry in bbfw_orig.filelist:
|
|
528
|
+
keep = False
|
|
529
|
+
filename = entry.filename
|
|
530
|
+
|
|
531
|
+
if filename in signed_file:
|
|
532
|
+
keep = True
|
|
533
|
+
|
|
534
|
+
# check for anything but .mbn and .fls if bb_nonce is set
|
|
535
|
+
if bb_nonce and not keep:
|
|
536
|
+
ext = os.path.splitext(filename)[1]
|
|
537
|
+
keep |= ext in (".fls", ".mbn", ".elf", ".bin")
|
|
538
|
+
|
|
539
|
+
if keep and (filename not in signed_file):
|
|
540
|
+
bbfw_patched.writestr(bbfw_orig.getinfo(filename), bbfw_orig.read(filename))
|
|
541
|
+
|
|
542
|
+
if bb_nonce:
|
|
543
|
+
if is_fls:
|
|
544
|
+
# add BBTicket to file ebl.fls
|
|
545
|
+
buffer = bbfw_orig.read("ebl.fls")
|
|
546
|
+
fls = self.fls_parse(buffer)
|
|
547
|
+
data = self.fls_insert_ticket(fls, bbticket)
|
|
548
|
+
bbfw_patched.writestr("ebl.fls", data)
|
|
549
|
+
else:
|
|
550
|
+
# add BBTicket as bbticket.der
|
|
551
|
+
zname = zipfile.ZipInfo("bbticket.der")
|
|
552
|
+
zname.filename = "bbticket.der"
|
|
553
|
+
ZIP_EXT_ATTR_FILE = 0o100000
|
|
554
|
+
zname.external_attr = (0o644 | ZIP_EXT_ATTR_FILE) << 16
|
|
555
|
+
bbfw_patched.writestr(zname, bbticket)
|
|
556
|
+
|
|
557
|
+
bbfw_patched.close()
|
|
558
|
+
tmp_zip_write.seek(0)
|
|
559
|
+
return tmp_zip_write.read()
|
|
558
560
|
finally:
|
|
559
561
|
if tmp_zip_read_name:
|
|
560
562
|
os.remove(tmp_zip_read_name)
|
|
561
563
|
|
|
564
|
+
@asyncio_print_traceback
|
|
562
565
|
async def send_baseband_data(self, message: dict):
|
|
563
|
-
self.logger.info(f
|
|
566
|
+
self.logger.info(f"About to send BasebandData: {message}")
|
|
564
567
|
service = await self._get_service_for_data_request(message)
|
|
565
568
|
|
|
566
569
|
# NOTE: this function is called 2 or 3 times!
|
|
567
570
|
|
|
568
571
|
# setup request data
|
|
569
|
-
arguments = message[
|
|
570
|
-
bb_chip_id = arguments.get(
|
|
571
|
-
bb_cert_id = arguments.get(
|
|
572
|
-
bb_snum = arguments.get(
|
|
573
|
-
bb_nonce = arguments.get(
|
|
572
|
+
arguments = message["Arguments"]
|
|
573
|
+
bb_chip_id = arguments.get("ChipID")
|
|
574
|
+
bb_cert_id = arguments.get("CertID")
|
|
575
|
+
bb_snum = arguments.get("ChipSerialNo")
|
|
576
|
+
bb_nonce = arguments.get("Nonce")
|
|
574
577
|
bbtss = self.bbtss
|
|
575
578
|
|
|
576
579
|
if (bb_nonce is None) or (self.bbtss is None):
|
|
577
580
|
# populate parameters
|
|
578
|
-
parameters = {
|
|
581
|
+
parameters = {"ApECID": self.device.ecid}
|
|
579
582
|
if bb_nonce:
|
|
580
|
-
parameters[
|
|
581
|
-
parameters[
|
|
582
|
-
parameters[
|
|
583
|
-
parameters[
|
|
583
|
+
parameters["BbNonce"] = bb_nonce
|
|
584
|
+
parameters["BbChipID"] = bb_chip_id
|
|
585
|
+
parameters["BbGoldCertId"] = bb_cert_id
|
|
586
|
+
parameters["BbSNUM"] = bb_snum
|
|
584
587
|
|
|
585
588
|
self.populate_tss_request_from_manifest(parameters)
|
|
586
589
|
|
|
@@ -591,11 +594,11 @@ class Restore(BaseRestore):
|
|
|
591
594
|
request.add_common_tags(parameters)
|
|
592
595
|
request.add_baseband_tags(parameters)
|
|
593
596
|
|
|
594
|
-
fdr_support = self.build_identity[
|
|
597
|
+
fdr_support = self.build_identity["Info"].get("FDRSupport", False)
|
|
595
598
|
if fdr_support:
|
|
596
|
-
request.update({
|
|
599
|
+
request.update({"ApProductionMode": True, "ApSecurityMode": True})
|
|
597
600
|
|
|
598
|
-
self.logger.info(
|
|
601
|
+
self.logger.info("Sending Baseband TSS request...")
|
|
599
602
|
bbtss = await request.send_receive()
|
|
600
603
|
|
|
601
604
|
if bb_nonce:
|
|
@@ -603,124 +606,127 @@ class Restore(BaseRestore):
|
|
|
603
606
|
self.bbtss = bbtss
|
|
604
607
|
|
|
605
608
|
# get baseband firmware file path from build identity
|
|
606
|
-
bbfwpath = self.build_identity[
|
|
609
|
+
bbfwpath = self.build_identity["Manifest"]["BasebandFirmware"]["Info"]["Path"]
|
|
607
610
|
|
|
608
611
|
# extract baseband firmware to temp file
|
|
609
612
|
bbfw = self.ipsw.read(bbfwpath)
|
|
610
613
|
|
|
611
|
-
buffer = self.sign_bbfw(bbfw, bbtss, bb_nonce)
|
|
614
|
+
buffer = self.sign_bbfw(bbfw, bbtss, bb_nonce, bb_chip_id)
|
|
612
615
|
|
|
613
|
-
self.logger.info(
|
|
614
|
-
await service.aio_send_plist({
|
|
616
|
+
self.logger.info("Sending BasebandData now...")
|
|
617
|
+
await service.aio_send_plist({"BasebandData": buffer})
|
|
615
618
|
|
|
616
619
|
async def send_fdr_trust_data(self, message: dict) -> None:
|
|
617
|
-
self.logger.info(
|
|
620
|
+
self.logger.info("About to send FDR Trust data...")
|
|
618
621
|
service = await self._get_service_for_data_request(message)
|
|
619
622
|
|
|
620
623
|
# FIXME: What should we send here?
|
|
621
624
|
# Sending an empty dict makes it continue with FDR
|
|
622
625
|
# and this is what iTunes seems to be doing too
|
|
623
|
-
self.logger.info(
|
|
626
|
+
self.logger.info("Sending FDR Trust data now...")
|
|
624
627
|
await service.aio_send_plist({})
|
|
625
628
|
|
|
626
629
|
async def send_image_data(
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
self.logger.debug(f
|
|
630
|
-
arguments = message[
|
|
630
|
+
self, message: dict, image_list_k: Optional[str], image_type_k: Optional[str], image_data_k: Optional[str]
|
|
631
|
+
) -> None:
|
|
632
|
+
self.logger.debug(f"send_image_data: {message}")
|
|
633
|
+
arguments = message["Arguments"]
|
|
631
634
|
want_image_list = arguments.get(image_list_k)
|
|
632
|
-
image_name = arguments.get(
|
|
633
|
-
build_id_manifest = self.build_identity[
|
|
634
|
-
|
|
635
|
-
if
|
|
635
|
+
image_name = arguments.get("ImageName")
|
|
636
|
+
build_id_manifest = self.build_identity["Manifest"]
|
|
637
|
+
|
|
638
|
+
if (
|
|
639
|
+
(not want_image_list)
|
|
640
|
+
and (image_name is not None)
|
|
641
|
+
and (image_name not in build_id_manifest)
|
|
642
|
+
and (image_name.startswith("Ap"))
|
|
643
|
+
):
|
|
644
|
+
image_name = image_name.replace("Ap", "Ap,")
|
|
636
645
|
if image_name not in build_id_manifest:
|
|
637
|
-
|
|
638
|
-
image_name = image_name.replace('Ap', 'Ap,')
|
|
639
|
-
if image_name not in build_id_manifest:
|
|
640
|
-
raise PyMobileDevice3Exception(f'{image_name} not in build_id_manifest')
|
|
646
|
+
raise PyMobileDevice3Exception(f"{image_name} not in build_id_manifest")
|
|
641
647
|
|
|
642
648
|
if image_type_k is None:
|
|
643
|
-
image_type_k = arguments[
|
|
649
|
+
image_type_k = arguments["ImageType"]
|
|
644
650
|
|
|
645
651
|
if image_type_k is None:
|
|
646
|
-
raise PyMobileDevice3Exception(
|
|
652
|
+
raise PyMobileDevice3Exception("missing ImageType")
|
|
647
653
|
|
|
648
654
|
if want_image_list is None and image_name is None:
|
|
649
|
-
self.logger.info(f
|
|
655
|
+
self.logger.info(f"About to send {image_data_k}...")
|
|
650
656
|
|
|
651
657
|
matched_images = []
|
|
652
|
-
data_dict =
|
|
658
|
+
data_dict = {}
|
|
653
659
|
|
|
654
660
|
for component, manifest_entry in build_id_manifest.items():
|
|
655
661
|
if not isinstance(manifest_entry, dict):
|
|
656
662
|
continue
|
|
657
663
|
|
|
658
|
-
is_image_type = manifest_entry[
|
|
664
|
+
is_image_type = manifest_entry["Info"].get(image_type_k)
|
|
659
665
|
if is_image_type:
|
|
660
666
|
if want_image_list:
|
|
661
|
-
self.logger.info(f
|
|
667
|
+
self.logger.info(f"found {component} component")
|
|
662
668
|
matched_images.append(component)
|
|
663
669
|
elif image_name is None or image_name == component:
|
|
664
670
|
if image_name is None:
|
|
665
|
-
self.logger.info(f
|
|
671
|
+
self.logger.info(f"found {image_type_k} component '{component}'")
|
|
666
672
|
else:
|
|
667
|
-
self.logger.info(f
|
|
673
|
+
self.logger.info(f"found component '{component}'")
|
|
668
674
|
|
|
669
675
|
data_dict[component] = self.get_personalized_data(component, tss=self.recovery.tss)
|
|
670
676
|
|
|
671
|
-
req =
|
|
677
|
+
req = {}
|
|
672
678
|
if want_image_list:
|
|
673
679
|
req[image_list_k] = matched_images
|
|
674
|
-
self.logger.info(f
|
|
680
|
+
self.logger.info(f"Sending {image_type_k} image list")
|
|
675
681
|
else:
|
|
676
682
|
if image_name:
|
|
677
683
|
if image_name in data_dict:
|
|
678
684
|
req[image_data_k] = data_dict[image_name]
|
|
679
|
-
req[
|
|
680
|
-
self.logger.info(f
|
|
685
|
+
req["ImageName"] = image_name
|
|
686
|
+
self.logger.info(f"Sending {image_type_k} for {image_name}...")
|
|
681
687
|
else:
|
|
682
688
|
req[image_data_k] = data_dict
|
|
683
|
-
self.logger.info(f
|
|
689
|
+
self.logger.info(f"Sending {image_type_k} now...")
|
|
684
690
|
|
|
685
691
|
await self._restored.send(req)
|
|
686
692
|
|
|
687
693
|
async def send_bootability_bundle_data(self, message: dict) -> None:
|
|
688
|
-
self.logger.debug(f
|
|
694
|
+
self.logger.debug(f"send_bootability_bundle_data: {message}")
|
|
689
695
|
service = await self._get_service_for_data_request(message)
|
|
690
696
|
await service.aio_sendall(self.ipsw.bootability)
|
|
691
697
|
await service.aio_close()
|
|
692
698
|
|
|
693
699
|
async def send_manifest(self) -> None:
|
|
694
|
-
self.logger.debug(
|
|
695
|
-
await self._restored.send({
|
|
700
|
+
self.logger.debug("send_manifest")
|
|
701
|
+
await self._restored.send({"ReceiptManifest": self.build_identity.manifest})
|
|
696
702
|
|
|
697
703
|
async def get_se_firmware_data(self, updater_name: str, info: dict, arguments: dict) -> dict:
|
|
698
|
-
chip_id = info.get(
|
|
704
|
+
chip_id = info.get("SE,ChipID")
|
|
699
705
|
if chip_id is None:
|
|
700
|
-
chip_id = self.build_identity[
|
|
706
|
+
chip_id = self.build_identity["Manifest"]["SE,ChipID"]
|
|
701
707
|
|
|
702
708
|
if chip_id == 0x20211:
|
|
703
|
-
comp_name =
|
|
704
|
-
elif chip_id in (0x73, 0x64, 0xC8, 0xD2,
|
|
705
|
-
comp_name =
|
|
709
|
+
comp_name = "SE,Firmware"
|
|
710
|
+
elif chip_id in (0x73, 0x64, 0xC8, 0xD2, 0x2C, 0x36, 0x37):
|
|
711
|
+
comp_name = "SE,UpdatePayload"
|
|
706
712
|
else:
|
|
707
|
-
self.logger.warning(f
|
|
713
|
+
self.logger.warning(f"Unknown SE,ChipID {chip_id} detected. Restore might fail.")
|
|
708
714
|
|
|
709
|
-
if self.build_identity.has_component(
|
|
710
|
-
comp_name =
|
|
711
|
-
elif self.build_identity.has_component(
|
|
712
|
-
comp_name =
|
|
715
|
+
if self.build_identity.has_component("SE,UpdatePayload"):
|
|
716
|
+
comp_name = "SE,UpdatePayload"
|
|
717
|
+
elif self.build_identity.has_component("SE,Firmware"):
|
|
718
|
+
comp_name = "SE,Firmware"
|
|
713
719
|
else:
|
|
714
|
-
raise NotImplementedError(
|
|
720
|
+
raise NotImplementedError("Neither 'SE,Firmware' nor 'SE,UpdatePayload' found in build identity.")
|
|
715
721
|
|
|
716
722
|
component_data = self.build_identity.get_component(comp_name).data
|
|
717
723
|
|
|
718
|
-
if
|
|
724
|
+
if "DeviceGeneratedTags" in arguments:
|
|
719
725
|
response = self.get_device_generated_firmware_data(updater_name, info, arguments)
|
|
720
726
|
else:
|
|
721
727
|
# create SE request
|
|
722
728
|
request = TSSRequest()
|
|
723
|
-
parameters =
|
|
729
|
+
parameters = {}
|
|
724
730
|
|
|
725
731
|
# add manifest for current build_identity to parameters
|
|
726
732
|
self.populate_tss_request_from_manifest(parameters)
|
|
@@ -731,22 +737,22 @@ class Restore(BaseRestore):
|
|
|
731
737
|
# add required tags for SE TSS request
|
|
732
738
|
request.add_se_tags(parameters, None)
|
|
733
739
|
|
|
734
|
-
self.logger.info(
|
|
740
|
+
self.logger.info("Sending SE TSS request...")
|
|
735
741
|
response = await request.send_receive()
|
|
736
742
|
|
|
737
|
-
if
|
|
738
|
-
self.logger.info(
|
|
743
|
+
if "SE,Ticket" in response:
|
|
744
|
+
self.logger.info("Received SE ticket")
|
|
739
745
|
else:
|
|
740
|
-
raise PyMobileDevice3Exception(
|
|
746
|
+
raise PyMobileDevice3Exception("No 'SE,Ticket' in TSS response, this might not work")
|
|
741
747
|
|
|
742
|
-
response[
|
|
748
|
+
response["FirmwareData"] = component_data
|
|
743
749
|
|
|
744
750
|
return response
|
|
745
751
|
|
|
746
752
|
async def get_yonkers_firmware_data(self, info: dict):
|
|
747
753
|
# create Yonkers request
|
|
748
754
|
request = TSSRequest()
|
|
749
|
-
parameters =
|
|
755
|
+
parameters = {}
|
|
750
756
|
|
|
751
757
|
# add manifest for current build_identity to parameters
|
|
752
758
|
self.populate_tss_request_from_manifest(parameters)
|
|
@@ -758,33 +764,33 @@ class Restore(BaseRestore):
|
|
|
758
764
|
comp_name = request.add_yonkers_tags(parameters, None)
|
|
759
765
|
|
|
760
766
|
if comp_name is None:
|
|
761
|
-
raise PyMobileDevice3Exception(
|
|
767
|
+
raise PyMobileDevice3Exception("Could not determine Yonkers firmware component")
|
|
762
768
|
|
|
763
|
-
self.logger.debug(f
|
|
769
|
+
self.logger.debug(f"restore_get_yonkers_firmware_data: using {comp_name}")
|
|
764
770
|
|
|
765
|
-
self.logger.info(
|
|
771
|
+
self.logger.info("Sending SE Yonkers request...")
|
|
766
772
|
response = await request.send_receive()
|
|
767
773
|
|
|
768
|
-
if
|
|
769
|
-
self.logger.info(
|
|
774
|
+
if "Yonkers,Ticket" in response:
|
|
775
|
+
self.logger.info("Received SE ticket")
|
|
770
776
|
else:
|
|
771
|
-
raise PyMobileDevice3Exception(
|
|
777
|
+
raise PyMobileDevice3Exception("No 'Yonkers,Ticket' in TSS response, this might not work")
|
|
772
778
|
|
|
773
779
|
# now get actual component data
|
|
774
780
|
component_data = self.build_identity.get_component(comp_name).data
|
|
775
781
|
|
|
776
782
|
firmware_data = {
|
|
777
|
-
|
|
783
|
+
"YonkersFirmware": component_data,
|
|
778
784
|
}
|
|
779
785
|
|
|
780
|
-
response[
|
|
786
|
+
response["FirmwareData"] = firmware_data
|
|
781
787
|
|
|
782
788
|
return response
|
|
783
789
|
|
|
784
790
|
async def get_savage_firmware_data(self, info: dict):
|
|
785
791
|
# create Savage request
|
|
786
792
|
request = TSSRequest()
|
|
787
|
-
parameters =
|
|
793
|
+
parameters = {}
|
|
788
794
|
|
|
789
795
|
# add manifest for current build_identity to parameters
|
|
790
796
|
self.populate_tss_request_from_manifest(parameters)
|
|
@@ -796,47 +802,47 @@ class Restore(BaseRestore):
|
|
|
796
802
|
comp_name = request.add_savage_tags(parameters, None)
|
|
797
803
|
|
|
798
804
|
if comp_name is None:
|
|
799
|
-
raise PyMobileDevice3Exception(
|
|
805
|
+
raise PyMobileDevice3Exception("Could not determine Savage firmware component")
|
|
800
806
|
|
|
801
|
-
self.logger.debug(f
|
|
807
|
+
self.logger.debug(f"restore_get_savage_firmware_data: using {comp_name}")
|
|
802
808
|
|
|
803
|
-
self.logger.info(
|
|
809
|
+
self.logger.info("Sending SE Savage request...")
|
|
804
810
|
response = await request.send_receive()
|
|
805
811
|
|
|
806
|
-
if
|
|
807
|
-
self.logger.info(
|
|
812
|
+
if "Savage,Ticket" in response:
|
|
813
|
+
self.logger.info("Received SE ticket")
|
|
808
814
|
else:
|
|
809
|
-
raise PyMobileDevice3Exception(
|
|
815
|
+
raise PyMobileDevice3Exception("No 'Savage,Ticket' in TSS response, this might not work")
|
|
810
816
|
|
|
811
817
|
# now get actual component data
|
|
812
818
|
component_data = self.build_identity.get_component(comp_name).data
|
|
813
|
-
component_data = struct.pack(
|
|
819
|
+
component_data = struct.pack("<L", len(component_data)) + b"\x00" * 12
|
|
814
820
|
|
|
815
|
-
response[
|
|
821
|
+
response["FirmwareData"] = component_data
|
|
816
822
|
|
|
817
823
|
return response
|
|
818
824
|
|
|
819
825
|
async def get_rose_firmware_data(self, updater_name: str, info: dict, arguments: dict):
|
|
820
|
-
self.logger.info(f
|
|
826
|
+
self.logger.info(f"get_rose_firmware_data: {info}")
|
|
821
827
|
|
|
822
|
-
if
|
|
828
|
+
if "DeviceGeneratedTags" in arguments:
|
|
823
829
|
response = self.get_device_generated_firmware_data(updater_name, info, arguments)
|
|
824
830
|
return response
|
|
825
831
|
else:
|
|
826
832
|
# create Rose request
|
|
827
833
|
request = TSSRequest()
|
|
828
|
-
parameters =
|
|
834
|
+
parameters = {}
|
|
829
835
|
|
|
830
836
|
# add manifest for current build_identity to parameters
|
|
831
837
|
self.populate_tss_request_from_manifest(parameters)
|
|
832
838
|
|
|
833
|
-
parameters[
|
|
839
|
+
parameters["ApProductionMode"] = True
|
|
834
840
|
|
|
835
841
|
if self.device.is_image4_supported:
|
|
836
|
-
parameters[
|
|
837
|
-
parameters[
|
|
842
|
+
parameters["ApSecurityMode"] = True
|
|
843
|
+
parameters["ApSupportsImg4"] = True
|
|
838
844
|
else:
|
|
839
|
-
parameters[
|
|
845
|
+
parameters["ApSupportsImg4"] = False
|
|
840
846
|
|
|
841
847
|
# add Rap,* tags from info dictionary to parameters
|
|
842
848
|
parameters.update(info)
|
|
@@ -844,42 +850,42 @@ class Restore(BaseRestore):
|
|
|
844
850
|
# add required tags for Rose TSS request
|
|
845
851
|
request.add_rose_tags(parameters, None)
|
|
846
852
|
|
|
847
|
-
self.logger.info(
|
|
853
|
+
self.logger.info("Sending Rose TSS request...")
|
|
848
854
|
response = await request.send_receive()
|
|
849
855
|
|
|
850
|
-
rose_ticket = response.get(
|
|
856
|
+
rose_ticket = response.get("Rap,Ticket")
|
|
851
857
|
if rose_ticket is None:
|
|
852
858
|
self.logger.error('No "Rap,Ticket" in TSS response, this might not work')
|
|
853
859
|
|
|
854
|
-
comp_name =
|
|
860
|
+
comp_name = "Rap,RTKitOS"
|
|
855
861
|
component_data = self.build_identity.get_component(comp_name).data
|
|
856
862
|
|
|
857
863
|
ftab = Ftab(component_data)
|
|
858
864
|
|
|
859
|
-
comp_name =
|
|
865
|
+
comp_name = "Rap,RestoreRTKitOS"
|
|
860
866
|
if self.build_identity.has_component(comp_name):
|
|
861
867
|
rftab = Ftab(self.build_identity.get_component(comp_name).data)
|
|
862
868
|
|
|
863
|
-
component_data = rftab.get_entry_data(b
|
|
869
|
+
component_data = rftab.get_entry_data(b"rrko")
|
|
864
870
|
if component_data is None:
|
|
865
871
|
self.logger.error('Could not find "rrko" entry in ftab. This will probably break things')
|
|
866
872
|
else:
|
|
867
|
-
ftab.add_entry(b
|
|
873
|
+
ftab.add_entry(b"rrko", component_data)
|
|
868
874
|
|
|
869
|
-
response[
|
|
875
|
+
response["FirmwareData"] = ftab.data
|
|
870
876
|
|
|
871
877
|
return response
|
|
872
878
|
|
|
873
879
|
async def get_veridian_firmware_data(self, updater_name: str, info: dict, arguments: dict):
|
|
874
|
-
self.logger.info(f
|
|
875
|
-
comp_name =
|
|
880
|
+
self.logger.info(f"get_veridian_firmware_data: {info}")
|
|
881
|
+
comp_name = "BMU,FirmwareMap"
|
|
876
882
|
|
|
877
|
-
if
|
|
883
|
+
if "DeviceGeneratedTags" in arguments:
|
|
878
884
|
response = self.get_device_generated_firmware_data(updater_name, info, arguments)
|
|
879
885
|
else:
|
|
880
886
|
# create Veridian request
|
|
881
887
|
request = TSSRequest()
|
|
882
|
-
parameters =
|
|
888
|
+
parameters = {}
|
|
883
889
|
|
|
884
890
|
# add manifest for current build_identity to parameters
|
|
885
891
|
self.populate_tss_request_from_manifest(parameters)
|
|
@@ -890,29 +896,29 @@ class Restore(BaseRestore):
|
|
|
890
896
|
# add required tags for Veridian TSS request
|
|
891
897
|
request.add_veridian_tags(parameters, None)
|
|
892
898
|
|
|
893
|
-
self.logger.info(
|
|
899
|
+
self.logger.info("Sending Veridian TSS request...")
|
|
894
900
|
response = await request.send_receive()
|
|
895
901
|
|
|
896
|
-
ticket = response.get(
|
|
902
|
+
ticket = response.get("BMU,Ticket")
|
|
897
903
|
if ticket is None:
|
|
898
904
|
self.logger.warning('No "BMU,Ticket" in TSS response, this might not work')
|
|
899
905
|
|
|
900
906
|
component_data = self.build_identity.get_component(comp_name).data
|
|
901
907
|
fw_map = plistlib.loads(component_data)
|
|
902
|
-
fw_map[
|
|
908
|
+
fw_map["fw_map_digest"] = self.build_identity["Manifest"][comp_name]["Digest"]
|
|
903
909
|
|
|
904
910
|
bin_plist = plistlib.dumps(fw_map, fmt=plistlib.PlistFormat.FMT_BINARY)
|
|
905
|
-
response[
|
|
911
|
+
response["FirmwareData"] = bin_plist
|
|
906
912
|
|
|
907
913
|
return response
|
|
908
914
|
|
|
909
915
|
async def get_tcon_firmware_data(self, info: dict):
|
|
910
|
-
self.logger.info(f
|
|
911
|
-
comp_name =
|
|
916
|
+
self.logger.info(f"restore_get_tcon_firmware_data: {info}")
|
|
917
|
+
comp_name = "Baobab,TCON"
|
|
912
918
|
|
|
913
919
|
# create Baobab request
|
|
914
920
|
request = TSSRequest()
|
|
915
|
-
parameters =
|
|
921
|
+
parameters = {}
|
|
916
922
|
|
|
917
923
|
# add manifest for current build_identity to parameters
|
|
918
924
|
self.populate_tss_request_from_manifest(parameters)
|
|
@@ -923,47 +929,46 @@ class Restore(BaseRestore):
|
|
|
923
929
|
# add required tags for Baobab TSS request
|
|
924
930
|
request.add_tcon_tags(parameters, None)
|
|
925
931
|
|
|
926
|
-
self.logger.info(
|
|
932
|
+
self.logger.info("Sending Baobab TSS request...")
|
|
927
933
|
response = await request.send_receive()
|
|
928
934
|
|
|
929
|
-
ticket = response.get(
|
|
935
|
+
ticket = response.get("Baobab,Ticket")
|
|
930
936
|
if ticket is None:
|
|
931
937
|
self.logger.warning('No "Baobab,Ticket" in TSS response, this might not work')
|
|
932
938
|
|
|
933
|
-
response[
|
|
939
|
+
response["FirmwareData"] = self.build_identity.get_component(comp_name).data
|
|
934
940
|
|
|
935
941
|
return response
|
|
936
942
|
|
|
937
943
|
async def get_device_generated_firmware_data(self, updater_name: str, info: dict, arguments: dict) -> dict:
|
|
938
|
-
self.logger.info(f
|
|
944
|
+
self.logger.info(f"get_device_generated_firmware_data ({updater_name}): {arguments}")
|
|
939
945
|
request = TSSRequest()
|
|
940
|
-
parameters =
|
|
946
|
+
parameters = {}
|
|
941
947
|
|
|
942
948
|
# add manifest for current build_identity to parameters
|
|
943
|
-
self.populate_tss_request_from_manifest(
|
|
944
|
-
parameters, arguments['DeviceGeneratedTags']['BuildIdentityTags'])
|
|
949
|
+
self.populate_tss_request_from_manifest(parameters, arguments["DeviceGeneratedTags"]["BuildIdentityTags"])
|
|
945
950
|
|
|
946
|
-
parameters[
|
|
947
|
-
parameters[
|
|
951
|
+
parameters["@BBTicket"] = True
|
|
952
|
+
parameters["ApSecurityMode"] = True
|
|
948
953
|
|
|
949
954
|
# by default, set it to True
|
|
950
|
-
parameters[
|
|
955
|
+
parameters["ApProductionMode"] = True
|
|
951
956
|
|
|
952
|
-
for k, v in arguments[
|
|
953
|
-
if k.endswith(
|
|
957
|
+
for k, v in arguments["MessageArgInfo"].items():
|
|
958
|
+
if k.endswith("ProductionMode"):
|
|
954
959
|
# if ApProductionMode should be overridden
|
|
955
|
-
parameters[
|
|
960
|
+
parameters["ApProductionMode"] = bool(v)
|
|
956
961
|
|
|
957
|
-
response_ticket = arguments[
|
|
962
|
+
response_ticket = arguments["DeviceGeneratedTags"]["ResponseTags"][0]
|
|
958
963
|
|
|
959
|
-
parameters.update(arguments[
|
|
964
|
+
parameters.update(arguments["DeviceGeneratedRequest"])
|
|
960
965
|
request.add_common_tags(info)
|
|
961
966
|
request.update(parameters)
|
|
962
967
|
|
|
963
|
-
for redacted_field in (
|
|
968
|
+
for redacted_field in ("RequiresUIDMode",):
|
|
964
969
|
request.remove_key(redacted_field)
|
|
965
970
|
|
|
966
|
-
self.logger.info(f
|
|
971
|
+
self.logger.info(f"Sending {updater_name} TSS request...")
|
|
967
972
|
response = await request.send_receive()
|
|
968
973
|
|
|
969
974
|
ticket = response.get(response_ticket)
|
|
@@ -974,198 +979,201 @@ class Restore(BaseRestore):
|
|
|
974
979
|
return response
|
|
975
980
|
|
|
976
981
|
async def get_timer_firmware_data(self, info: dict):
|
|
977
|
-
self.logger.info(f
|
|
982
|
+
self.logger.info(f"get_timer_firmware_data: {info}")
|
|
978
983
|
|
|
979
984
|
ftab = None
|
|
980
985
|
|
|
981
986
|
# create Timer request
|
|
982
987
|
request = TSSRequest()
|
|
983
|
-
parameters =
|
|
988
|
+
parameters = {}
|
|
984
989
|
|
|
985
990
|
# add manifest for current build_identity to parameters
|
|
986
991
|
self.populate_tss_request_from_manifest(parameters)
|
|
987
992
|
|
|
988
|
-
parameters[
|
|
993
|
+
parameters["ApProductionMode"] = True
|
|
989
994
|
if self.device.is_image4_supported:
|
|
990
|
-
parameters[
|
|
991
|
-
parameters[
|
|
995
|
+
parameters["ApSecurityMode"] = True
|
|
996
|
+
parameters["ApSupportsImg4"] = True
|
|
992
997
|
else:
|
|
993
|
-
parameters[
|
|
998
|
+
parameters["ApSupportsImg4"] = False
|
|
994
999
|
|
|
995
1000
|
# add Timer,* tags from info dictionary to parameters
|
|
996
|
-
info_array = info[
|
|
1001
|
+
info_array = info["InfoArray"]
|
|
997
1002
|
info_dict = info_array[0]
|
|
998
|
-
hwid = info_dict[
|
|
999
|
-
tag = info_dict[
|
|
1000
|
-
parameters[
|
|
1001
|
-
ticket_name = info_dict[
|
|
1002
|
-
parameters[
|
|
1003
|
-
parameters[f
|
|
1004
|
-
parameters[f
|
|
1005
|
-
parameters[f
|
|
1006
|
-
parameters[f
|
|
1007
|
-
parameters[f
|
|
1008
|
-
parameters[f
|
|
1009
|
-
parameters[f
|
|
1010
|
-
|
|
1011
|
-
ap_info = info[
|
|
1003
|
+
hwid = info_dict["HardwareID"]
|
|
1004
|
+
tag = info_dict["TagNumber"]
|
|
1005
|
+
parameters["TagNumber"] = tag
|
|
1006
|
+
ticket_name = info_dict["TicketName"]
|
|
1007
|
+
parameters["TicketName"] = ticket_name
|
|
1008
|
+
parameters[f"Timer,ChipID,{tag}"] = hwid["ChipID"]
|
|
1009
|
+
parameters[f"Timer,BoardID,{tag}"] = hwid["BoardID"]
|
|
1010
|
+
parameters[f"Timer,ECID,{tag}"] = hwid["ECID"]
|
|
1011
|
+
parameters[f"Timer,Nonce,{tag}"] = hwid["Nonce"]
|
|
1012
|
+
parameters[f"Timer,SecurityMode,{tag}"] = hwid["SecurityMode"]
|
|
1013
|
+
parameters[f"Timer,SecurityDomain,{tag}"] = hwid["SecurityDomain"]
|
|
1014
|
+
parameters[f"Timer,ProductionMode,{tag}"] = hwid["ProductionMode"]
|
|
1015
|
+
|
|
1016
|
+
ap_info = info["APInfo"]
|
|
1012
1017
|
parameters.update(ap_info)
|
|
1013
1018
|
|
|
1014
1019
|
# add required tags for Timer TSS request
|
|
1015
1020
|
request.add_timer_tags(parameters, None)
|
|
1016
1021
|
|
|
1017
|
-
self.logger.info(f
|
|
1022
|
+
self.logger.info(f"Sending {ticket_name} TSS request...")
|
|
1018
1023
|
response = await request.send_receive()
|
|
1019
1024
|
|
|
1020
1025
|
ticket = response.get(ticket_name)
|
|
1021
1026
|
if ticket is None:
|
|
1022
1027
|
self.logger.warning(f'No "{ticket_name}" in TSS response, this might not work')
|
|
1023
1028
|
|
|
1024
|
-
comp_name = f
|
|
1029
|
+
comp_name = f"Timer,RTKitOS,{tag}"
|
|
1025
1030
|
if self.build_identity.has_component(comp_name):
|
|
1026
1031
|
ftab = Ftab(self.build_identity.get_component(comp_name).data)
|
|
1027
|
-
if ftab.tag != b
|
|
1028
|
-
self.logger.warning(f
|
|
1032
|
+
if ftab.tag != b"rkos":
|
|
1033
|
+
self.logger.warning(f"Unexpected tag {ftab.tag}. continuing anyway.")
|
|
1029
1034
|
else:
|
|
1030
1035
|
self.logger.info(f'NOTE: Build identity does not have a "{comp_name}" component.')
|
|
1031
1036
|
|
|
1032
|
-
comp_name = f
|
|
1037
|
+
comp_name = f"Timer,RestoreRTKitOS,{tag}"
|
|
1033
1038
|
if self.build_identity.has_component(comp_name):
|
|
1034
1039
|
rftab = Ftab(self.build_identity.get_component(comp_name).data)
|
|
1035
1040
|
|
|
1036
|
-
component_data = rftab.get_entry_data(b
|
|
1041
|
+
component_data = rftab.get_entry_data(b"rrko")
|
|
1037
1042
|
if component_data is None:
|
|
1038
1043
|
self.logger.error('Could not find "rrko" entry in ftab. This will probably break things')
|
|
1039
1044
|
else:
|
|
1040
1045
|
if ftab is None:
|
|
1041
|
-
raise PyMobileDevice3Exception(
|
|
1042
|
-
ftab.add_entry(b
|
|
1046
|
+
raise PyMobileDevice3Exception("ftab is None")
|
|
1047
|
+
ftab.add_entry(b"rrko", component_data)
|
|
1043
1048
|
else:
|
|
1044
1049
|
self.logger.info(f'NOTE: Build identity does not have a "{comp_name}" component.')
|
|
1045
1050
|
|
|
1046
|
-
response[
|
|
1051
|
+
response["FirmwareData"] = ftab.data
|
|
1047
1052
|
|
|
1048
1053
|
return response
|
|
1049
1054
|
|
|
1050
1055
|
async def send_firmware_updater_data(self, message: dict):
|
|
1051
|
-
self.logger.debug(f
|
|
1056
|
+
self.logger.debug(f"got FirmwareUpdaterData request: {message}")
|
|
1052
1057
|
service = await self._get_service_for_data_request(message)
|
|
1053
|
-
arguments = message[
|
|
1054
|
-
s_type = arguments[
|
|
1055
|
-
updater_name = arguments[
|
|
1056
|
-
device_generated_request = arguments.get(
|
|
1058
|
+
arguments = message["Arguments"]
|
|
1059
|
+
s_type = arguments["MessageArgType"]
|
|
1060
|
+
updater_name = arguments["MessageArgUpdaterName"]
|
|
1061
|
+
device_generated_request = arguments.get("DeviceGeneratedRequest")
|
|
1057
1062
|
|
|
1058
|
-
if s_type not in (
|
|
1059
|
-
raise PyMobileDevice3Exception(f
|
|
1063
|
+
if s_type not in ("FirmwareResponseData",):
|
|
1064
|
+
raise PyMobileDevice3Exception(f"MessageArgType has unexpected value '{s_type}'")
|
|
1060
1065
|
|
|
1061
|
-
info = arguments[
|
|
1066
|
+
info = arguments["MessageArgInfo"]
|
|
1062
1067
|
|
|
1063
1068
|
if device_generated_request is not None:
|
|
1064
1069
|
fwdict = await self.get_device_generated_firmware_data(updater_name, info, arguments)
|
|
1065
1070
|
if fwdict is None:
|
|
1066
|
-
raise PyMobileDevice3Exception(f
|
|
1071
|
+
raise PyMobileDevice3Exception(f"Couldn't get {updater_name} firmware data")
|
|
1067
1072
|
|
|
1068
|
-
elif updater_name ==
|
|
1073
|
+
elif updater_name == "SE":
|
|
1069
1074
|
fwdict = await self.get_se_firmware_data(updater_name, info, arguments)
|
|
1070
1075
|
if fwdict is None:
|
|
1071
|
-
raise PyMobileDevice3Exception(
|
|
1076
|
+
raise PyMobileDevice3Exception("Couldn't get SE firmware data")
|
|
1072
1077
|
|
|
1073
|
-
elif updater_name ==
|
|
1074
|
-
fwtype =
|
|
1075
|
-
info2 = info.get(
|
|
1078
|
+
elif updater_name == "Savage":
|
|
1079
|
+
fwtype = "Savage"
|
|
1080
|
+
info2 = info.get("YonkersDeviceInfo")
|
|
1076
1081
|
if info2:
|
|
1077
|
-
fwtype =
|
|
1082
|
+
fwtype = "Yonkers"
|
|
1078
1083
|
fwdict = await self.get_yonkers_firmware_data(info2)
|
|
1079
1084
|
else:
|
|
1080
1085
|
fwdict = await self.get_savage_firmware_data(info)
|
|
1081
1086
|
if fwdict is None:
|
|
1082
|
-
raise PyMobileDevice3Exception(f
|
|
1087
|
+
raise PyMobileDevice3Exception(f"Couldn't get {fwtype} firmware data")
|
|
1083
1088
|
|
|
1084
|
-
elif updater_name ==
|
|
1089
|
+
elif updater_name == "Rose":
|
|
1085
1090
|
fwdict = await self.get_rose_firmware_data(updater_name, info, arguments)
|
|
1086
1091
|
if fwdict is None:
|
|
1087
|
-
raise PyMobileDevice3Exception(
|
|
1092
|
+
raise PyMobileDevice3Exception("Couldn't get Rose firmware data")
|
|
1088
1093
|
|
|
1089
|
-
elif updater_name ==
|
|
1094
|
+
elif updater_name == "T200":
|
|
1090
1095
|
fwdict = await self.get_veridian_firmware_data(updater_name, info, arguments)
|
|
1091
1096
|
if fwdict is None:
|
|
1092
|
-
raise PyMobileDevice3Exception(
|
|
1097
|
+
raise PyMobileDevice3Exception("Couldn't get Veridian firmware data")
|
|
1093
1098
|
|
|
1094
|
-
elif updater_name ==
|
|
1099
|
+
elif updater_name == "AppleTCON":
|
|
1095
1100
|
fwdict = await self.get_tcon_firmware_data(info)
|
|
1096
1101
|
if fwdict is None:
|
|
1097
|
-
raise PyMobileDevice3Exception(
|
|
1102
|
+
raise PyMobileDevice3Exception("Couldn't get TCON firmware data")
|
|
1098
1103
|
|
|
1099
|
-
elif updater_name ==
|
|
1104
|
+
elif updater_name == "AppleTypeCRetimer":
|
|
1100
1105
|
fwdict = await self.get_timer_firmware_data(info)
|
|
1101
1106
|
if fwdict is None:
|
|
1102
|
-
raise PyMobileDevice3Exception(
|
|
1107
|
+
raise PyMobileDevice3Exception("Couldn't get AppleTypeCRetimer firmware data")
|
|
1103
1108
|
|
|
1104
1109
|
else:
|
|
1105
|
-
raise PyMobileDevice3Exception(f
|
|
1110
|
+
raise PyMobileDevice3Exception(f"Got unknown updater name: {updater_name}")
|
|
1106
1111
|
|
|
1107
|
-
self.logger.info(
|
|
1108
|
-
await service.aio_send_plist({
|
|
1112
|
+
self.logger.info("Sending FirmwareResponse data now...")
|
|
1113
|
+
await service.aio_send_plist({"FirmwareResponseData": fwdict})
|
|
1109
1114
|
|
|
1110
1115
|
async def send_firmware_updater_preflight(self, message: dict) -> None:
|
|
1111
|
-
self.logger.warning(f
|
|
1116
|
+
self.logger.warning(f"send_firmware_updater_preflight: {message}")
|
|
1112
1117
|
service = await self._get_service_for_data_request(message)
|
|
1113
1118
|
await service.aio_send_plist({})
|
|
1114
1119
|
|
|
1115
1120
|
async def send_url_asset(self, message: dict) -> None:
|
|
1116
|
-
self.logger.info(f
|
|
1121
|
+
self.logger.info(f"send_url_asset: {message}")
|
|
1117
1122
|
service = await self._get_service_for_data_request(message)
|
|
1118
|
-
arguments = message[
|
|
1119
|
-
assert arguments[
|
|
1120
|
-
url = arguments[
|
|
1123
|
+
arguments = message["Arguments"]
|
|
1124
|
+
assert arguments["RequestMethod"] == "GET"
|
|
1125
|
+
url = arguments["RequestURL"]
|
|
1121
1126
|
|
|
1122
1127
|
if url in self._url_assets_cache:
|
|
1123
|
-
self.logger.debug(
|
|
1128
|
+
self.logger.debug("Using cached URLAsset")
|
|
1124
1129
|
response = self._url_assets_cache[url]
|
|
1125
1130
|
else:
|
|
1126
1131
|
loop = asyncio.get_event_loop()
|
|
1127
1132
|
with ThreadPoolExecutor() as executor:
|
|
1128
1133
|
response = await loop.run_in_executor(executor, requests.get, url)
|
|
1129
1134
|
self._url_assets_cache[url] = response
|
|
1130
|
-
await service.aio_send_plist(
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1135
|
+
await service.aio_send_plist(
|
|
1136
|
+
{
|
|
1137
|
+
"ResponseBody": response.content,
|
|
1138
|
+
"ResponseBodyDone": True,
|
|
1139
|
+
"ResponseHeaders": dict(response.headers),
|
|
1140
|
+
"ResponseStatus": response.status_code,
|
|
1141
|
+
},
|
|
1142
|
+
fmt=plistlib.FMT_BINARY,
|
|
1143
|
+
)
|
|
1136
1144
|
await service.aio_close()
|
|
1137
1145
|
|
|
1138
1146
|
async def send_streamed_image_decryption_key(self, message: dict) -> None:
|
|
1139
|
-
self.logger.info(f
|
|
1147
|
+
self.logger.info(f"send_streamed_image_decryption_key: {message}")
|
|
1140
1148
|
service = await self._get_service_for_data_request(message)
|
|
1141
|
-
arguments = message[
|
|
1142
|
-
assert arguments[
|
|
1149
|
+
arguments = message["Arguments"]
|
|
1150
|
+
assert arguments["RequestMethod"] == "POST"
|
|
1143
1151
|
|
|
1144
1152
|
response = requests.post(
|
|
1145
|
-
arguments[
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
self.logger.info(f'response {response} {response.content}')
|
|
1153
|
+
arguments["RequestURL"], headers=arguments["RequestAdditionalHeaders"], data=arguments["RequestBody"]
|
|
1154
|
+
)
|
|
1155
|
+
self.logger.info(f"response {response} {response.content}")
|
|
1149
1156
|
await service.aio_send_plist({
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1157
|
+
"ResponseBody": response.content,
|
|
1158
|
+
"ResponseBodyDone": True,
|
|
1159
|
+
"ResponseHeaders": dict(response.headers),
|
|
1160
|
+
"ResponseStatus": response.status_code,
|
|
1154
1161
|
})
|
|
1155
1162
|
|
|
1156
1163
|
async def send_component(self, component: str, component_name: Optional[str] = None) -> None:
|
|
1157
1164
|
if component_name is None:
|
|
1158
1165
|
component_name = component
|
|
1159
1166
|
|
|
1160
|
-
self.logger.info(f
|
|
1161
|
-
await self._restored.send(
|
|
1162
|
-
|
|
1167
|
+
self.logger.info(f"Sending now {component_name}...")
|
|
1168
|
+
await self._restored.send({
|
|
1169
|
+
f"{component_name}File": self.get_personalized_data(component, tss=self.recovery.tss)
|
|
1170
|
+
})
|
|
1163
1171
|
|
|
1164
1172
|
async def handle_data_request_msg(self, message: dict):
|
|
1165
|
-
self.logger.debug(f
|
|
1173
|
+
self.logger.debug(f"handle_data_request_msg: {message}")
|
|
1166
1174
|
|
|
1167
1175
|
# checks and see what kind of data restored is requests and pass the request to its own handler
|
|
1168
|
-
data_type = message.get(
|
|
1176
|
+
data_type = message.get("DataType")
|
|
1169
1177
|
|
|
1170
1178
|
if not isinstance(data_type, str):
|
|
1171
1179
|
return
|
|
@@ -1174,41 +1182,41 @@ class Restore(BaseRestore):
|
|
|
1174
1182
|
await self._data_request_handlers[data_type](message)
|
|
1175
1183
|
elif data_type in self._data_request_components:
|
|
1176
1184
|
await self._data_request_components[data_type](data_type)
|
|
1177
|
-
elif data_type ==
|
|
1178
|
-
await self.send_component(
|
|
1179
|
-
elif data_type ==
|
|
1180
|
-
await self.send_component(
|
|
1181
|
-
elif data_type ==
|
|
1182
|
-
await self.send_image_data(message,
|
|
1183
|
-
elif data_type ==
|
|
1184
|
-
await self.send_image_data(message,
|
|
1185
|
-
elif data_type ==
|
|
1186
|
-
await self.send_image_data(message,
|
|
1187
|
-
elif data_type ==
|
|
1185
|
+
elif data_type == "SystemImageRootHash":
|
|
1186
|
+
await self.send_component("SystemVolume", data_type)
|
|
1187
|
+
elif data_type == "SystemImageCanonicalMetadata":
|
|
1188
|
+
await self.send_component("Ap,SystemVolumeCanonicalMetadata", data_type)
|
|
1189
|
+
elif data_type == "FUDData":
|
|
1190
|
+
await self.send_image_data(message, "FUDImageList", "IsFUDFirmware", "FUDImageData")
|
|
1191
|
+
elif data_type == "PersonalizedData":
|
|
1192
|
+
await self.send_image_data(message, "ImageList", None, "ImageData")
|
|
1193
|
+
elif data_type == "EANData":
|
|
1194
|
+
await self.send_image_data(message, "EANImageList", "IsEarlyAccessFirmware", "EANData")
|
|
1195
|
+
elif data_type == "BootabilityBundle":
|
|
1188
1196
|
await self.send_bootability_bundle_data(message)
|
|
1189
|
-
elif data_type ==
|
|
1197
|
+
elif data_type == "ReceiptManifest":
|
|
1190
1198
|
await self.send_manifest()
|
|
1191
|
-
elif data_type ==
|
|
1199
|
+
elif data_type == "BasebandUpdaterOutputData":
|
|
1192
1200
|
await self.handle_baseband_updater_output_data(message)
|
|
1193
|
-
elif data_type ==
|
|
1201
|
+
elif data_type == "HostSystemTime":
|
|
1194
1202
|
await self.handle_host_system_time(message)
|
|
1195
1203
|
else:
|
|
1196
|
-
self.logger.error(f
|
|
1204
|
+
self.logger.error(f"unknown data request: {message}")
|
|
1197
1205
|
|
|
1198
1206
|
async def handle_previous_restore_log_msg(self, message: dict):
|
|
1199
|
-
restorelog = message[
|
|
1200
|
-
self.logger.debug(f
|
|
1207
|
+
restorelog = message["PreviousRestoreLog"]
|
|
1208
|
+
self.logger.debug(f"PreviousRestoreLog: {restorelog}")
|
|
1201
1209
|
|
|
1202
1210
|
async def handle_progress_msg(self, message: dict) -> None:
|
|
1203
|
-
operation = message[
|
|
1211
|
+
operation = message["Operation"]
|
|
1204
1212
|
if operation in PROGRESS_BAR_OPERATIONS:
|
|
1205
|
-
message[
|
|
1213
|
+
message["Operation"] = PROGRESS_BAR_OPERATIONS[operation]
|
|
1206
1214
|
|
|
1207
|
-
if message[
|
|
1208
|
-
progress = message[
|
|
1215
|
+
if message["Operation"] == "VERIFY_RESTORE":
|
|
1216
|
+
progress = message["Progress"]
|
|
1209
1217
|
|
|
1210
1218
|
if self._pb_verify_restore is None:
|
|
1211
|
-
self._pb_verify_restore = tqdm(total=100, desc=
|
|
1219
|
+
self._pb_verify_restore = tqdm(total=100, desc="verify-restore", dynamic_ncols=True)
|
|
1212
1220
|
self._pb_verify_restore_old_value = 0
|
|
1213
1221
|
|
|
1214
1222
|
self._pb_verify_restore.update(progress - self._pb_verify_restore_old_value)
|
|
@@ -1220,79 +1228,78 @@ class Restore(BaseRestore):
|
|
|
1220
1228
|
|
|
1221
1229
|
return
|
|
1222
1230
|
|
|
1223
|
-
self.logger.debug(f
|
|
1231
|
+
self.logger.debug(f"progress-bar: {message}")
|
|
1224
1232
|
|
|
1225
1233
|
async def handle_status_msg(self, message: dict):
|
|
1226
|
-
self.logger.debug(f
|
|
1227
|
-
status = message[
|
|
1228
|
-
log = message.get(
|
|
1234
|
+
self.logger.debug(f"status message: {message}")
|
|
1235
|
+
status = message["Status"]
|
|
1236
|
+
log = message.get("Log")
|
|
1229
1237
|
|
|
1230
1238
|
if log:
|
|
1231
1239
|
# this is the true device log that may inform us for anything that went wrong
|
|
1232
|
-
self.logger.debug(f
|
|
1240
|
+
self.logger.debug(f"log:\n{log}\n")
|
|
1233
1241
|
|
|
1234
1242
|
if status == 0:
|
|
1235
1243
|
self._restore_finished = True
|
|
1236
|
-
await self._restored.send({
|
|
1244
|
+
await self._restored.send({"MsgType": "ReceivedFinalStatusMsg"})
|
|
1237
1245
|
else:
|
|
1238
1246
|
if status in known_errors:
|
|
1239
1247
|
self.logger.error(known_errors[status])
|
|
1240
1248
|
else:
|
|
1241
|
-
self.logger.error(
|
|
1249
|
+
self.logger.error("unknown error")
|
|
1242
1250
|
|
|
1243
1251
|
async def handle_checkpoint_msg(self, message: dict):
|
|
1244
|
-
self.logger.debug(f
|
|
1252
|
+
self.logger.debug(f"checkpoint: {message}")
|
|
1245
1253
|
|
|
1246
1254
|
async def handle_bb_update_status_msg(self, message: dict):
|
|
1247
|
-
self.logger.debug(f
|
|
1248
|
-
if not message[
|
|
1255
|
+
self.logger.debug(f"bb_update_status_msg: {message}")
|
|
1256
|
+
if not message["Accepted"]:
|
|
1249
1257
|
raise PyMobileDevice3Exception(str(message))
|
|
1250
1258
|
|
|
1251
1259
|
async def handle_baseband_updater_output_data(self, message: dict) -> None:
|
|
1252
|
-
self.logger.debug(f
|
|
1253
|
-
data_port = message[
|
|
1260
|
+
self.logger.debug(f"restore_handle_baseband_updater_output_data: {message}")
|
|
1261
|
+
data_port = message["DataPort"]
|
|
1254
1262
|
|
|
1255
|
-
self.logger.info(
|
|
1263
|
+
self.logger.info("Connecting to baseband updater data port")
|
|
1256
1264
|
|
|
1257
1265
|
while True:
|
|
1258
1266
|
try:
|
|
1259
|
-
client = ServiceConnection.create_using_usbmux(self._restored.udid, data_port,
|
|
1260
|
-
connection_type='USB')
|
|
1267
|
+
client = ServiceConnection.create_using_usbmux(self._restored.udid, data_port, connection_type="USB")
|
|
1261
1268
|
break
|
|
1262
1269
|
except ConnectionFailedError:
|
|
1263
|
-
self.logger.debug(
|
|
1270
|
+
self.logger.debug("Retrying connection...")
|
|
1264
1271
|
|
|
1265
1272
|
if not client:
|
|
1266
|
-
raise ConnectionFailedError(f
|
|
1273
|
+
raise ConnectionFailedError(f"failed to establish connection to {data_port}")
|
|
1267
1274
|
|
|
1268
|
-
self.logger.info(
|
|
1275
|
+
self.logger.info("Connected to BasebandUpdaterOutputData data port")
|
|
1269
1276
|
|
|
1270
|
-
filename = f
|
|
1271
|
-
self.logger.info(f
|
|
1277
|
+
filename = f"updater_output-{self._restored.udid}.cpio"
|
|
1278
|
+
self.logger.info(f"Writing updater output into: {filename}")
|
|
1272
1279
|
|
|
1273
|
-
with open(filename,
|
|
1280
|
+
with open(filename, "wb") as f:
|
|
1274
1281
|
while True:
|
|
1275
1282
|
buf = client.recv()
|
|
1276
1283
|
if not buf:
|
|
1277
1284
|
break
|
|
1278
1285
|
f.write(buf)
|
|
1279
1286
|
|
|
1280
|
-
self.logger.debug(
|
|
1287
|
+
self.logger.debug("Closing connection of BasebandUpdaterOutputData data port")
|
|
1281
1288
|
client.close()
|
|
1282
1289
|
|
|
1283
1290
|
async def handle_host_system_time(self, message: dict) -> None:
|
|
1284
|
-
await self._restored.send({
|
|
1291
|
+
await self._restored.send({"SetHostTimeOnDevice": time.time()})
|
|
1285
1292
|
|
|
1286
1293
|
async def handle_restored_crash(self, message: dict) -> None:
|
|
1287
|
-
backtrace =
|
|
1288
|
-
self.logger.info(f
|
|
1294
|
+
backtrace = "\n".join(message["RestoredBacktrace"])
|
|
1295
|
+
self.logger.info(f"restored crashed. backtrace:\n{backtrace}")
|
|
1289
1296
|
|
|
1290
1297
|
async def handle_async_wait(self, message: dict) -> None:
|
|
1291
1298
|
self.logger.debug(message)
|
|
1292
1299
|
|
|
1293
1300
|
async def handle_restore_attestation(self, message: dict) -> None:
|
|
1294
1301
|
self.logger.debug(message)
|
|
1295
|
-
await self._restored.send({
|
|
1302
|
+
await self._restored.send({"RestoreShouldAttest": False})
|
|
1296
1303
|
|
|
1297
1304
|
async def _connect_to_restored_service(self):
|
|
1298
1305
|
while True:
|
|
@@ -1303,33 +1310,38 @@ class Restore(BaseRestore):
|
|
|
1303
1310
|
await asyncio.sleep(1)
|
|
1304
1311
|
|
|
1305
1312
|
async def restore_device(self) -> None:
|
|
1306
|
-
self.logger.debug(
|
|
1313
|
+
self.logger.debug("waiting for device to connect for restored service")
|
|
1307
1314
|
await self._connect_to_restored_service()
|
|
1308
1315
|
|
|
1309
|
-
self.logger.info(f
|
|
1310
|
-
self.logger.info(f
|
|
1311
|
-
self.logger.info(f
|
|
1316
|
+
self.logger.info(f"hardware info: {self._restored.hardware_info}")
|
|
1317
|
+
self.logger.info(f"version: {self._restored.version}")
|
|
1318
|
+
self.logger.info(f"saved_debug_info: {self._restored.saved_debug_info}")
|
|
1312
1319
|
|
|
1313
1320
|
if self.recovery.tss.bb_ticket is not None:
|
|
1314
1321
|
# initial TSS response contains a baseband ticket
|
|
1315
1322
|
self.bbtss = self.recovery.tss
|
|
1316
1323
|
|
|
1317
1324
|
if self._ignore_fdr:
|
|
1318
|
-
self.logger.info(
|
|
1325
|
+
self.logger.info("Establishing a mock FDR listener")
|
|
1319
1326
|
self._fdr = ServiceConnection.create_using_usbmux(
|
|
1320
|
-
self._restored.udid, FDRClient.SERVICE_PORT, connection_type=
|
|
1327
|
+
self._restored.udid, FDRClient.SERVICE_PORT, connection_type="USB"
|
|
1321
1328
|
)
|
|
1322
1329
|
else:
|
|
1323
|
-
self.logger.info(
|
|
1330
|
+
self.logger.info("Starting FDR listener thread")
|
|
1324
1331
|
start_fdr_thread(fdr_type.FDR_CTRL)
|
|
1325
1332
|
|
|
1326
|
-
sep = self.build_identity[
|
|
1327
|
-
spp = self.build_identity[
|
|
1328
|
-
opts = RestoreOptions(
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
+
sep = self.build_identity["Manifest"]["SEP"].get("Info")
|
|
1334
|
+
spp = self.build_identity["Info"].get("SystemPartitionPadding")
|
|
1335
|
+
opts = RestoreOptions(
|
|
1336
|
+
firmware_preflight_info=self._firmware_preflight_info,
|
|
1337
|
+
sep=sep,
|
|
1338
|
+
macos_variant=self.macos_variant,
|
|
1339
|
+
build_identity=self.build_identity,
|
|
1340
|
+
restore_boot_args=self.recovery.restore_boot_args,
|
|
1341
|
+
spp=spp,
|
|
1342
|
+
restore_behavior=self.build_identity.restore_behavior,
|
|
1343
|
+
msp=self.build_identity.minimum_system_partition,
|
|
1344
|
+
)
|
|
1333
1345
|
|
|
1334
1346
|
# start the restore process
|
|
1335
1347
|
await self._restored.start_restore(opts)
|
|
@@ -1342,17 +1354,17 @@ class Restore(BaseRestore):
|
|
|
1342
1354
|
message = await self._restored.recv()
|
|
1343
1355
|
|
|
1344
1356
|
# discover what kind of message has been received
|
|
1345
|
-
message_type = message.get(
|
|
1357
|
+
message_type = message.get("MsgType")
|
|
1346
1358
|
|
|
1347
1359
|
if message_type in self._handlers:
|
|
1348
1360
|
try:
|
|
1349
1361
|
await self._handlers[message_type](message)
|
|
1350
1362
|
except Exception:
|
|
1351
|
-
self.logger.
|
|
1363
|
+
self.logger.exception(f"Failed to handle {message_type}")
|
|
1352
1364
|
else:
|
|
1353
1365
|
# there might be some other message types i'm not aware of, but I think
|
|
1354
1366
|
# at least the "previous error logs" messages usually end up here
|
|
1355
|
-
self.logger.debug(f
|
|
1367
|
+
self.logger.debug(f"unhandled message type received: {message}")
|
|
1356
1368
|
|
|
1357
1369
|
async def update(self):
|
|
1358
1370
|
await self.recovery.boot_ramdisk()
|
|
@@ -1361,24 +1373,24 @@ class Restore(BaseRestore):
|
|
|
1361
1373
|
await self.restore_device()
|
|
1362
1374
|
|
|
1363
1375
|
async def _get_service_for_data_request(self, message: dict) -> ServiceConnection:
|
|
1364
|
-
data_port = message.get(
|
|
1376
|
+
data_port = message.get("DataPort")
|
|
1365
1377
|
if data_port is None:
|
|
1366
1378
|
return self._restored.service
|
|
1367
|
-
data_type = message[
|
|
1368
|
-
data_port = message[
|
|
1379
|
+
data_type = message["DataType"]
|
|
1380
|
+
data_port = message["DataPort"]
|
|
1369
1381
|
|
|
1370
|
-
self.logger.info(f
|
|
1382
|
+
self.logger.info(f"Connecting to {data_type} data port ({data_port})")
|
|
1371
1383
|
|
|
1372
1384
|
while True:
|
|
1373
1385
|
try:
|
|
1374
|
-
service = ServiceConnection.create_using_usbmux(self._restored.udid, data_port, connection_type=
|
|
1386
|
+
service = ServiceConnection.create_using_usbmux(self._restored.udid, data_port, connection_type="USB")
|
|
1375
1387
|
break
|
|
1376
1388
|
except ConnectionFailedError:
|
|
1377
|
-
self.logger.debug(
|
|
1389
|
+
self.logger.debug("Retrying connection...")
|
|
1378
1390
|
|
|
1379
1391
|
if not service:
|
|
1380
|
-
raise ConnectionFailedError(f
|
|
1392
|
+
raise ConnectionFailedError(f"failed to establish connection to {data_port}")
|
|
1381
1393
|
|
|
1382
|
-
self.logger.info(f
|
|
1394
|
+
self.logger.info(f"Connected to {data_type} data port ({data_port})")
|
|
1383
1395
|
await service.aio_start()
|
|
1384
1396
|
return service
|