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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import hashlib
|
|
2
|
+
import logging
|
|
2
3
|
import plistlib
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from typing import Optional
|
|
@@ -7,21 +8,32 @@ from developer_disk_image.repo import DeveloperDiskImageRepository
|
|
|
7
8
|
from packaging.version import Version
|
|
8
9
|
|
|
9
10
|
from pymobiledevice3.common import get_home_folder
|
|
10
|
-
from pymobiledevice3.exceptions import
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
from pymobiledevice3.exceptions import (
|
|
12
|
+
AlreadyMountedError,
|
|
13
|
+
DeveloperDiskImageNotFoundError,
|
|
14
|
+
DeveloperModeIsNotEnabledError,
|
|
15
|
+
InternalError,
|
|
16
|
+
MessageNotSupportedError,
|
|
17
|
+
MissingManifestError,
|
|
18
|
+
NoSuchBuildIdentityError,
|
|
19
|
+
NotMountedError,
|
|
20
|
+
PyMobileDevice3Exception,
|
|
21
|
+
UnsupportedCommandError,
|
|
22
|
+
)
|
|
13
23
|
from pymobiledevice3.lockdown import LockdownClient
|
|
14
24
|
from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
|
|
15
25
|
from pymobiledevice3.restore.tss import TSSRequest
|
|
16
26
|
from pymobiledevice3.services.lockdown_service import LockdownService
|
|
17
27
|
|
|
18
|
-
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
LATEST_DDI_BUILD_ID = "17B5045g"
|
|
19
31
|
|
|
20
32
|
|
|
21
33
|
class MobileImageMounterService(LockdownService):
|
|
22
34
|
# implemented in /usr/libexec/mobile_storage_proxy
|
|
23
|
-
SERVICE_NAME =
|
|
24
|
-
RSD_SERVICE_NAME =
|
|
35
|
+
SERVICE_NAME = "com.apple.mobile.mobile_image_mounter"
|
|
36
|
+
RSD_SERVICE_NAME = "com.apple.mobile.mobile_image_mounter.shim.remote"
|
|
25
37
|
IMAGE_TYPE: Optional[str] = None
|
|
26
38
|
|
|
27
39
|
def __init__(self, lockdown: LockdownServiceProvider):
|
|
@@ -37,21 +49,20 @@ class MobileImageMounterService(LockdownService):
|
|
|
37
49
|
raise DeveloperModeIsNotEnabledError()
|
|
38
50
|
|
|
39
51
|
def copy_devices(self) -> list[dict]:
|
|
40
|
-
"""
|
|
52
|
+
"""Copy mounted devices list."""
|
|
41
53
|
try:
|
|
42
|
-
return self.service.send_recv_plist({
|
|
54
|
+
return self.service.send_recv_plist({"Command": "CopyDevices"})["EntryList"]
|
|
43
55
|
except KeyError as e:
|
|
44
56
|
raise MessageNotSupportedError from e
|
|
45
57
|
|
|
46
58
|
def lookup_image(self, image_type: str) -> bytes:
|
|
47
|
-
"""
|
|
48
|
-
response = self.service.send_recv_plist({
|
|
49
|
-
'ImageType': image_type})
|
|
59
|
+
"""Lookup mounted image by its name."""
|
|
60
|
+
response = self.service.send_recv_plist({"Command": "LookupImage", "ImageType": image_type})
|
|
50
61
|
|
|
51
|
-
if not response or not response.get(
|
|
62
|
+
if not response or not response.get("ImagePresent", True):
|
|
52
63
|
raise NotMountedError()
|
|
53
64
|
|
|
54
|
-
signature = response.get(
|
|
65
|
+
signature = response.get("ImageSignature", [])
|
|
55
66
|
if isinstance(signature, list):
|
|
56
67
|
if not signature:
|
|
57
68
|
raise NotMountedError()
|
|
@@ -61,125 +72,128 @@ class MobileImageMounterService(LockdownService):
|
|
|
61
72
|
def is_image_mounted(self, image_type: str) -> bool:
|
|
62
73
|
try:
|
|
63
74
|
self.lookup_image(image_type)
|
|
64
|
-
return True
|
|
65
75
|
except NotMountedError:
|
|
66
76
|
return False
|
|
77
|
+
return True
|
|
67
78
|
|
|
68
79
|
def unmount_image(self, mount_path: str) -> None:
|
|
69
|
-
"""
|
|
70
|
-
request = {
|
|
80
|
+
"""umount image (Added on iOS 14.0)"""
|
|
81
|
+
request = {"Command": "UnmountImage", "MountPath": mount_path}
|
|
71
82
|
response = self.service.send_recv_plist(request)
|
|
72
83
|
|
|
73
|
-
error = response.get(
|
|
84
|
+
error = response.get("Error")
|
|
74
85
|
if error:
|
|
75
|
-
if error ==
|
|
86
|
+
if error == "UnknownCommand":
|
|
76
87
|
raise UnsupportedCommandError()
|
|
77
|
-
elif
|
|
88
|
+
elif "There is no matching entry" in response.get("DetailedError", ""):
|
|
78
89
|
raise NotMountedError(response)
|
|
79
|
-
elif error ==
|
|
90
|
+
elif error == "InternalError":
|
|
80
91
|
raise InternalError(response)
|
|
81
92
|
else:
|
|
82
93
|
raise PyMobileDevice3Exception(response)
|
|
83
94
|
|
|
84
95
|
def mount_image(self, image_type: str, signature: bytes, extras: Optional[dict] = None) -> None:
|
|
85
|
-
"""
|
|
96
|
+
"""Upload image into device."""
|
|
86
97
|
|
|
87
98
|
if self.is_image_mounted(image_type):
|
|
88
99
|
raise AlreadyMountedError()
|
|
89
100
|
|
|
90
|
-
request = {
|
|
91
|
-
'ImageType': image_type,
|
|
92
|
-
'ImageSignature': signature}
|
|
101
|
+
request = {"Command": "MountImage", "ImageType": image_type, "ImageSignature": signature}
|
|
93
102
|
|
|
94
103
|
if extras is not None:
|
|
95
104
|
request.update(extras)
|
|
96
105
|
response = self.service.send_recv_plist(request)
|
|
97
106
|
|
|
98
|
-
if
|
|
107
|
+
if "Developer mode is not enabled" in response.get("DetailedError", ""):
|
|
99
108
|
raise DeveloperModeIsNotEnabledError()
|
|
100
109
|
|
|
101
|
-
status = response.get(
|
|
110
|
+
status = response.get("Status")
|
|
102
111
|
|
|
103
|
-
if status !=
|
|
104
|
-
raise PyMobileDevice3Exception(f
|
|
112
|
+
if status != "Complete":
|
|
113
|
+
raise PyMobileDevice3Exception(f"command MountImage failed with: {response}")
|
|
105
114
|
|
|
106
115
|
def upload_image(self, image_type: str, image: bytes, signature: bytes) -> None:
|
|
107
|
-
"""
|
|
108
|
-
self.service.send_plist({
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
"""Upload image into device."""
|
|
117
|
+
self.service.send_plist({
|
|
118
|
+
"Command": "ReceiveBytes",
|
|
119
|
+
"ImageType": image_type,
|
|
120
|
+
"ImageSize": len(image),
|
|
121
|
+
"ImageSignature": signature,
|
|
122
|
+
})
|
|
112
123
|
result = self.service.recv_plist()
|
|
113
124
|
|
|
114
|
-
status = result.get(
|
|
125
|
+
status = result.get("Status")
|
|
115
126
|
|
|
116
|
-
if status !=
|
|
117
|
-
raise PyMobileDevice3Exception(f
|
|
127
|
+
if status != "ReceiveBytesAck":
|
|
128
|
+
raise PyMobileDevice3Exception(f"command ReceiveBytes failed with: {result}")
|
|
118
129
|
|
|
119
130
|
self.service.sendall(image)
|
|
120
131
|
result = self.service.recv_plist()
|
|
121
132
|
|
|
122
|
-
status = result.get(
|
|
133
|
+
status = result.get("Status")
|
|
123
134
|
|
|
124
|
-
if status !=
|
|
125
|
-
raise PyMobileDevice3Exception(f
|
|
135
|
+
if status != "Complete":
|
|
136
|
+
raise PyMobileDevice3Exception(f"command ReceiveBytes failed to send bytes with: {result}")
|
|
126
137
|
|
|
127
138
|
def query_developer_mode_status(self) -> bool:
|
|
128
|
-
response = self.service.send_recv_plist({
|
|
139
|
+
response = self.service.send_recv_plist({"Command": "QueryDeveloperModeStatus"})
|
|
129
140
|
|
|
130
141
|
try:
|
|
131
|
-
return response[
|
|
142
|
+
return response["DeveloperModeStatus"]
|
|
132
143
|
except KeyError as e:
|
|
133
144
|
raise MessageNotSupportedError from e
|
|
134
145
|
|
|
135
146
|
def query_nonce(self, personalized_image_type: Optional[str] = None) -> bytes:
|
|
136
|
-
request = {
|
|
147
|
+
request = {"Command": "QueryNonce"}
|
|
137
148
|
if personalized_image_type is not None:
|
|
138
|
-
request[
|
|
149
|
+
request["PersonalizedImageType"] = personalized_image_type
|
|
139
150
|
response = self.service.send_recv_plist(request)
|
|
140
151
|
try:
|
|
141
|
-
return response[
|
|
152
|
+
return response["PersonalizationNonce"]
|
|
142
153
|
except KeyError as e:
|
|
143
154
|
raise MessageNotSupportedError from e
|
|
144
155
|
|
|
145
156
|
def query_personalization_identifiers(self, image_type: Optional[str] = None) -> dict:
|
|
146
|
-
request = {
|
|
157
|
+
request = {"Command": "QueryPersonalizationIdentifiers"}
|
|
147
158
|
|
|
148
159
|
if image_type is not None:
|
|
149
|
-
request[
|
|
160
|
+
request["PersonalizedImageType"] = image_type
|
|
150
161
|
|
|
151
162
|
response = self.service.send_recv_plist(request)
|
|
152
163
|
|
|
153
164
|
try:
|
|
154
|
-
return response[
|
|
165
|
+
return response["PersonalizationIdentifiers"]
|
|
155
166
|
except KeyError as e:
|
|
156
167
|
raise MessageNotSupportedError from e
|
|
157
168
|
|
|
158
169
|
def query_personalization_manifest(self, image_type: str, signature: bytes) -> bytes:
|
|
159
170
|
response = self.service.send_recv_plist({
|
|
160
|
-
|
|
161
|
-
|
|
171
|
+
"Command": "QueryPersonalizationManifest",
|
|
172
|
+
"PersonalizedImageType": image_type,
|
|
173
|
+
"ImageType": image_type,
|
|
174
|
+
"ImageSignature": signature,
|
|
175
|
+
})
|
|
162
176
|
try:
|
|
163
177
|
# The response "ImageSignature" is actually an IM4M
|
|
164
|
-
return response[
|
|
165
|
-
except KeyError:
|
|
166
|
-
raise MissingManifestError()
|
|
178
|
+
return response["ImageSignature"]
|
|
179
|
+
except KeyError as e:
|
|
180
|
+
raise MissingManifestError() from e
|
|
167
181
|
|
|
168
182
|
def roll_personalization_nonce(self) -> None:
|
|
169
183
|
try:
|
|
170
|
-
self.service.send_recv_plist({
|
|
184
|
+
self.service.send_recv_plist({"Command": "RollPersonalizationNonce"})
|
|
171
185
|
except ConnectionAbortedError:
|
|
172
186
|
return
|
|
173
187
|
|
|
174
188
|
def roll_cryptex_nonce(self) -> None:
|
|
175
189
|
try:
|
|
176
|
-
self.service.send_recv_plist({
|
|
190
|
+
self.service.send_recv_plist({"Command": "RollCryptexNonce"})
|
|
177
191
|
except ConnectionAbortedError:
|
|
178
192
|
return
|
|
179
193
|
|
|
180
194
|
|
|
181
195
|
class DeveloperDiskImageMounter(MobileImageMounterService):
|
|
182
|
-
IMAGE_TYPE =
|
|
196
|
+
IMAGE_TYPE = "Developer"
|
|
183
197
|
|
|
184
198
|
def mount(self, image: Path, signature: Path) -> None:
|
|
185
199
|
self.raise_if_cannot_mount()
|
|
@@ -190,14 +204,15 @@ class DeveloperDiskImageMounter(MobileImageMounterService):
|
|
|
190
204
|
self.mount_image(self.IMAGE_TYPE, signature)
|
|
191
205
|
|
|
192
206
|
def umount(self) -> None:
|
|
193
|
-
self.unmount_image(
|
|
207
|
+
self.unmount_image("/Developer")
|
|
194
208
|
|
|
195
209
|
|
|
196
210
|
class PersonalizedImageMounter(MobileImageMounterService):
|
|
197
|
-
IMAGE_TYPE =
|
|
211
|
+
IMAGE_TYPE = "Personalized"
|
|
198
212
|
|
|
199
|
-
async def mount(
|
|
200
|
-
|
|
213
|
+
async def mount(
|
|
214
|
+
self, image: Path, build_manifest: Path, trust_cache: Path, info_plist: Optional[dict] = None
|
|
215
|
+
) -> None:
|
|
201
216
|
self.raise_if_cannot_mount()
|
|
202
217
|
|
|
203
218
|
image = image.read_bytes()
|
|
@@ -207,7 +222,7 @@ class PersonalizedImageMounter(MobileImageMounterService):
|
|
|
207
222
|
# in case of failure, the service will close the socket, so we'll have to reestablish the connection
|
|
208
223
|
# and query the manifest from Apple's ticket server instead
|
|
209
224
|
try:
|
|
210
|
-
manifest = self.query_personalization_manifest(
|
|
225
|
+
manifest = self.query_personalization_manifest("DeveloperDiskImage", hashlib.sha384(image).digest())
|
|
211
226
|
except MissingManifestError:
|
|
212
227
|
self.service = self.lockdown.start_lockdown_service(self.service_name)
|
|
213
228
|
manifest = await self.get_manifest_from_tss(plistlib.loads(build_manifest.read_bytes()))
|
|
@@ -216,107 +231,110 @@ class PersonalizedImageMounter(MobileImageMounterService):
|
|
|
216
231
|
|
|
217
232
|
extras = {}
|
|
218
233
|
if info_plist is not None:
|
|
219
|
-
extras[
|
|
220
|
-
extras[
|
|
234
|
+
extras["ImageInfoPlist"] = info_plist
|
|
235
|
+
extras["ImageTrustCache"] = trust_cache
|
|
221
236
|
self.mount_image(self.IMAGE_TYPE, manifest, extras=extras)
|
|
222
237
|
|
|
223
238
|
def umount(self) -> None:
|
|
224
|
-
self.unmount_image(
|
|
239
|
+
self.unmount_image("/System/Developer")
|
|
225
240
|
|
|
226
241
|
async def get_manifest_from_tss(self, build_manifest: dict) -> bytes:
|
|
227
242
|
request = TSSRequest()
|
|
228
243
|
|
|
229
244
|
personalization_identifiers = self.query_personalization_identifiers()
|
|
230
245
|
for key, value in personalization_identifiers.items():
|
|
231
|
-
if key.startswith(
|
|
246
|
+
if key.startswith("Ap,"):
|
|
232
247
|
request.update({key: value})
|
|
233
248
|
|
|
234
|
-
board_id = personalization_identifiers[
|
|
235
|
-
chip_id = personalization_identifiers[
|
|
249
|
+
board_id = personalization_identifiers["BoardId"]
|
|
250
|
+
chip_id = personalization_identifiers["ChipID"]
|
|
236
251
|
|
|
237
252
|
build_identity = None
|
|
238
|
-
for tmp_build_identity in build_manifest[
|
|
239
|
-
if
|
|
240
|
-
|
|
253
|
+
for tmp_build_identity in build_manifest["BuildIdentities"]:
|
|
254
|
+
if (
|
|
255
|
+
int(tmp_build_identity["ApBoardID"], 0) == board_id
|
|
256
|
+
and int(tmp_build_identity["ApChipID"], 0) == chip_id
|
|
257
|
+
):
|
|
241
258
|
build_identity = tmp_build_identity
|
|
242
259
|
break
|
|
243
260
|
else:
|
|
244
|
-
raise NoSuchBuildIdentityError(f
|
|
245
|
-
manifest = build_identity[
|
|
261
|
+
raise NoSuchBuildIdentityError(f"Could not find the manifest for board {board_id} and chip {chip_id}")
|
|
262
|
+
manifest = build_identity["Manifest"]
|
|
246
263
|
|
|
247
264
|
parameters = {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
265
|
+
"ApProductionMode": True,
|
|
266
|
+
"ApSecurityDomain": 1,
|
|
267
|
+
"ApSecurityMode": True,
|
|
268
|
+
"ApSupportsImg4": True,
|
|
252
269
|
}
|
|
253
270
|
|
|
254
271
|
request.update({
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
272
|
+
"@ApImg4Ticket": True,
|
|
273
|
+
"@BBTicket": True,
|
|
274
|
+
"ApBoardID": board_id,
|
|
275
|
+
"ApChipID": chip_id,
|
|
276
|
+
"ApECID": self.lockdown.ecid,
|
|
277
|
+
"ApNonce": self.query_nonce("DeveloperDiskImage"),
|
|
278
|
+
"ApProductionMode": True,
|
|
279
|
+
"ApSecurityDomain": 1,
|
|
280
|
+
"ApSecurityMode": True,
|
|
281
|
+
"SepNonce": b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
|
282
|
+
"UID_MODE": False,
|
|
266
283
|
})
|
|
267
284
|
|
|
268
285
|
for key, manifest_entry in manifest.items():
|
|
269
|
-
info_dict = manifest_entry.get(
|
|
286
|
+
info_dict = manifest_entry.get("Info")
|
|
270
287
|
if info_dict is None:
|
|
271
288
|
continue
|
|
272
289
|
|
|
273
|
-
if not manifest_entry.get(
|
|
274
|
-
self.logger.debug(f
|
|
290
|
+
if not manifest_entry.get("Trusted", False):
|
|
291
|
+
self.logger.debug(f"skipping {key} as it is not trusted")
|
|
275
292
|
continue
|
|
276
293
|
|
|
277
294
|
# copy this entry
|
|
278
295
|
tss_entry = dict(manifest_entry)
|
|
279
296
|
|
|
280
297
|
# remove obsolete Info node
|
|
281
|
-
tss_entry.pop(
|
|
298
|
+
tss_entry.pop("Info")
|
|
282
299
|
|
|
283
300
|
# handle RestoreRequestRules
|
|
284
|
-
if
|
|
285
|
-
rules = manifest[
|
|
301
|
+
if "RestoreRequestRules" in manifest["LoadableTrustCache"]["Info"]:
|
|
302
|
+
rules = manifest["LoadableTrustCache"]["Info"]["RestoreRequestRules"]
|
|
286
303
|
if rules:
|
|
287
|
-
self.logger.debug(f
|
|
304
|
+
self.logger.debug(f"Applying restore request rules for entry {key}")
|
|
288
305
|
tss_entry = request.apply_restore_request_rules(tss_entry, parameters, rules)
|
|
289
306
|
|
|
290
307
|
# Make sure we have a Digest key for Trusted items even if empty
|
|
291
|
-
if manifest_entry.get(
|
|
292
|
-
tss_entry[
|
|
308
|
+
if manifest_entry.get("Digest") is None:
|
|
309
|
+
tss_entry["Digest"] = b""
|
|
293
310
|
|
|
294
311
|
request.update({key: tss_entry})
|
|
295
312
|
|
|
296
313
|
response = await request.send_receive()
|
|
297
|
-
return response[
|
|
314
|
+
return response["ApImg4Ticket"]
|
|
298
315
|
|
|
299
316
|
|
|
300
317
|
def auto_mount_developer(
|
|
301
|
-
|
|
302
|
-
|
|
318
|
+
lockdown: LockdownServiceProvider, xcode: Optional[str] = None, version: Optional[str] = None
|
|
319
|
+
) -> None:
|
|
320
|
+
"""auto-detect correct DeveloperDiskImage and mount it"""
|
|
303
321
|
if xcode is None:
|
|
304
322
|
# avoid "default"-ing this option, because Windows and Linux won't have this path
|
|
305
|
-
xcode = Path(
|
|
323
|
+
xcode = Path("/Applications/Xcode.app")
|
|
306
324
|
if not (xcode.exists()):
|
|
307
|
-
xcode = get_home_folder() /
|
|
325
|
+
xcode = get_home_folder() / "Xcode.app"
|
|
308
326
|
xcode.mkdir(parents=True, exist_ok=True)
|
|
309
327
|
|
|
310
328
|
image_mounter = DeveloperDiskImageMounter(lockdown=lockdown)
|
|
311
|
-
if image_mounter.is_image_mounted(
|
|
329
|
+
if image_mounter.is_image_mounted("Developer"):
|
|
312
330
|
raise AlreadyMountedError()
|
|
313
331
|
|
|
314
332
|
if version is None:
|
|
315
333
|
version = Version(lockdown.product_version)
|
|
316
|
-
version = f
|
|
317
|
-
image_dir = f
|
|
318
|
-
image_path = f
|
|
319
|
-
signature = f
|
|
334
|
+
version = f"{version.major}.{version.minor}"
|
|
335
|
+
image_dir = f"{xcode}/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/{version}"
|
|
336
|
+
image_path = f"{image_dir}/DeveloperDiskImage.dmg"
|
|
337
|
+
signature = f"{image_path}.signature"
|
|
320
338
|
developer_disk_image_dir = Path(image_path).parent
|
|
321
339
|
|
|
322
340
|
image_path = Path(image_path)
|
|
@@ -339,15 +357,17 @@ def auto_mount_developer(
|
|
|
339
357
|
|
|
340
358
|
|
|
341
359
|
async def auto_mount_personalized(lockdown: LockdownServiceProvider) -> None:
|
|
342
|
-
local_path = get_home_folder() /
|
|
360
|
+
local_path = get_home_folder() / "Xcode_iOS_DDI_Personalized"
|
|
343
361
|
local_path.mkdir(parents=True, exist_ok=True)
|
|
344
362
|
|
|
345
|
-
image = local_path /
|
|
346
|
-
build_manifest = local_path /
|
|
347
|
-
trustcache = local_path /
|
|
363
|
+
image = local_path / "Image.dmg"
|
|
364
|
+
build_manifest = local_path / "BuildManifest.plist"
|
|
365
|
+
trustcache = local_path / "Image.trustcache"
|
|
348
366
|
|
|
349
|
-
if (
|
|
350
|
-
|
|
367
|
+
if (
|
|
368
|
+
not build_manifest.exists()
|
|
369
|
+
or plistlib.loads(build_manifest.read_bytes()).get("ProductBuildVersion") != LATEST_DDI_BUILD_ID
|
|
370
|
+
):
|
|
351
371
|
# download the Personalized image from our repository
|
|
352
372
|
repo = DeveloperDiskImageRepository.create()
|
|
353
373
|
personalized_image = repo.get_personalized_disk_image()
|
|
@@ -355,12 +375,20 @@ async def auto_mount_personalized(lockdown: LockdownServiceProvider) -> None:
|
|
|
355
375
|
image.write_bytes(personalized_image.image)
|
|
356
376
|
build_manifest.write_bytes(personalized_image.build_manifest)
|
|
357
377
|
trustcache.write_bytes(personalized_image.trustcache)
|
|
378
|
+
downloaded_ddi_build_id = plistlib.loads(personalized_image.build_manifest).get("ProductBuildVersion")
|
|
379
|
+
if downloaded_ddi_build_id != LATEST_DDI_BUILD_ID:
|
|
380
|
+
logger.warning(
|
|
381
|
+
"Downloaded personalized image has unexpected ProductBuildVersion "
|
|
382
|
+
f"{downloaded_ddi_build_id}. Please update pymobiledevice3!"
|
|
383
|
+
)
|
|
358
384
|
|
|
359
385
|
await PersonalizedImageMounter(lockdown=lockdown).mount(image, build_manifest, trustcache)
|
|
360
386
|
|
|
361
387
|
|
|
362
|
-
async def auto_mount(
|
|
363
|
-
|
|
388
|
+
async def auto_mount(
|
|
389
|
+
lockdown: LockdownServiceProvider, xcode: Optional[str] = None, version: Optional[str] = None
|
|
390
|
+
) -> None:
|
|
391
|
+
if Version(lockdown.product_version) < Version("17.0"):
|
|
364
392
|
auto_mount_developer(lockdown, xcode=xcode, version=version)
|
|
365
393
|
else:
|
|
366
394
|
await auto_mount_personalized(lockdown)
|