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
|
@@ -7,13 +7,15 @@ from pymobiledevice3.remote.xpc_message import XpcInt64Type, XpcUInt64Type
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def _generate_core_device_version_dict(version: str) -> dict:
|
|
10
|
-
version_components = version.split(
|
|
11
|
-
return {
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
version_components = version.split(".")
|
|
11
|
+
return {
|
|
12
|
+
"components": [XpcUInt64Type(component) for component in version_components],
|
|
13
|
+
"originalComponentsCount": XpcInt64Type(len(version_components)),
|
|
14
|
+
"stringValue": version,
|
|
15
|
+
}
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
CORE_DEVICE_VERSION = _generate_core_device_version_dict(
|
|
18
|
+
CORE_DEVICE_VERSION = _generate_core_device_version_dict("325.3")
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
class CoreDeviceService(RemoteService):
|
|
@@ -21,14 +23,15 @@ class CoreDeviceService(RemoteService):
|
|
|
21
23
|
if input_ is None:
|
|
22
24
|
input_ = {}
|
|
23
25
|
response = await self.service.send_receive_request({
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
"CoreDevice.CoreDeviceDDIProtocolVersion": XpcInt64Type(0),
|
|
27
|
+
"CoreDevice.action": {},
|
|
28
|
+
"CoreDevice.coreDeviceVersion": CORE_DEVICE_VERSION,
|
|
29
|
+
"CoreDevice.deviceIdentifier": str(uuid.uuid4()),
|
|
30
|
+
"CoreDevice.featureIdentifier": feature_identifier,
|
|
31
|
+
"CoreDevice.input": input_,
|
|
32
|
+
"CoreDevice.invocationIdentifier": str(uuid.uuid4()),
|
|
33
|
+
})
|
|
34
|
+
output = response.get("CoreDevice.output")
|
|
32
35
|
if output is None:
|
|
33
|
-
raise CoreDeviceError(f
|
|
36
|
+
raise CoreDeviceError(f"Failed to invoke: {feature_identifier}. Got error: {response}")
|
|
34
37
|
return output
|
|
@@ -7,7 +7,7 @@ class DeviceInfoService(CoreDeviceService):
|
|
|
7
7
|
Query device information
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
SERVICE_NAME =
|
|
10
|
+
SERVICE_NAME = "com.apple.coredevice.deviceinfo"
|
|
11
11
|
|
|
12
12
|
def __init__(self, rsd: RemoteServiceDiscoveryService):
|
|
13
13
|
super().__init__(rsd, self.SERVICE_NAME)
|
|
@@ -16,13 +16,13 @@ class DeviceInfoService(CoreDeviceService):
|
|
|
16
16
|
"""
|
|
17
17
|
Get device information
|
|
18
18
|
"""
|
|
19
|
-
return await self.invoke(
|
|
19
|
+
return await self.invoke("com.apple.coredevice.feature.getdeviceinfo", {})
|
|
20
20
|
|
|
21
21
|
async def get_display_info(self) -> dict:
|
|
22
22
|
"""
|
|
23
23
|
Get display information
|
|
24
24
|
"""
|
|
25
|
-
return await self.invoke(
|
|
25
|
+
return await self.invoke("com.apple.coredevice.feature.getdisplayinfo", {})
|
|
26
26
|
|
|
27
27
|
async def query_mobilegestalt(self, keys: list[str]) -> dict:
|
|
28
28
|
"""
|
|
@@ -30,10 +30,10 @@ class DeviceInfoService(CoreDeviceService):
|
|
|
30
30
|
|
|
31
31
|
Can only be performed to specific devices
|
|
32
32
|
"""
|
|
33
|
-
return await self.invoke(
|
|
33
|
+
return await self.invoke("com.apple.coredevice.feature.querymobilegestalt", {"keys": keys})
|
|
34
34
|
|
|
35
35
|
async def get_lockstate(self) -> dict:
|
|
36
36
|
"""
|
|
37
37
|
Get lockstate
|
|
38
38
|
"""
|
|
39
|
-
return await self.invoke(
|
|
39
|
+
return await self.invoke("com.apple.coredevice.feature.getlockstate", {})
|
|
@@ -1,19 +1,34 @@
|
|
|
1
|
+
import dataclasses
|
|
1
2
|
from collections.abc import AsyncGenerator
|
|
2
3
|
|
|
3
4
|
from pymobiledevice3.remote.core_device.core_device_service import CoreDeviceService
|
|
4
5
|
from pymobiledevice3.remote.remote_service_discovery import RemoteServiceDiscoveryService
|
|
5
6
|
|
|
6
7
|
|
|
8
|
+
@dataclasses.dataclass
|
|
9
|
+
class SysDiagnoseResponse:
|
|
10
|
+
preferred_filename: str
|
|
11
|
+
file_size: int
|
|
12
|
+
generator: AsyncGenerator[bytes, None]
|
|
13
|
+
|
|
14
|
+
|
|
7
15
|
class DiagnosticsServiceService(CoreDeviceService):
|
|
8
16
|
"""
|
|
9
17
|
Obtain device diagnostics
|
|
10
18
|
"""
|
|
11
19
|
|
|
12
|
-
SERVICE_NAME =
|
|
20
|
+
SERVICE_NAME = "com.apple.coredevice.diagnosticsservice"
|
|
13
21
|
|
|
14
22
|
def __init__(self, rsd: RemoteServiceDiscoveryService):
|
|
15
23
|
super().__init__(rsd, self.SERVICE_NAME)
|
|
16
24
|
|
|
17
|
-
async def capture_sysdiagnose(self, is_dry_run: bool) ->
|
|
18
|
-
response = await self.invoke(
|
|
19
|
-
|
|
25
|
+
async def capture_sysdiagnose(self, is_dry_run: bool) -> SysDiagnoseResponse:
|
|
26
|
+
response = await self.invoke(
|
|
27
|
+
"com.apple.coredevice.feature.capturesysdiagnose",
|
|
28
|
+
{"options": {"collectFullLogs": True}, "isDryRun": is_dry_run},
|
|
29
|
+
)
|
|
30
|
+
return SysDiagnoseResponse(
|
|
31
|
+
file_size=response["fileTransfer"]["expectedLength"],
|
|
32
|
+
preferred_filename=response["preferredFilename"],
|
|
33
|
+
generator=self.service.iter_file_chunks(response["fileTransfer"]["expectedLength"]),
|
|
34
|
+
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import struct
|
|
3
|
+
import time
|
|
3
4
|
import uuid
|
|
4
5
|
from collections.abc import AsyncGenerator
|
|
5
6
|
from enum import IntEnum
|
|
@@ -8,7 +9,7 @@ from typing import Optional
|
|
|
8
9
|
from pymobiledevice3.exceptions import CoreDeviceError
|
|
9
10
|
from pymobiledevice3.remote.core_device.core_device_service import CoreDeviceService
|
|
10
11
|
from pymobiledevice3.remote.remote_service_discovery import RemoteServiceDiscoveryService
|
|
11
|
-
from pymobiledevice3.remote.xpc_message import XpcUInt64Type
|
|
12
|
+
from pymobiledevice3.remote.xpc_message import XpcInt64Type, XpcUInt64Type
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class Domain(IntEnum):
|
|
@@ -19,10 +20,10 @@ class Domain(IntEnum):
|
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
APPLE_DOMAIN_DICT = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
"appDataContainer": Domain.APP_DATA_CONTAINER,
|
|
24
|
+
"appGroupDataContainer": Domain.APP_GROUP_DATA_CONTAINER,
|
|
25
|
+
"temporary": Domain.TEMPORARY,
|
|
26
|
+
"systemCrashLogs": Domain.SYSTEM_CRASH_LOGS,
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
|
|
@@ -31,41 +32,70 @@ class FileServiceService(CoreDeviceService):
|
|
|
31
32
|
Filesystem control
|
|
32
33
|
"""
|
|
33
34
|
|
|
34
|
-
CTRL_SERVICE_NAME =
|
|
35
|
+
CTRL_SERVICE_NAME = "com.apple.coredevice.fileservice.control"
|
|
35
36
|
|
|
36
|
-
def __init__(self, rsd: RemoteServiceDiscoveryService, domain: Domain) -> None:
|
|
37
|
+
def __init__(self, rsd: RemoteServiceDiscoveryService, domain: Domain, identifier: str = "") -> None:
|
|
37
38
|
super().__init__(rsd, self.CTRL_SERVICE_NAME)
|
|
38
39
|
self.domain: Domain = domain
|
|
39
40
|
self.session: Optional[str] = None
|
|
41
|
+
self.identifier = identifier
|
|
40
42
|
|
|
41
43
|
async def connect(self) -> None:
|
|
42
44
|
await super().connect()
|
|
43
45
|
response = await self.send_receive_request({
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
"Cmd": "CreateSession",
|
|
47
|
+
"Domain": XpcUInt64Type(self.domain),
|
|
48
|
+
"Identifier": self.identifier,
|
|
49
|
+
"Session": "",
|
|
50
|
+
"User": "mobile",
|
|
51
|
+
})
|
|
52
|
+
self.session = response["NewSessionID"]
|
|
47
53
|
|
|
48
|
-
async def retrieve_directory_list(self, path: str =
|
|
49
|
-
return (
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
async def retrieve_directory_list(self, path: str = ".") -> AsyncGenerator[list[str], None]:
|
|
55
|
+
return (
|
|
56
|
+
await self.send_receive_request({
|
|
57
|
+
"Cmd": "RetrieveDirectoryList",
|
|
58
|
+
"MessageUUID": str(uuid.uuid4()),
|
|
59
|
+
"Path": path,
|
|
60
|
+
"SessionID": self.session,
|
|
61
|
+
})
|
|
62
|
+
)["FileList"]
|
|
52
63
|
|
|
53
|
-
async def retrieve_file(self, path: str =
|
|
54
|
-
response = await self.send_receive_request({
|
|
55
|
-
|
|
56
|
-
)
|
|
57
|
-
data_service = self.rsd.get_service_port('com.apple.coredevice.fileservice.data')
|
|
64
|
+
async def retrieve_file(self, path: str = ".") -> bytes:
|
|
65
|
+
response = await self.send_receive_request({"Cmd": "RetrieveFile", "Path": path, "SessionID": self.session})
|
|
66
|
+
data_service = self.rsd.get_service_port("com.apple.coredevice.fileservice.data")
|
|
58
67
|
reader, writer = await asyncio.open_connection(self.service.address[0], data_service)
|
|
59
|
-
writer.write(b
|
|
68
|
+
writer.write(b"rwb!FILE" + struct.pack(">QQQQ", response["Response"], 0, response["NewFileID"], 0))
|
|
60
69
|
await writer.drain()
|
|
61
70
|
await reader.readexactly(0x24)
|
|
62
|
-
return await reader.readexactly(struct.unpack(
|
|
71
|
+
return await reader.readexactly(struct.unpack(">I", await reader.readexactly(4))[0])
|
|
72
|
+
|
|
73
|
+
async def propose_empty_file(
|
|
74
|
+
self,
|
|
75
|
+
path: str = ".",
|
|
76
|
+
file_permissions: int = 0o644,
|
|
77
|
+
uid: int = 501,
|
|
78
|
+
gid: int = 501,
|
|
79
|
+
creation_time: int = time.time(),
|
|
80
|
+
last_modification_time: int = time.time(),
|
|
81
|
+
) -> None:
|
|
82
|
+
"""Request to write an empty file at given path."""
|
|
83
|
+
await self.send_receive_request({
|
|
84
|
+
"Cmd": "ProposeEmptyFile",
|
|
85
|
+
"FileCreationTime": XpcInt64Type(creation_time),
|
|
86
|
+
"FileLastModificationTime": XpcInt64Type(last_modification_time),
|
|
87
|
+
"FilePermissions": XpcInt64Type(file_permissions),
|
|
88
|
+
"FileOwnerUserID": XpcInt64Type(uid),
|
|
89
|
+
"FileOwnerGroupID": XpcInt64Type(gid),
|
|
90
|
+
"Path": path,
|
|
91
|
+
"SessionID": self.session,
|
|
92
|
+
})
|
|
63
93
|
|
|
64
94
|
async def send_receive_request(self, request: dict) -> dict:
|
|
65
95
|
response = await self.service.send_receive_request(request)
|
|
66
|
-
encoded_error = response.get(
|
|
96
|
+
encoded_error = response.get("EncodedError")
|
|
67
97
|
if encoded_error is not None:
|
|
68
|
-
localized_description = response.get(
|
|
98
|
+
localized_description = response.get("LocalizedDescription")
|
|
69
99
|
if localized_description is not None:
|
|
70
100
|
raise CoreDeviceError(localized_description)
|
|
71
101
|
raise CoreDeviceError(encoded_error)
|
|
@@ -2,12 +2,16 @@ import base64
|
|
|
2
2
|
import logging
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from datetime import datetime
|
|
5
|
-
from typing import Optional, Union
|
|
5
|
+
from typing import Any, Optional, Union
|
|
6
6
|
|
|
7
7
|
from pymobiledevice3.bonjour import DEFAULT_BONJOUR_TIMEOUT, browse_remoted
|
|
8
8
|
from pymobiledevice3.common import get_home_folder
|
|
9
|
-
from pymobiledevice3.exceptions import
|
|
10
|
-
|
|
9
|
+
from pymobiledevice3.exceptions import (
|
|
10
|
+
InvalidServiceError,
|
|
11
|
+
NoDeviceConnectedError,
|
|
12
|
+
PyMobileDevice3Exception,
|
|
13
|
+
StartServiceError,
|
|
14
|
+
)
|
|
11
15
|
from pymobiledevice3.lockdown import LockdownClient, create_using_remote
|
|
12
16
|
from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
|
|
13
17
|
from pymobiledevice3.pair_records import get_local_pairing_record, get_remote_pairing_record_filename
|
|
@@ -38,11 +42,15 @@ class RemoteServiceDiscoveryService(LockdownServiceProvider):
|
|
|
38
42
|
|
|
39
43
|
@property
|
|
40
44
|
def product_version(self) -> str:
|
|
41
|
-
return self.peer_info[
|
|
45
|
+
return self.peer_info["Properties"]["OSVersion"]
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def product_build_version(self) -> str:
|
|
49
|
+
return self.peer_info["Properties"]["BuildVersion"]
|
|
42
50
|
|
|
43
51
|
@property
|
|
44
52
|
def ecid(self) -> int:
|
|
45
|
-
return self.peer_info[
|
|
53
|
+
return self.peer_info["Properties"]["UniqueChipID"]
|
|
46
54
|
|
|
47
55
|
@property
|
|
48
56
|
def developer_mode_status(self) -> bool:
|
|
@@ -57,17 +65,24 @@ class RemoteServiceDiscoveryService(LockdownServiceProvider):
|
|
|
57
65
|
|
|
58
66
|
async def connect(self) -> None:
|
|
59
67
|
await self.service.connect()
|
|
60
|
-
self.peer_info = await self.service.receive_response()
|
|
61
|
-
self.udid = self.peer_info['Properties']['UniqueDeviceID']
|
|
62
|
-
self.product_type = self.peer_info['Properties']['ProductType']
|
|
63
68
|
try:
|
|
64
|
-
self.
|
|
65
|
-
|
|
66
|
-
self.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
self.peer_info = await self.service.receive_response()
|
|
70
|
+
self.udid = self.peer_info["Properties"]["UniqueDeviceID"]
|
|
71
|
+
self.product_type = self.peer_info["Properties"]["ProductType"]
|
|
72
|
+
try:
|
|
73
|
+
self.lockdown = create_using_remote(
|
|
74
|
+
self.start_lockdown_service("com.apple.mobile.lockdown.remote.trusted")
|
|
75
|
+
)
|
|
76
|
+
except InvalidServiceError:
|
|
77
|
+
self.lockdown = create_using_remote(
|
|
78
|
+
self.start_lockdown_service("com.apple.mobile.lockdown.remote.untrusted")
|
|
79
|
+
)
|
|
80
|
+
self.all_values = self.lockdown.all_values
|
|
81
|
+
except Exception:
|
|
82
|
+
await self.close()
|
|
83
|
+
raise
|
|
69
84
|
|
|
70
|
-
def get_value(self, domain: str = None, key: str = None):
|
|
85
|
+
def get_value(self, domain: Optional[str] = None, key: Optional[str] = None) -> Any:
|
|
71
86
|
return self.lockdown.get_value(domain, key)
|
|
72
87
|
|
|
73
88
|
def start_lockdown_service_without_checkin(self, name: str) -> ServiceConnection:
|
|
@@ -75,20 +90,30 @@ class RemoteServiceDiscoveryService(LockdownServiceProvider):
|
|
|
75
90
|
|
|
76
91
|
def start_lockdown_service(self, name: str, include_escrow_bag: bool = False) -> ServiceConnection:
|
|
77
92
|
service = self.start_lockdown_service_without_checkin(name)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
93
|
+
try:
|
|
94
|
+
checkin = {"Label": "pymobiledevice3", "ProtocolVersion": "2", "Request": "RSDCheckin"}
|
|
95
|
+
if include_escrow_bag:
|
|
96
|
+
pairing_record = get_local_pairing_record(
|
|
97
|
+
get_remote_pairing_record_filename(self.udid), get_home_folder()
|
|
98
|
+
)
|
|
99
|
+
checkin["EscrowBag"] = base64.b64decode(pairing_record["remote_unlock_host_key"])
|
|
100
|
+
response = service.send_recv_plist(checkin)
|
|
101
|
+
if response["Request"] != "RSDCheckin":
|
|
102
|
+
raise PyMobileDevice3Exception(f'Invalid response for RSDCheckIn: {response}. Expected "RSDCheckIn"')
|
|
103
|
+
response = service.recv_plist()
|
|
104
|
+
if response["Request"] != "StartService":
|
|
105
|
+
raise PyMobileDevice3Exception(
|
|
106
|
+
f'Invalid response for RSDCheckIn: {response}. Expected "ServiceService"'
|
|
107
|
+
)
|
|
108
|
+
error = response.get("Error")
|
|
109
|
+
if error is not None:
|
|
110
|
+
raise StartServiceError(name, error)
|
|
111
|
+
except Exception:
|
|
112
|
+
service.close()
|
|
113
|
+
raise
|
|
88
114
|
return service
|
|
89
115
|
|
|
90
|
-
async def aio_start_lockdown_service(
|
|
91
|
-
self, name: str, include_escrow_bag: bool = False) -> ServiceConnection:
|
|
116
|
+
async def aio_start_lockdown_service(self, name: str, include_escrow_bag: bool = False) -> ServiceConnection:
|
|
92
117
|
service = self.start_lockdown_service(name, include_escrow_bag=include_escrow_bag)
|
|
93
118
|
await service.aio_start()
|
|
94
119
|
return service
|
|
@@ -97,9 +122,9 @@ class RemoteServiceDiscoveryService(LockdownServiceProvider):
|
|
|
97
122
|
try:
|
|
98
123
|
return self.start_lockdown_service_without_checkin(name)
|
|
99
124
|
except StartServiceError:
|
|
100
|
-
logging.getLogger(self.__module__).
|
|
101
|
-
|
|
102
|
-
|
|
125
|
+
logging.getLogger(self.__module__).exception(
|
|
126
|
+
"Failed to connect to required service. Make sure DeveloperDiskImage.dmg has been mounted. "
|
|
127
|
+
"You can do so using: pymobiledevice3 mounter mount"
|
|
103
128
|
)
|
|
104
129
|
raise
|
|
105
130
|
|
|
@@ -108,24 +133,24 @@ class RemoteServiceDiscoveryService(LockdownServiceProvider):
|
|
|
108
133
|
return service
|
|
109
134
|
|
|
110
135
|
def start_service(self, name: str) -> Union[RemoteXPCConnection, ServiceConnection]:
|
|
111
|
-
service = self.peer_info[
|
|
112
|
-
service_properties = service.get(
|
|
113
|
-
use_remote_xpc = service_properties.get(
|
|
136
|
+
service = self.peer_info["Services"][name]
|
|
137
|
+
service_properties = service.get("Properties", {})
|
|
138
|
+
use_remote_xpc = service_properties.get("UsesRemoteXPC", False)
|
|
114
139
|
return self.start_remote_service(name) if use_remote_xpc else self.start_lockdown_service(name)
|
|
115
140
|
|
|
116
141
|
def get_service_port(self, name: str) -> int:
|
|
117
142
|
"""takes a service name and returns the port that service is running on if the service exists"""
|
|
118
|
-
service = self.peer_info[
|
|
143
|
+
service = self.peer_info["Services"].get(name)
|
|
119
144
|
if service is None:
|
|
120
|
-
raise InvalidServiceError(f
|
|
121
|
-
return int(service[
|
|
145
|
+
raise InvalidServiceError(f"No such service: {name}")
|
|
146
|
+
return int(service["Port"])
|
|
122
147
|
|
|
123
148
|
async def close(self) -> None:
|
|
124
149
|
if self.lockdown is not None:
|
|
125
150
|
self.lockdown.close()
|
|
126
151
|
await self.service.close()
|
|
127
152
|
|
|
128
|
-
async def __aenter__(self) ->
|
|
153
|
+
async def __aenter__(self) -> "RemoteServiceDiscoveryService":
|
|
129
154
|
await self.connect()
|
|
130
155
|
return self
|
|
131
156
|
|
|
@@ -133,20 +158,29 @@ class RemoteServiceDiscoveryService(LockdownServiceProvider):
|
|
|
133
158
|
await self.close()
|
|
134
159
|
|
|
135
160
|
def __repr__(self) -> str:
|
|
136
|
-
name_str =
|
|
161
|
+
name_str = ""
|
|
137
162
|
if self.name:
|
|
138
|
-
name_str = f
|
|
139
|
-
return (
|
|
140
|
-
|
|
163
|
+
name_str = f" NAME:{self.name}"
|
|
164
|
+
return (
|
|
165
|
+
f"<{self.__class__.__name__} PRODUCT:{self.product_type} VERSION:{self.product_version} "
|
|
166
|
+
f"UDID:{self.udid}{name_str}>"
|
|
167
|
+
)
|
|
141
168
|
|
|
142
169
|
|
|
143
170
|
async def get_remoted_devices(timeout: float = DEFAULT_BONJOUR_TIMEOUT) -> list[RSDDevice]:
|
|
144
171
|
result = []
|
|
145
|
-
for
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
172
|
+
for instance in await browse_remoted(timeout):
|
|
173
|
+
for address in instance.addresses:
|
|
174
|
+
with RemoteServiceDiscoveryService((address.full_ip, RSD_PORT)) as rsd:
|
|
175
|
+
properties = rsd.peer_info["Properties"]
|
|
176
|
+
result.append(
|
|
177
|
+
RSDDevice(
|
|
178
|
+
hostname=address.full_ip,
|
|
179
|
+
udid=properties["UniqueDeviceID"],
|
|
180
|
+
product_type=properties["ProductType"],
|
|
181
|
+
os_version=properties["OSVersion"],
|
|
182
|
+
)
|
|
183
|
+
)
|
|
150
184
|
return result
|
|
151
185
|
|
|
152
186
|
|