pymobiledevice3 5.0.1__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 +442 -421
- 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.1.dist-info → pymobiledevice3-5.0.2.dist-info}/METADATA +1 -1
- pymobiledevice3-5.0.2.dist-info/RECORD +173 -0
- pymobiledevice3-5.0.1.dist-info/RECORD +0 -173
- {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.2.dist-info}/WHEEL +0 -0
- {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.2.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.2.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.2.dist-info}/top_level.txt +0 -0
pymobiledevice3/services/amfi.py
CHANGED
|
@@ -1,30 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
import logging
|
|
3
3
|
|
|
4
|
-
from pymobiledevice3.exceptions import
|
|
5
|
-
|
|
4
|
+
from pymobiledevice3.exceptions import (
|
|
5
|
+
AmfiError,
|
|
6
|
+
DeveloperModeError,
|
|
7
|
+
DeviceHasPasscodeSetError,
|
|
8
|
+
PyMobileDevice3Exception,
|
|
9
|
+
)
|
|
6
10
|
from pymobiledevice3.lockdown import LockdownClient, retry_create_using_usbmux
|
|
7
11
|
from pymobiledevice3.services.heartbeat import HeartbeatService
|
|
8
12
|
|
|
9
13
|
|
|
10
14
|
class AmfiService:
|
|
11
|
-
|
|
12
15
|
DEVELOPER_MODE_REVEAL = 0
|
|
13
16
|
DEVELOPER_MODE_ENABLE = 1
|
|
14
17
|
DEVELOPER_MODE_ACCEPT = 2
|
|
15
18
|
|
|
16
|
-
SERVICE_NAME =
|
|
19
|
+
SERVICE_NAME = "com.apple.amfi.lockdown"
|
|
17
20
|
|
|
18
21
|
def __init__(self, lockdown: LockdownClient):
|
|
19
22
|
self._lockdown = lockdown
|
|
20
23
|
self._logger = logging.getLogger(self.__module__)
|
|
21
24
|
|
|
22
25
|
def reveal_developer_mode_option_in_ui(self):
|
|
23
|
-
"""
|
|
26
|
+
"""create an empty file at AMFIShowOverridePath"""
|
|
24
27
|
service = self._lockdown.start_lockdown_service(self.SERVICE_NAME)
|
|
25
|
-
resp = service.send_recv_plist({
|
|
26
|
-
if not resp.get(
|
|
27
|
-
raise PyMobileDevice3Exception(f
|
|
28
|
+
resp = service.send_recv_plist({"action": self.DEVELOPER_MODE_REVEAL})
|
|
29
|
+
if not resp.get("success"):
|
|
30
|
+
raise PyMobileDevice3Exception(f"create_AMFIShowOverridePath() failed with: {resp}")
|
|
28
31
|
|
|
29
32
|
def enable_developer_mode(self, enable_post_restart=True):
|
|
30
33
|
"""
|
|
@@ -33,16 +36,16 @@ class AmfiService:
|
|
|
33
36
|
with "yes"
|
|
34
37
|
"""
|
|
35
38
|
service = self._lockdown.start_lockdown_service(self.SERVICE_NAME)
|
|
36
|
-
resp = service.send_recv_plist({
|
|
37
|
-
error = resp.get(
|
|
39
|
+
resp = service.send_recv_plist({"action": self.DEVELOPER_MODE_ENABLE})
|
|
40
|
+
error = resp.get("Error")
|
|
38
41
|
|
|
39
42
|
if error is not None:
|
|
40
|
-
if error ==
|
|
43
|
+
if error == "Device has a passcode set":
|
|
41
44
|
raise DeviceHasPasscodeSetError()
|
|
42
45
|
raise AmfiError(error)
|
|
43
46
|
|
|
44
|
-
if not resp.get(
|
|
45
|
-
raise DeveloperModeError(f
|
|
47
|
+
if not resp.get("success"):
|
|
48
|
+
raise DeveloperModeError(f"enable_developer_mode(): {resp}")
|
|
46
49
|
|
|
47
50
|
if not enable_post_restart:
|
|
48
51
|
return
|
|
@@ -50,14 +53,14 @@ class AmfiService:
|
|
|
50
53
|
try:
|
|
51
54
|
HeartbeatService(self._lockdown).start()
|
|
52
55
|
except (ConnectionAbortedError, BrokenPipeError):
|
|
53
|
-
self._logger.debug(
|
|
56
|
+
self._logger.debug("device disconnected, awaiting reconnect")
|
|
54
57
|
|
|
55
58
|
self._lockdown = retry_create_using_usbmux(None, serial=self._lockdown.udid)
|
|
56
59
|
self.enable_developer_mode_post_restart()
|
|
57
60
|
|
|
58
61
|
def enable_developer_mode_post_restart(self):
|
|
59
|
-
"""
|
|
62
|
+
"""answer the prompt that appears after the restart with "yes" """
|
|
60
63
|
service = self._lockdown.start_lockdown_service(self.SERVICE_NAME)
|
|
61
|
-
resp = service.send_recv_plist({
|
|
62
|
-
if not resp.get(
|
|
63
|
-
raise DeveloperModeError(f
|
|
64
|
+
resp = service.send_recv_plist({"action": self.DEVELOPER_MODE_ACCEPT})
|
|
65
|
+
if not resp.get("success"):
|
|
66
|
+
raise DeveloperModeError(f"enable_developer_mode_post_restart() failed: {resp}")
|
|
@@ -8,8 +8,8 @@ from pymobiledevice3.services.lockdown_service import LockdownService
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class CompanionProxyService(LockdownService):
|
|
11
|
-
SERVICE_NAME =
|
|
12
|
-
RSD_SERVICE_NAME =
|
|
11
|
+
SERVICE_NAME = "com.apple.companion_proxy"
|
|
12
|
+
RSD_SERVICE_NAME = "com.apple.companion_proxy.shim.remote"
|
|
13
13
|
|
|
14
14
|
def __init__(self, lockdown: LockdownServiceProvider):
|
|
15
15
|
if isinstance(lockdown, LockdownClient):
|
|
@@ -19,48 +19,52 @@ class CompanionProxyService(LockdownService):
|
|
|
19
19
|
|
|
20
20
|
def list(self):
|
|
21
21
|
service = self.lockdown.start_lockdown_service(self.service_name)
|
|
22
|
-
return service.send_recv_plist({
|
|
22
|
+
return service.send_recv_plist({"Command": "GetDeviceRegistry"}).get("PairedDevicesArray", [])
|
|
23
23
|
|
|
24
24
|
def listen_for_devices(self):
|
|
25
25
|
service = self.lockdown.start_lockdown_service(self.service_name)
|
|
26
|
-
service.send_plist({
|
|
26
|
+
service.send_plist({"Command": "StartListeningForDevices"})
|
|
27
27
|
while True:
|
|
28
28
|
yield service.recv_plist()
|
|
29
29
|
|
|
30
30
|
def get_value(self, udid: str, key: str):
|
|
31
31
|
service = self.lockdown.start_lockdown_service(self.service_name)
|
|
32
|
-
response = service.send_recv_plist({
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
response = service.send_recv_plist({
|
|
33
|
+
"Command": "GetValueFromRegistry",
|
|
34
|
+
"GetValueGizmoUDIDKey": udid,
|
|
35
|
+
"GetValueKeyKey": key,
|
|
36
|
+
})
|
|
35
37
|
|
|
36
|
-
value = response.get(
|
|
38
|
+
value = response.get("RetrievedValueDictionary")
|
|
37
39
|
if value is not None:
|
|
38
40
|
return value
|
|
39
41
|
|
|
40
|
-
error = response.get(
|
|
42
|
+
error = response.get("Error")
|
|
41
43
|
raise PyMobileDevice3Exception(error)
|
|
42
44
|
|
|
43
|
-
def start_forwarding_service_port(
|
|
44
|
-
|
|
45
|
+
def start_forwarding_service_port(
|
|
46
|
+
self, remote_port: int, service_name: Optional[str] = None, options: Optional[dict] = None
|
|
47
|
+
):
|
|
45
48
|
service = self.lockdown.start_lockdown_service(self.service_name)
|
|
46
49
|
|
|
47
|
-
request = {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
request = {
|
|
51
|
+
"Command": "StartForwardingServicePort",
|
|
52
|
+
"GizmoRemotePortNumber": remote_port,
|
|
53
|
+
"IsServiceLowPriority": False,
|
|
54
|
+
"PreferWifi": False,
|
|
55
|
+
}
|
|
51
56
|
|
|
52
57
|
if service_name is not None:
|
|
53
|
-
request[
|
|
58
|
+
request["ForwardedServiceName"] = service_name
|
|
54
59
|
|
|
55
60
|
if options is not None:
|
|
56
61
|
request.update(options)
|
|
57
62
|
|
|
58
|
-
return service.send_recv_plist(request).get(
|
|
63
|
+
return service.send_recv_plist(request).get("CompanionProxyServicePort")
|
|
59
64
|
|
|
60
65
|
def stop_forwarding_service_port(self, remote_port: int):
|
|
61
66
|
service = self.lockdown.start_lockdown_service(self.service_name)
|
|
62
67
|
|
|
63
|
-
request = {
|
|
64
|
-
'GizmoRemotePortNumber': remote_port}
|
|
68
|
+
request = {"Command": "StopForwardingServicePort", "GizmoRemotePortNumber": remote_port}
|
|
65
69
|
|
|
66
70
|
return service.send_recv_plist(request)
|
|
@@ -4,22 +4,26 @@ import re
|
|
|
4
4
|
import time
|
|
5
5
|
from collections.abc import Generator
|
|
6
6
|
from json import JSONDecodeError
|
|
7
|
-
from typing import Callable, Optional
|
|
7
|
+
from typing import Callable, ClassVar, Optional
|
|
8
8
|
|
|
9
9
|
from pycrashreport.crash_report import get_crash_report_from_buf
|
|
10
10
|
from xonsh.built_ins import XSH
|
|
11
11
|
from xonsh.cli_utils import Annotated, Arg
|
|
12
12
|
|
|
13
|
-
from pymobiledevice3.exceptions import
|
|
14
|
-
|
|
13
|
+
from pymobiledevice3.exceptions import (
|
|
14
|
+
AfcException,
|
|
15
|
+
AfcFileNotFoundError,
|
|
16
|
+
NotificationTimeoutError,
|
|
17
|
+
SysdiagnoseTimeoutError,
|
|
18
|
+
)
|
|
15
19
|
from pymobiledevice3.lockdown import LockdownClient
|
|
16
20
|
from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
|
|
17
21
|
from pymobiledevice3.services.afc import AfcService, AfcShell, path_completer
|
|
18
22
|
from pymobiledevice3.services.notification_proxy import NotificationProxyService
|
|
19
23
|
from pymobiledevice3.services.os_trace import OsTraceService
|
|
20
24
|
|
|
21
|
-
SYSDIAGNOSE_PROCESS_NAMES = (
|
|
22
|
-
SYSDIAGNOSE_DIR =
|
|
25
|
+
SYSDIAGNOSE_PROCESS_NAMES = ("sysdiagnose", "sysdiagnosed")
|
|
26
|
+
SYSDIAGNOSE_DIR = "DiagnosticLogs/sysdiagnose"
|
|
23
27
|
SYSDIAGNOSE_IN_PROGRESS_MAX_TTL_SECS = 600
|
|
24
28
|
|
|
25
29
|
# on iOS17, we need to wait for a moment before trying to fetch the sysdiagnose archive
|
|
@@ -27,14 +31,14 @@ IOS17_SYSDIAGNOSE_DELAY = 3
|
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
class CrashReportsManager:
|
|
30
|
-
COPY_MOBILE_NAME =
|
|
31
|
-
RSD_COPY_MOBILE_NAME =
|
|
34
|
+
COPY_MOBILE_NAME = "com.apple.crashreportcopymobile"
|
|
35
|
+
RSD_COPY_MOBILE_NAME = "com.apple.crashreportcopymobile.shim.remote"
|
|
32
36
|
|
|
33
|
-
CRASH_MOVER_NAME =
|
|
34
|
-
RSD_CRASH_MOVER_NAME =
|
|
37
|
+
CRASH_MOVER_NAME = "com.apple.crashreportmover"
|
|
38
|
+
RSD_CRASH_MOVER_NAME = "com.apple.crashreportmover.shim.remote"
|
|
35
39
|
|
|
36
|
-
APPSTORED_PATH =
|
|
37
|
-
IN_PROGRESS_SYSDIAGNOSE_EXTENSIONS = [
|
|
40
|
+
APPSTORED_PATH = "/com.apple.appstored"
|
|
41
|
+
IN_PROGRESS_SYSDIAGNOSE_EXTENSIONS: ClassVar = [".tmp", ".tar.gz"]
|
|
38
42
|
|
|
39
43
|
def __init__(self, lockdown: LockdownServiceProvider):
|
|
40
44
|
self.logger = logging.getLogger(__name__)
|
|
@@ -63,16 +67,16 @@ class CrashReportsManager:
|
|
|
63
67
|
Clear all crash reports.
|
|
64
68
|
"""
|
|
65
69
|
undeleted_items = []
|
|
66
|
-
for filename in self.ls(
|
|
70
|
+
for filename in self.ls("/"):
|
|
67
71
|
undeleted_items.extend(self.afc.rm(filename, force=True))
|
|
68
72
|
|
|
69
73
|
for item in undeleted_items:
|
|
70
74
|
# special case of file that sometimes created automatically right after delete,
|
|
71
75
|
# and then we can't delete the folder because it's not empty
|
|
72
76
|
if item != self.APPSTORED_PATH:
|
|
73
|
-
raise AfcException(f
|
|
77
|
+
raise AfcException(f"failed to clear crash reports directory, undeleted items: {undeleted_items}", None)
|
|
74
78
|
|
|
75
|
-
def ls(self, path: str =
|
|
79
|
+
def ls(self, path: str = "/", depth: int = 1) -> list[str]:
|
|
76
80
|
"""
|
|
77
81
|
List file and folder in the crash report's directory.
|
|
78
82
|
:param path: Path to list, relative to the crash report's directory.
|
|
@@ -81,8 +85,9 @@ class CrashReportsManager:
|
|
|
81
85
|
"""
|
|
82
86
|
return list(self.afc.dirlist(path, depth))[1:] # skip the root path '/'
|
|
83
87
|
|
|
84
|
-
def pull(
|
|
85
|
-
|
|
88
|
+
def pull(
|
|
89
|
+
self, out: str, entry: str = "/", erase: bool = False, match: Optional[str] = None, progress_bar: bool = True
|
|
90
|
+
) -> None:
|
|
86
91
|
"""
|
|
87
92
|
Pull crash reports from the device.
|
|
88
93
|
:param out: Directory to pull crash reports to.
|
|
@@ -93,20 +98,19 @@ class CrashReportsManager:
|
|
|
93
98
|
"""
|
|
94
99
|
|
|
95
100
|
def log(src: str, dst: str) -> None:
|
|
96
|
-
self.logger.info(f
|
|
97
|
-
if erase:
|
|
98
|
-
|
|
99
|
-
self.afc.rm_single(src, force=True)
|
|
101
|
+
self.logger.info(f"{src} --> {dst}")
|
|
102
|
+
if erase and not self.afc.isdir(src):
|
|
103
|
+
self.afc.rm_single(src, force=True)
|
|
100
104
|
|
|
101
105
|
match = None if match is None else re.compile(match)
|
|
102
106
|
self.afc.pull(entry, out, match, callback=log, progress_bar=progress_bar, ignore_errors=True)
|
|
103
107
|
|
|
104
108
|
def flush(self) -> None:
|
|
105
|
-
"""
|
|
106
|
-
ack = b
|
|
109
|
+
"""Trigger com.apple.crashreportmover to flush all products into CrashReports directory"""
|
|
110
|
+
ack = b"ping\x00"
|
|
107
111
|
assert ack == self.lockdown.start_lockdown_service(self.crash_mover_service_name).recvall(len(ack))
|
|
108
112
|
|
|
109
|
-
def watch(self, name: str = None, raw: bool = False) -> Generator[str, None, None]:
|
|
113
|
+
def watch(self, name: Optional[str] = None, raw: bool = False) -> Generator[str, None, None]:
|
|
110
114
|
"""
|
|
111
115
|
Monitor creation of new crash reports for a given process name.
|
|
112
116
|
|
|
@@ -114,16 +118,18 @@ class CrashReportsManager:
|
|
|
114
118
|
representation for the crash.
|
|
115
119
|
"""
|
|
116
120
|
for syslog_entry in OsTraceService(lockdown=self.lockdown).syslog():
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
if (
|
|
122
|
+
(posixpath.basename(syslog_entry.filename) != "osanalyticshelper")
|
|
123
|
+
or (posixpath.basename(syslog_entry.image_name) != "OSAnalytics")
|
|
124
|
+
or not syslog_entry.message.startswith("Saved type ")
|
|
125
|
+
):
|
|
120
126
|
# skip non-ips creation syslog lines
|
|
121
127
|
continue
|
|
122
128
|
|
|
123
129
|
filename = posixpath.basename(syslog_entry.message.split()[-1])
|
|
124
|
-
self.logger.debug(f
|
|
130
|
+
self.logger.debug(f"crash report: {filename}")
|
|
125
131
|
|
|
126
|
-
if posixpath.splitext(filename)[-1] not in (
|
|
132
|
+
if posixpath.splitext(filename)[-1] not in (".ips", ".panic"):
|
|
127
133
|
continue
|
|
128
134
|
|
|
129
135
|
while True:
|
|
@@ -141,8 +147,14 @@ class CrashReportsManager:
|
|
|
141
147
|
else:
|
|
142
148
|
yield crash_report
|
|
143
149
|
|
|
144
|
-
def get_new_sysdiagnose(
|
|
145
|
-
|
|
150
|
+
def get_new_sysdiagnose(
|
|
151
|
+
self,
|
|
152
|
+
out: str,
|
|
153
|
+
erase: bool = True,
|
|
154
|
+
*,
|
|
155
|
+
timeout: Optional[float] = None,
|
|
156
|
+
callback: Optional[Callable[[float], None]] = None,
|
|
157
|
+
) -> None:
|
|
146
158
|
"""
|
|
147
159
|
Monitor the creation of a newly created sysdiagnose archive and pull it
|
|
148
160
|
:param out: filename
|
|
@@ -160,7 +172,7 @@ class CrashReportsManager:
|
|
|
160
172
|
if callback is not None:
|
|
161
173
|
callback(time.monotonic() - start_time)
|
|
162
174
|
|
|
163
|
-
self.logger.info(
|
|
175
|
+
self.logger.info("sysdiagnose tarball creation has been started")
|
|
164
176
|
self._wait_for_sysdiagnose_to_finish(timeout)
|
|
165
177
|
|
|
166
178
|
if callback is not None:
|
|
@@ -173,17 +185,17 @@ class CrashReportsManager:
|
|
|
173
185
|
|
|
174
186
|
def _wait_for_sysdiagnose_to_finish(self, end_time: Optional[float] = None) -> None:
|
|
175
187
|
with NotificationProxyService(self.lockdown, timeout=end_time) as service:
|
|
176
|
-
stop_notification =
|
|
188
|
+
stop_notification = "com.apple.sysdiagnose.sysdiagnoseStopped"
|
|
177
189
|
service.notify_register_dispatch(stop_notification)
|
|
178
190
|
try:
|
|
179
191
|
for event in service.receive_notification():
|
|
180
|
-
if event[
|
|
192
|
+
if event["Name"] != stop_notification:
|
|
181
193
|
continue
|
|
182
|
-
self.logger.debug(f
|
|
194
|
+
self.logger.debug(f"Received {event}")
|
|
183
195
|
time.sleep(IOS17_SYSDIAGNOSE_DELAY)
|
|
184
196
|
break
|
|
185
197
|
except NotificationTimeoutError as e:
|
|
186
|
-
raise SysdiagnoseTimeoutError(
|
|
198
|
+
raise SysdiagnoseTimeoutError("Timeout waiting for sysdiagnose completion") from e
|
|
187
199
|
|
|
188
200
|
def _get_new_sysdiagnose_filename(self, end_time: Optional[float] = None) -> str:
|
|
189
201
|
sysdiagnose_filename = None
|
|
@@ -193,17 +205,19 @@ class CrashReportsManager:
|
|
|
193
205
|
try:
|
|
194
206
|
for filename in self.afc.listdir(SYSDIAGNOSE_DIR):
|
|
195
207
|
# search for an IN_PROGRESS archive
|
|
196
|
-
if filename not in excluded_temp_files and
|
|
208
|
+
if filename not in excluded_temp_files and "IN_PROGRESS_" in filename:
|
|
197
209
|
for ext in self.IN_PROGRESS_SYSDIAGNOSE_EXTENSIONS:
|
|
198
210
|
if filename.endswith(ext):
|
|
199
|
-
delta =
|
|
200
|
-
|
|
211
|
+
delta = (
|
|
212
|
+
self.lockdown.date
|
|
213
|
+
- self.afc.stat(posixpath.join(SYSDIAGNOSE_DIR, filename))["st_mtime"]
|
|
214
|
+
)
|
|
201
215
|
# Ignores IN_PROGRESS sysdiagnose files older than the defined time to live
|
|
202
216
|
if delta.total_seconds() < SYSDIAGNOSE_IN_PROGRESS_MAX_TTL_SECS:
|
|
203
|
-
self.logger.debug(f
|
|
217
|
+
self.logger.debug(f"Detected in progress sysdiagnose {filename}")
|
|
204
218
|
sysdiagnose_filename = filename.rsplit(ext)[0]
|
|
205
|
-
sysdiagnose_filename = sysdiagnose_filename.replace(
|
|
206
|
-
sysdiagnose_filename = f
|
|
219
|
+
sysdiagnose_filename = sysdiagnose_filename.replace("IN_PROGRESS_", "")
|
|
220
|
+
sysdiagnose_filename = f"{sysdiagnose_filename}.tar.gz"
|
|
207
221
|
return posixpath.join(SYSDIAGNOSE_DIR, sysdiagnose_filename)
|
|
208
222
|
else:
|
|
209
223
|
self.logger.warning(f"Old sysdiagnose temp file ignored {filename}")
|
|
@@ -212,7 +226,7 @@ class CrashReportsManager:
|
|
|
212
226
|
pass
|
|
213
227
|
|
|
214
228
|
if self._check_timeout(end_time):
|
|
215
|
-
raise SysdiagnoseTimeoutError(
|
|
229
|
+
raise SysdiagnoseTimeoutError("Timeout finding in-progress sysdiagnose filename")
|
|
216
230
|
|
|
217
231
|
def _check_timeout(self, end_time: Optional[float] = None) -> bool:
|
|
218
232
|
return end_time is not None and time.monotonic() > end_time
|
|
@@ -222,16 +236,16 @@ class CrashReportsShell(AfcShell):
|
|
|
222
236
|
@classmethod
|
|
223
237
|
def create(cls, service_provider: LockdownServiceProvider, **kwargs):
|
|
224
238
|
manager = CrashReportsManager(service_provider)
|
|
225
|
-
XSH.ctx[
|
|
239
|
+
XSH.ctx["_manager"] = manager
|
|
226
240
|
super(CrashReportsShell, CrashReportsShell).create(service_provider, service=manager.afc)
|
|
227
241
|
|
|
228
242
|
def _setup_shell_commands(self):
|
|
229
243
|
super()._setup_shell_commands()
|
|
230
|
-
self._register_arg_parse_alias(
|
|
231
|
-
self._register_arg_parse_alias(
|
|
244
|
+
self._register_arg_parse_alias("parse", self._do_parse)
|
|
245
|
+
self._register_arg_parse_alias("clear", self._do_clear)
|
|
232
246
|
|
|
233
247
|
def _do_parse(self, filename: Annotated[str, Arg(completer=path_completer)]) -> None:
|
|
234
248
|
print(get_crash_report_from_buf(self.afc.get_file_contents(filename).decode(), filename=filename))
|
|
235
249
|
|
|
236
250
|
def _do_clear(self) -> None:
|
|
237
|
-
XSH.ctx[
|
|
251
|
+
XSH.ctx["_manager"].clear()
|
|
@@ -7,14 +7,14 @@ CHUNK_SIZE = 200
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class DebugServerAppList(LockdownService):
|
|
10
|
-
SERVICE_NAME =
|
|
10
|
+
SERVICE_NAME = "com.apple.debugserver.DVTSecureSocketProxy.applist"
|
|
11
11
|
|
|
12
12
|
def __init__(self, lockdown: LockdownClient):
|
|
13
13
|
super().__init__(lockdown, self.SERVICE_NAME)
|
|
14
14
|
|
|
15
15
|
def get(self) -> dict:
|
|
16
|
-
buf = b
|
|
17
|
-
while b
|
|
16
|
+
buf = b""
|
|
17
|
+
while b"</plist>" not in buf:
|
|
18
18
|
buf += self.service.recv(CHUNK_SIZE)
|
|
19
19
|
|
|
20
20
|
return plistlib.loads(buf)
|
|
@@ -4,25 +4,25 @@ from pymobiledevice3.services.lockdown_service import LockdownService
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class DtDeviceArbitration(LockdownService):
|
|
7
|
-
SERVICE_NAME =
|
|
7
|
+
SERVICE_NAME = "com.apple.dt.devicearbitration"
|
|
8
8
|
|
|
9
9
|
def __init__(self, lockdown: LockdownClient):
|
|
10
10
|
super().__init__(lockdown, self.SERVICE_NAME, is_developer_service=True)
|
|
11
11
|
|
|
12
12
|
@property
|
|
13
13
|
def version(self) -> dict:
|
|
14
|
-
return self.service.send_recv_plist({
|
|
14
|
+
return self.service.send_recv_plist({"command": "version"})
|
|
15
15
|
|
|
16
16
|
def check_in(self, hostname: str, force: bool = False):
|
|
17
|
-
request = {
|
|
17
|
+
request = {"command": "check-in", "hostname": hostname}
|
|
18
18
|
if force:
|
|
19
|
-
request[
|
|
19
|
+
request["command"] = "force-check-in"
|
|
20
20
|
response = self.service.send_recv_plist(request)
|
|
21
|
-
if response.get(
|
|
21
|
+
if response.get("result") != "success":
|
|
22
22
|
raise DeviceAlreadyInUseError(response)
|
|
23
23
|
|
|
24
24
|
def check_out(self):
|
|
25
|
-
request = {
|
|
25
|
+
request = {"command": "check-out"}
|
|
26
26
|
response = self.service.send_recv_plist(request)
|
|
27
|
-
if response.get(
|
|
28
|
-
raise ArbitrationError(f
|
|
27
|
+
if response.get("result") != "success":
|
|
28
|
+
raise ArbitrationError(f"failed with: {response}")
|