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