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