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.
Files changed (164) hide show
  1. misc/plist_sniffer.py +15 -15
  2. misc/remotexpc_sniffer.py +29 -28
  3. misc/understanding_idevice_protocol_layers.md +15 -10
  4. pymobiledevice3/__main__.py +317 -127
  5. pymobiledevice3/_version.py +22 -4
  6. pymobiledevice3/bonjour.py +358 -113
  7. pymobiledevice3/ca.py +253 -16
  8. pymobiledevice3/cli/activation.py +31 -23
  9. pymobiledevice3/cli/afc.py +49 -40
  10. pymobiledevice3/cli/amfi.py +16 -21
  11. pymobiledevice3/cli/apps.py +87 -42
  12. pymobiledevice3/cli/backup.py +160 -90
  13. pymobiledevice3/cli/bonjour.py +44 -40
  14. pymobiledevice3/cli/cli_common.py +204 -198
  15. pymobiledevice3/cli/companion_proxy.py +14 -14
  16. pymobiledevice3/cli/crash.py +105 -56
  17. pymobiledevice3/cli/developer/__init__.py +62 -0
  18. pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
  19. pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
  20. pymobiledevice3/cli/developer/arbitration.py +50 -0
  21. pymobiledevice3/cli/developer/condition.py +33 -0
  22. pymobiledevice3/cli/developer/core_device.py +294 -0
  23. pymobiledevice3/cli/developer/debugserver.py +244 -0
  24. pymobiledevice3/cli/developer/dvt/__init__.py +438 -0
  25. pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
  26. pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
  27. pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
  28. pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
  29. pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
  30. pymobiledevice3/cli/developer/simulate_location.py +51 -0
  31. pymobiledevice3/cli/diagnostics/__init__.py +75 -0
  32. pymobiledevice3/cli/diagnostics/battery.py +47 -0
  33. pymobiledevice3/cli/idam.py +42 -0
  34. pymobiledevice3/cli/lockdown.py +108 -103
  35. pymobiledevice3/cli/mounter.py +158 -99
  36. pymobiledevice3/cli/notification.py +38 -26
  37. pymobiledevice3/cli/pcap.py +45 -24
  38. pymobiledevice3/cli/power_assertion.py +18 -17
  39. pymobiledevice3/cli/processes.py +17 -23
  40. pymobiledevice3/cli/profile.py +165 -109
  41. pymobiledevice3/cli/provision.py +35 -34
  42. pymobiledevice3/cli/remote.py +217 -129
  43. pymobiledevice3/cli/restore.py +159 -143
  44. pymobiledevice3/cli/springboard.py +63 -53
  45. pymobiledevice3/cli/syslog.py +193 -86
  46. pymobiledevice3/cli/usbmux.py +73 -33
  47. pymobiledevice3/cli/version.py +5 -7
  48. pymobiledevice3/cli/webinspector.py +376 -214
  49. pymobiledevice3/common.py +3 -1
  50. pymobiledevice3/exceptions.py +182 -58
  51. pymobiledevice3/irecv.py +52 -53
  52. pymobiledevice3/irecv_devices.py +1489 -464
  53. pymobiledevice3/lockdown.py +473 -275
  54. pymobiledevice3/lockdown_service_provider.py +15 -8
  55. pymobiledevice3/osu/os_utils.py +27 -9
  56. pymobiledevice3/osu/posix_util.py +34 -15
  57. pymobiledevice3/osu/win_util.py +14 -8
  58. pymobiledevice3/pair_records.py +102 -21
  59. pymobiledevice3/remote/common.py +8 -4
  60. pymobiledevice3/remote/core_device/app_service.py +94 -67
  61. pymobiledevice3/remote/core_device/core_device_service.py +17 -14
  62. pymobiledevice3/remote/core_device/device_info.py +5 -5
  63. pymobiledevice3/remote/core_device/diagnostics_service.py +19 -4
  64. pymobiledevice3/remote/core_device/file_service.py +53 -23
  65. pymobiledevice3/remote/remote_service_discovery.py +79 -45
  66. pymobiledevice3/remote/remotexpc.py +73 -44
  67. pymobiledevice3/remote/tunnel_service.py +442 -317
  68. pymobiledevice3/remote/utils.py +14 -13
  69. pymobiledevice3/remote/xpc_message.py +145 -125
  70. pymobiledevice3/resources/dsc_uuid_map.py +19 -19
  71. pymobiledevice3/resources/firmware_notifications.py +20 -16
  72. pymobiledevice3/resources/notifications.txt +144 -0
  73. pymobiledevice3/restore/asr.py +27 -27
  74. pymobiledevice3/restore/base_restore.py +110 -21
  75. pymobiledevice3/restore/consts.py +87 -66
  76. pymobiledevice3/restore/device.py +59 -12
  77. pymobiledevice3/restore/fdr.py +46 -48
  78. pymobiledevice3/restore/ftab.py +19 -19
  79. pymobiledevice3/restore/img4.py +163 -0
  80. pymobiledevice3/restore/mbn.py +587 -0
  81. pymobiledevice3/restore/recovery.py +151 -151
  82. pymobiledevice3/restore/restore.py +562 -544
  83. pymobiledevice3/restore/restore_options.py +131 -110
  84. pymobiledevice3/restore/restored_client.py +51 -31
  85. pymobiledevice3/restore/tss.py +385 -267
  86. pymobiledevice3/service_connection.py +252 -59
  87. pymobiledevice3/services/accessibilityaudit.py +202 -120
  88. pymobiledevice3/services/afc.py +962 -365
  89. pymobiledevice3/services/amfi.py +24 -30
  90. pymobiledevice3/services/companion.py +23 -19
  91. pymobiledevice3/services/crash_reports.py +71 -47
  92. pymobiledevice3/services/debugserver_applist.py +3 -3
  93. pymobiledevice3/services/device_arbitration.py +8 -8
  94. pymobiledevice3/services/device_link.py +101 -79
  95. pymobiledevice3/services/diagnostics.py +973 -967
  96. pymobiledevice3/services/dtfetchsymbols.py +8 -8
  97. pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
  98. pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
  99. pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
  100. pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
  101. pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
  102. pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
  103. pymobiledevice3/services/dvt/instruments/device_info.py +20 -11
  104. pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
  105. pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
  106. pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
  107. pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
  108. pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
  109. pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
  110. pymobiledevice3/services/dvt/instruments/process_control.py +35 -10
  111. pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
  112. pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
  113. pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
  114. pymobiledevice3/services/file_relay.py +10 -10
  115. pymobiledevice3/services/heartbeat.py +9 -8
  116. pymobiledevice3/services/house_arrest.py +16 -15
  117. pymobiledevice3/services/idam.py +20 -0
  118. pymobiledevice3/services/installation_proxy.py +173 -81
  119. pymobiledevice3/services/lockdown_service.py +20 -10
  120. pymobiledevice3/services/misagent.py +22 -19
  121. pymobiledevice3/services/mobile_activation.py +147 -64
  122. pymobiledevice3/services/mobile_config.py +331 -294
  123. pymobiledevice3/services/mobile_image_mounter.py +141 -113
  124. pymobiledevice3/services/mobilebackup2.py +203 -145
  125. pymobiledevice3/services/notification_proxy.py +11 -11
  126. pymobiledevice3/services/os_trace.py +134 -74
  127. pymobiledevice3/services/pcapd.py +314 -302
  128. pymobiledevice3/services/power_assertion.py +10 -9
  129. pymobiledevice3/services/preboard.py +4 -4
  130. pymobiledevice3/services/remote_fetch_symbols.py +21 -14
  131. pymobiledevice3/services/remote_server.py +176 -146
  132. pymobiledevice3/services/restore_service.py +16 -16
  133. pymobiledevice3/services/screenshot.py +15 -12
  134. pymobiledevice3/services/simulate_location.py +7 -7
  135. pymobiledevice3/services/springboard.py +15 -15
  136. pymobiledevice3/services/syslog.py +5 -5
  137. pymobiledevice3/services/web_protocol/alert.py +11 -11
  138. pymobiledevice3/services/web_protocol/automation_session.py +251 -239
  139. pymobiledevice3/services/web_protocol/cdp_screencast.py +46 -37
  140. pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
  141. pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
  142. pymobiledevice3/services/web_protocol/driver.py +114 -111
  143. pymobiledevice3/services/web_protocol/element.py +124 -111
  144. pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
  145. pymobiledevice3/services/web_protocol/selenium_api.py +49 -49
  146. pymobiledevice3/services/web_protocol/session_protocol.py +18 -12
  147. pymobiledevice3/services/web_protocol/switch_to.py +30 -27
  148. pymobiledevice3/services/webinspector.py +189 -155
  149. pymobiledevice3/tcp_forwarder.py +87 -69
  150. pymobiledevice3/tunneld/__init__.py +0 -0
  151. pymobiledevice3/tunneld/api.py +63 -0
  152. pymobiledevice3/tunneld/server.py +603 -0
  153. pymobiledevice3/usbmux.py +198 -147
  154. pymobiledevice3/utils.py +14 -11
  155. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/METADATA +55 -28
  156. pymobiledevice3-7.0.6.dist-info/RECORD +188 -0
  157. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/WHEEL +1 -1
  158. pymobiledevice3/cli/developer.py +0 -1215
  159. pymobiledevice3/cli/diagnostics.py +0 -99
  160. pymobiledevice3/tunneld.py +0 -524
  161. pymobiledevice3-4.14.6.dist-info/RECORD +0 -168
  162. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/entry_points.txt +0 -0
  163. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info/licenses}/LICENSE +0 -0
  164. {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 AlreadyMountedError, DeveloperDiskImageNotFoundError, \
11
- DeveloperModeIsNotEnabledError, InternalError, MessageNotSupportedError, MissingManifestError, \
12
- NoSuchBuildIdentityError, NotMountedError, PyMobileDevice3Exception, UnsupportedCommandError
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
- LATEST_DDI_BUILD_ID = '16A242d'
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 = 'com.apple.mobile.mobile_image_mounter'
24
- RSD_SERVICE_NAME = 'com.apple.mobile.mobile_image_mounter.shim.remote'
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
- """ Copy mounted devices list. """
52
+ """Copy mounted devices list."""
41
53
  try:
42
- return self.service.send_recv_plist({'Command': 'CopyDevices'})['EntryList']
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
- """ Lookup mounted image by its name. """
48
- response = self.service.send_recv_plist({'Command': 'LookupImage',
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('ImagePresent', True):
62
+ if not response or not response.get("ImagePresent", True):
52
63
  raise NotMountedError()
53
64
 
54
- signature = response.get('ImageSignature', [])
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
- """ umount image (Added on iOS 14.0) """
70
- request = {'Command': 'UnmountImage', 'MountPath': mount_path}
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('Error')
84
+ error = response.get("Error")
74
85
  if error:
75
- if error == 'UnknownCommand':
86
+ if error == "UnknownCommand":
76
87
  raise UnsupportedCommandError()
77
- elif 'There is no matching entry' in response.get('DetailedError', ''):
88
+ elif "There is no matching entry" in response.get("DetailedError", ""):
78
89
  raise NotMountedError(response)
79
- elif error == 'InternalError':
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
- """ Upload image into device. """
96
+ """Upload image into device."""
86
97
 
87
98
  if self.is_image_mounted(image_type):
88
99
  raise AlreadyMountedError()
89
100
 
90
- request = {'Command': 'MountImage',
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 'Developer mode is not enabled' in response.get('DetailedError', ''):
107
+ if "Developer mode is not enabled" in response.get("DetailedError", ""):
99
108
  raise DeveloperModeIsNotEnabledError()
100
109
 
101
- status = response.get('Status')
110
+ status = response.get("Status")
102
111
 
103
- if status != 'Complete':
104
- raise PyMobileDevice3Exception(f'command MountImage failed with: {response}')
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
- """ Upload image into device. """
108
- self.service.send_plist({'Command': 'ReceiveBytes',
109
- 'ImageType': image_type,
110
- 'ImageSize': len(image),
111
- 'ImageSignature': signature})
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('Status')
125
+ status = result.get("Status")
115
126
 
116
- if status != 'ReceiveBytesAck':
117
- raise PyMobileDevice3Exception(f'command ReceiveBytes failed with: {result}')
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('Status')
133
+ status = result.get("Status")
123
134
 
124
- if status != 'Complete':
125
- raise PyMobileDevice3Exception(f'command ReceiveBytes failed to send bytes with: {result}')
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({'Command': 'QueryDeveloperModeStatus'})
139
+ response = self.service.send_recv_plist({"Command": "QueryDeveloperModeStatus"})
129
140
 
130
141
  try:
131
- return response['DeveloperModeStatus']
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 = {'Command': 'QueryNonce'}
147
+ request = {"Command": "QueryNonce"}
137
148
  if personalized_image_type is not None:
138
- request['PersonalizedImageType'] = personalized_image_type
149
+ request["PersonalizedImageType"] = personalized_image_type
139
150
  response = self.service.send_recv_plist(request)
140
151
  try:
141
- return response['PersonalizationNonce']
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 = {'Command': 'QueryPersonalizationIdentifiers'}
157
+ request = {"Command": "QueryPersonalizationIdentifiers"}
147
158
 
148
159
  if image_type is not None:
149
- request['PersonalizedImageType'] = image_type
160
+ request["PersonalizedImageType"] = image_type
150
161
 
151
162
  response = self.service.send_recv_plist(request)
152
163
 
153
164
  try:
154
- return response['PersonalizationIdentifiers']
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
- 'Command': 'QueryPersonalizationManifest', 'PersonalizedImageType': image_type, 'ImageType': image_type,
161
- 'ImageSignature': signature})
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['ImageSignature']
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({'Command': 'RollPersonalizationNonce'})
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({'Command': 'RollCryptexNonce'})
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 = 'Developer'
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('/Developer')
207
+ self.unmount_image("/Developer")
194
208
 
195
209
 
196
210
  class PersonalizedImageMounter(MobileImageMounterService):
197
- IMAGE_TYPE = 'Personalized'
211
+ IMAGE_TYPE = "Personalized"
198
212
 
199
- async def mount(self, image: Path, build_manifest: Path, trust_cache: Path,
200
- info_plist: Optional[dict] = None) -> None:
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('DeveloperDiskImage', hashlib.sha384(image).digest())
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['ImageInfoPlist'] = info_plist
220
- extras['ImageTrustCache'] = trust_cache
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('/System/Developer')
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('Ap,'):
246
+ if key.startswith("Ap,"):
232
247
  request.update({key: value})
233
248
 
234
- board_id = personalization_identifiers['BoardId']
235
- chip_id = personalization_identifiers['ChipID']
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['BuildIdentities']:
239
- if int(tmp_build_identity['ApBoardID'], 0) == board_id and \
240
- int(tmp_build_identity['ApChipID'], 0) == chip_id:
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'Could not find the manifest for board {board_id} and chip {chip_id}')
245
- manifest = build_identity['Manifest']
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
- 'ApProductionMode': True,
249
- 'ApSecurityDomain': 1,
250
- 'ApSecurityMode': True,
251
- 'ApSupportsImg4': True,
265
+ "ApProductionMode": True,
266
+ "ApSecurityDomain": 1,
267
+ "ApSecurityMode": True,
268
+ "ApSupportsImg4": True,
252
269
  }
253
270
 
254
271
  request.update({
255
- '@ApImg4Ticket': True,
256
- '@BBTicket': True,
257
- 'ApBoardID': board_id,
258
- 'ApChipID': chip_id,
259
- 'ApECID': self.lockdown.ecid,
260
- 'ApNonce': self.query_nonce('DeveloperDiskImage'),
261
- 'ApProductionMode': True,
262
- 'ApSecurityDomain': 1,
263
- 'ApSecurityMode': True,
264
- 'SepNonce': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
265
- 'UID_MODE': False,
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('Info')
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('Trusted', False):
274
- self.logger.debug(f'skipping {key} as it is not trusted')
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('Info')
298
+ tss_entry.pop("Info")
282
299
 
283
300
  # handle RestoreRequestRules
284
- if 'RestoreRequestRules' in manifest['LoadableTrustCache']['Info']:
285
- rules = manifest['LoadableTrustCache']['Info']['RestoreRequestRules']
301
+ if "RestoreRequestRules" in manifest["LoadableTrustCache"]["Info"]:
302
+ rules = manifest["LoadableTrustCache"]["Info"]["RestoreRequestRules"]
286
303
  if rules:
287
- self.logger.debug(f'Applying restore request rules for entry {key}')
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('Digest') is None:
292
- tss_entry['Digest'] = b''
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['ApImg4Ticket']
314
+ return response["ApImg4Ticket"]
298
315
 
299
316
 
300
317
  def auto_mount_developer(
301
- lockdown: LockdownServiceProvider, xcode: Optional[str] = None, version: Optional[str] = None) -> None:
302
- """ auto-detect correct DeveloperDiskImage and mount it """
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('/Applications/Xcode.app')
323
+ xcode = Path("/Applications/Xcode.app")
306
324
  if not (xcode.exists()):
307
- xcode = get_home_folder() / 'Xcode.app'
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('Developer'):
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'{version.major}.{version.minor}'
317
- image_dir = f'{xcode}/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/{version}'
318
- image_path = f'{image_dir}/DeveloperDiskImage.dmg'
319
- signature = f'{image_path}.signature'
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() / 'Xcode_iOS_DDI_Personalized'
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 / 'Image.dmg'
346
- build_manifest = local_path / 'BuildManifest.plist'
347
- trustcache = local_path / 'Image.trustcache'
363
+ image = local_path / "Image.dmg"
364
+ build_manifest = local_path / "BuildManifest.plist"
365
+ trustcache = local_path / "Image.trustcache"
348
366
 
349
- if (not build_manifest.exists() or
350
- plistlib.loads(build_manifest.read_bytes()).get('ProductBuildVersion') != LATEST_DDI_BUILD_ID):
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(lockdown: LockdownServiceProvider, xcode: Optional[str] = None, version: Optional[str] = None) -> None:
363
- if Version(lockdown.product_version) < Version('17.0'):
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)