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
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import posixpath
|
|
5
|
+
import shlex
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from enum import IntEnum
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Annotated, NamedTuple, Optional
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
from click.exceptions import BadParameter, MissingParameter, UsageError
|
|
13
|
+
from typer_injector import InjectingTyper
|
|
14
|
+
|
|
15
|
+
from pymobiledevice3.cli.cli_common import ServiceProviderDep, print_json, user_requested_colored_output
|
|
16
|
+
from pymobiledevice3.cli.developer.dvt import core_profile_session, simulate_location, sysmon
|
|
17
|
+
from pymobiledevice3.exceptions import DvtDirListError, UnrecognizedSelectorError
|
|
18
|
+
from pymobiledevice3.services.dvt.dvt_secure_socket_proxy import DvtSecureSocketProxyService
|
|
19
|
+
from pymobiledevice3.services.dvt.instruments.activity_trace_tap import ActivityTraceTap, decode_message_format
|
|
20
|
+
from pymobiledevice3.services.dvt.instruments.application_listing import ApplicationListing
|
|
21
|
+
from pymobiledevice3.services.dvt.instruments.device_info import DeviceInfo
|
|
22
|
+
from pymobiledevice3.services.dvt.instruments.energy_monitor import EnergyMonitor
|
|
23
|
+
from pymobiledevice3.services.dvt.instruments.graphics import Graphics
|
|
24
|
+
from pymobiledevice3.services.dvt.instruments.network_monitor import ConnectionDetectionEvent, NetworkMonitor
|
|
25
|
+
from pymobiledevice3.services.dvt.instruments.notifications import Notifications
|
|
26
|
+
from pymobiledevice3.services.dvt.instruments.process_control import ProcessControl
|
|
27
|
+
from pymobiledevice3.services.dvt.instruments.screenshot import Screenshot
|
|
28
|
+
from pymobiledevice3.services.dvt.testmanaged.xcuitest import XCUITestService
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class MatchedProcessByPid(NamedTuple):
|
|
34
|
+
name: str
|
|
35
|
+
pid: int
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
cli = InjectingTyper(
|
|
39
|
+
name="dvt",
|
|
40
|
+
help="Drive DVT instrumentation APIs (process control, metrics, traces).",
|
|
41
|
+
no_args_is_help=True,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
cli.add_typer(sysmon.cli)
|
|
45
|
+
cli.add_typer(core_profile_session.cli)
|
|
46
|
+
cli.add_typer(simulate_location.cli)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@cli.command("proclist")
|
|
50
|
+
def proclist(service_provider: ServiceProviderDep) -> None:
|
|
51
|
+
"""Show processes (with start times) via DVT."""
|
|
52
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
53
|
+
processes = DeviceInfo(dvt).proclist()
|
|
54
|
+
for process in processes:
|
|
55
|
+
if "startDate" in process:
|
|
56
|
+
process["startDate"] = str(process["startDate"])
|
|
57
|
+
|
|
58
|
+
print_json(processes)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@cli.command("is-running-pid")
|
|
62
|
+
def is_running_pid(service_provider: ServiceProviderDep, pid: int) -> None:
|
|
63
|
+
"""Check if a PID is currently running."""
|
|
64
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
65
|
+
print_json(DeviceInfo(dvt).is_running_pid(pid))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@cli.command("memlimitoff")
|
|
69
|
+
def memlimitoff(service_provider: ServiceProviderDep, pid: int) -> None:
|
|
70
|
+
"""Disable jetsam memory limit for a PID."""
|
|
71
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
72
|
+
ProcessControl(dvt).disable_memory_limit_for_pid(pid)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@cli.command("applist")
|
|
76
|
+
def applist(service_provider: ServiceProviderDep) -> None:
|
|
77
|
+
"""List installed applications via DVT."""
|
|
78
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
79
|
+
apps = ApplicationListing(dvt).applist()
|
|
80
|
+
print_json(apps)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Signals(IntEnum):
|
|
84
|
+
"""Platform-independent version of `signal.Signals`, allowing names to be used on Windows."""
|
|
85
|
+
|
|
86
|
+
HUP = 1
|
|
87
|
+
INT = 2
|
|
88
|
+
QUIT = 3
|
|
89
|
+
ILL = 4
|
|
90
|
+
TRAP = 5
|
|
91
|
+
ABRT = 6
|
|
92
|
+
EMT = 7
|
|
93
|
+
FPE = 8
|
|
94
|
+
KILL = 9
|
|
95
|
+
BUS = 10
|
|
96
|
+
SEGV = 11
|
|
97
|
+
SYS = 12
|
|
98
|
+
PIPE = 13
|
|
99
|
+
ALRM = 14
|
|
100
|
+
TERM = 15
|
|
101
|
+
URG = 16
|
|
102
|
+
STOP = 17
|
|
103
|
+
TSTP = 18
|
|
104
|
+
CONT = 19
|
|
105
|
+
CHLD = 20
|
|
106
|
+
TTIN = 21
|
|
107
|
+
TTOU = 22
|
|
108
|
+
IO = 23
|
|
109
|
+
XCPU = 24
|
|
110
|
+
XFSZ = 25
|
|
111
|
+
VTALRM = 26
|
|
112
|
+
PROF = 27
|
|
113
|
+
WINCH = 28
|
|
114
|
+
INFO = 29
|
|
115
|
+
USR1 = 30
|
|
116
|
+
USR2 = 31
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@cli.command("signal")
|
|
120
|
+
def send_signal(
|
|
121
|
+
service_provider: ServiceProviderDep,
|
|
122
|
+
pid: int,
|
|
123
|
+
sig: Annotated[
|
|
124
|
+
Optional[int],
|
|
125
|
+
typer.Argument(),
|
|
126
|
+
] = None,
|
|
127
|
+
signal_name: Annotated[
|
|
128
|
+
Optional[str],
|
|
129
|
+
typer.Option("--signal-name", "-s"),
|
|
130
|
+
] = None,
|
|
131
|
+
) -> None:
|
|
132
|
+
"""Send a signal to a PID (choose numeric SIG or --signal-name)."""
|
|
133
|
+
if sig is not None and signal_name is not None:
|
|
134
|
+
raise UsageError(message="Cannot give SIG and SIGNAL-NAME together")
|
|
135
|
+
|
|
136
|
+
if signal_name is not None:
|
|
137
|
+
normalized_signal_name = signal_name.upper().removeprefix("SIG")
|
|
138
|
+
try:
|
|
139
|
+
sig = Signals[normalized_signal_name]
|
|
140
|
+
except KeyError:
|
|
141
|
+
raise BadParameter(f"{signal_name!r} is not a valid signal") from None
|
|
142
|
+
elif sig is not None:
|
|
143
|
+
try:
|
|
144
|
+
sig = Signals(sig)
|
|
145
|
+
except ValueError:
|
|
146
|
+
raise BadParameter(f"{sig} is not a valid signal") from None
|
|
147
|
+
else:
|
|
148
|
+
raise MissingParameter(param_type="argument|option", param_hint="'SIG|SIGNAL-NAME'")
|
|
149
|
+
|
|
150
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
151
|
+
ProcessControl(dvt).signal(pid, sig)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@cli.command("kill")
|
|
155
|
+
def kill(service_provider: ServiceProviderDep, pid: int) -> None:
|
|
156
|
+
"""Kill a process by PID."""
|
|
157
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
158
|
+
ProcessControl(dvt).kill(pid)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@cli.command()
|
|
162
|
+
def process_id_for_bundle_id(service_provider: ServiceProviderDep, app_bundle_identifier: str) -> None:
|
|
163
|
+
"""Get PID of a bundle identifier (only returns a valid value if its running)."""
|
|
164
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
165
|
+
print(ProcessControl(dvt).process_identifier_for_bundle_identifier(app_bundle_identifier))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def get_matching_processes(
|
|
169
|
+
service_provider: ServiceProviderDep,
|
|
170
|
+
name: Optional[str] = None,
|
|
171
|
+
bundle_identifier: Optional[str] = None,
|
|
172
|
+
) -> list[MatchedProcessByPid]:
|
|
173
|
+
result: list[MatchedProcessByPid] = []
|
|
174
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
175
|
+
device_info = DeviceInfo(dvt)
|
|
176
|
+
for process in device_info.proclist():
|
|
177
|
+
current_name = process["name"]
|
|
178
|
+
current_bundle_identifier = process.get("bundleIdentifier", "")
|
|
179
|
+
pid = process["pid"]
|
|
180
|
+
if (bundle_identifier is not None and bundle_identifier in current_bundle_identifier) or (
|
|
181
|
+
name is not None and name in current_name
|
|
182
|
+
):
|
|
183
|
+
result.append(MatchedProcessByPid(name=current_name, pid=pid))
|
|
184
|
+
return result
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@cli.command("pkill")
|
|
188
|
+
def pkill(
|
|
189
|
+
service_provider: ServiceProviderDep,
|
|
190
|
+
expression: str,
|
|
191
|
+
bundle: Annotated[
|
|
192
|
+
bool,
|
|
193
|
+
typer.Option(help="Treat given expression as a bundle-identifier instead of a process name"),
|
|
194
|
+
] = False,
|
|
195
|
+
) -> None:
|
|
196
|
+
"""Kill all processes containing `expression` in their name."""
|
|
197
|
+
matching_name = expression if not bundle else None
|
|
198
|
+
matching_bundle_identifier = expression if bundle else None
|
|
199
|
+
matching_processes = get_matching_processes(
|
|
200
|
+
service_provider, name=matching_name, bundle_identifier=matching_bundle_identifier
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
204
|
+
process_control = ProcessControl(dvt)
|
|
205
|
+
|
|
206
|
+
for process in matching_processes:
|
|
207
|
+
logger.info(f"killing {process.name}({process.pid})")
|
|
208
|
+
process_control.kill(process.pid)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@cli.command("launch")
|
|
212
|
+
def launch(
|
|
213
|
+
service_provider: ServiceProviderDep,
|
|
214
|
+
arguments: str,
|
|
215
|
+
kill_existing: Annotated[
|
|
216
|
+
bool,
|
|
217
|
+
typer.Option(help="Whether to kill an existing instance of this process"),
|
|
218
|
+
] = True,
|
|
219
|
+
suspended: Annotated[
|
|
220
|
+
bool,
|
|
221
|
+
typer.Option(help="Same as WaitForDebugger"),
|
|
222
|
+
] = False,
|
|
223
|
+
env: Annotated[
|
|
224
|
+
Optional[list[str]],
|
|
225
|
+
typer.Option(
|
|
226
|
+
help="Environment variable to pass to process given as key=value (can be specified multiple times)"
|
|
227
|
+
),
|
|
228
|
+
] = None,
|
|
229
|
+
stream: bool = False,
|
|
230
|
+
) -> None:
|
|
231
|
+
"""Launch a process."""
|
|
232
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
233
|
+
parsed_arguments = shlex.split(arguments)
|
|
234
|
+
process_control = ProcessControl(dvt)
|
|
235
|
+
pid = process_control.launch(
|
|
236
|
+
bundle_id=parsed_arguments[0],
|
|
237
|
+
arguments=parsed_arguments[1:],
|
|
238
|
+
kill_existing=kill_existing,
|
|
239
|
+
start_suspended=suspended,
|
|
240
|
+
environment=dict(var.split("=", 1) for var in env or ()),
|
|
241
|
+
)
|
|
242
|
+
print(f"Process launched with pid {pid}")
|
|
243
|
+
while stream:
|
|
244
|
+
for output_received in process_control:
|
|
245
|
+
logging.getLogger(f"PID:{output_received.pid}").info(output_received.message.strip())
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@cli.command("shell")
|
|
249
|
+
def dvt_shell(service_provider: ServiceProviderDep) -> None:
|
|
250
|
+
"""Launch developer shell (used for pymobiledevice3 R&D)"""
|
|
251
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
252
|
+
dvt.shell()
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def show_dirlist(device_info: DeviceInfo, dirname: str, recursive: bool = False) -> None:
|
|
256
|
+
try:
|
|
257
|
+
filenames = device_info.ls(dirname)
|
|
258
|
+
except DvtDirListError:
|
|
259
|
+
return
|
|
260
|
+
|
|
261
|
+
for filename in filenames:
|
|
262
|
+
filename = posixpath.join(dirname, filename)
|
|
263
|
+
print(filename)
|
|
264
|
+
if recursive:
|
|
265
|
+
show_dirlist(device_info, filename, recursive=recursive)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@cli.command("ls")
|
|
269
|
+
def ls(
|
|
270
|
+
service_provider: ServiceProviderDep,
|
|
271
|
+
path: Path,
|
|
272
|
+
recursive: Annotated[
|
|
273
|
+
bool,
|
|
274
|
+
typer.Option("--recursive", "-r"),
|
|
275
|
+
] = False,
|
|
276
|
+
) -> None:
|
|
277
|
+
"""List directory"""
|
|
278
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
279
|
+
show_dirlist(DeviceInfo(dvt), str(path), recursive=recursive)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
@cli.command("device-information")
|
|
283
|
+
def device_information(service_provider: ServiceProviderDep) -> None:
|
|
284
|
+
"""Print system information"""
|
|
285
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
286
|
+
device_info = DeviceInfo(dvt)
|
|
287
|
+
info = {
|
|
288
|
+
"hardware": device_info.hardware_information(),
|
|
289
|
+
"network": device_info.network_information(),
|
|
290
|
+
"kernel-name": device_info.mach_kernel_name(),
|
|
291
|
+
"kpep-database": device_info.kpep_database(),
|
|
292
|
+
}
|
|
293
|
+
with contextlib.suppress(UnrecognizedSelectorError):
|
|
294
|
+
info["system"] = device_info.system_information()
|
|
295
|
+
print_json(info)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@cli.command("netstat")
|
|
299
|
+
def netstat(service_provider: ServiceProviderDep) -> None:
|
|
300
|
+
"""Print information about current network activity."""
|
|
301
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt, NetworkMonitor(dvt) as monitor:
|
|
302
|
+
for event in monitor:
|
|
303
|
+
if isinstance(event, ConnectionDetectionEvent):
|
|
304
|
+
local_host, local_port = event.local_address.split(":")
|
|
305
|
+
remote_host, remote_port = event.local_address.split(":")
|
|
306
|
+
logger.info(f"Connection detected: {local_host}:{local_port} -> {remote_host}:{remote_port}")
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
@cli.command("screenshot")
|
|
310
|
+
def dvt_screenshot(service_provider: ServiceProviderDep, out: Path) -> None:
|
|
311
|
+
"""Take device screenshot"""
|
|
312
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
313
|
+
out.write_bytes(Screenshot(dvt).get_screenshot())
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@cli.command("xcuitest")
|
|
317
|
+
def xcuitest(service_provider: ServiceProviderDep, bundle_id: str) -> None:
|
|
318
|
+
"""
|
|
319
|
+
Start XCUITest
|
|
320
|
+
|
|
321
|
+
\b
|
|
322
|
+
Usage example:
|
|
323
|
+
\b python3 -m pymobiledevice3 developer dvt xcuitest com.facebook.WebDriverAgentRunner.xctrunner
|
|
324
|
+
"""
|
|
325
|
+
XCUITestService(service_provider).run(bundle_id)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@cli.command("trace-codes")
|
|
329
|
+
def dvt_trace_codes(service_provider: ServiceProviderDep) -> None:
|
|
330
|
+
"""Print KDebug trace codes."""
|
|
331
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
332
|
+
device_info = DeviceInfo(dvt)
|
|
333
|
+
print_json({hex(k): v for k, v in device_info.trace_codes().items()})
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
@cli.command("name-for-uid")
|
|
337
|
+
def dvt_name_for_uid(service_provider: ServiceProviderDep, uid: int) -> None:
|
|
338
|
+
"""Print the assiciated username for the given uid."""
|
|
339
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
340
|
+
device_info = DeviceInfo(dvt)
|
|
341
|
+
print(device_info.name_for_uid(uid))
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@cli.command("name-for-gid")
|
|
345
|
+
def dvt_name_for_gid(service_provider: ServiceProviderDep, gid: int) -> None:
|
|
346
|
+
"""Print the assiciated group name for the given gid."""
|
|
347
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
348
|
+
device_info = DeviceInfo(dvt)
|
|
349
|
+
print(device_info.name_for_gid(gid))
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
@cli.command("oslog")
|
|
353
|
+
def dvt_oslog(service_provider: ServiceProviderDep, pid: int) -> None:
|
|
354
|
+
"""Sniff device oslog (not very stable, but includes more data and normal syslog)"""
|
|
355
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt, ActivityTraceTap(dvt) as tap:
|
|
356
|
+
for message in tap:
|
|
357
|
+
message_pid = message.process
|
|
358
|
+
# without message_type maybe signpost have event_type
|
|
359
|
+
message_type = (
|
|
360
|
+
message.message_type
|
|
361
|
+
if hasattr(message, "message_type")
|
|
362
|
+
else message.event_type
|
|
363
|
+
if hasattr(message, "event_type")
|
|
364
|
+
else "unknown"
|
|
365
|
+
)
|
|
366
|
+
sender_image_path = message.sender_image_path
|
|
367
|
+
image_name = os.path.basename(sender_image_path)
|
|
368
|
+
subsystem = message.subsystem
|
|
369
|
+
category = message.category
|
|
370
|
+
timestamp = datetime.now()
|
|
371
|
+
|
|
372
|
+
if pid is not None and message_pid != pid:
|
|
373
|
+
continue
|
|
374
|
+
|
|
375
|
+
formatted_message = decode_message_format(message.message) if message.message else message.name
|
|
376
|
+
|
|
377
|
+
if user_requested_colored_output():
|
|
378
|
+
timestamp = typer.style(str(timestamp), bold=True)
|
|
379
|
+
message_pid = typer.style(str(message_pid), "magenta")
|
|
380
|
+
subsystem = typer.style(subsystem, "green")
|
|
381
|
+
category = typer.style(category, "green")
|
|
382
|
+
image_name = typer.style(image_name, "yellow")
|
|
383
|
+
message_type = typer.style(message_type, "cyan")
|
|
384
|
+
|
|
385
|
+
print(
|
|
386
|
+
f"[{timestamp}][{subsystem}][{category}][{message_pid}][{image_name}] "
|
|
387
|
+
f"<{message_type}>: {formatted_message}"
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
@cli.command("energy")
|
|
392
|
+
def dvt_energy(service_provider: ServiceProviderDep, pid_list: list[str]) -> None:
|
|
393
|
+
"""Monitor the energy consumption for given PIDs"""
|
|
394
|
+
|
|
395
|
+
if len(pid_list) == 0:
|
|
396
|
+
logger.error("pid_list must not be empty")
|
|
397
|
+
return
|
|
398
|
+
|
|
399
|
+
pid_int_list = [int(pid) for pid in pid_list]
|
|
400
|
+
|
|
401
|
+
with (
|
|
402
|
+
DvtSecureSocketProxyService(lockdown=service_provider) as dvt,
|
|
403
|
+
EnergyMonitor(dvt, pid_int_list) as energy_monitor,
|
|
404
|
+
):
|
|
405
|
+
for telemetry in energy_monitor:
|
|
406
|
+
logger.info(telemetry)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
@cli.command("notifications")
|
|
410
|
+
def dvt_notifications(service_provider: ServiceProviderDep) -> None:
|
|
411
|
+
"""Monitor memory and app notifications"""
|
|
412
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt, Notifications(dvt) as notifications:
|
|
413
|
+
for notification in notifications:
|
|
414
|
+
logger.info(notification)
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
@cli.command("graphics")
|
|
418
|
+
def dvt_graphics(service_provider: ServiceProviderDep) -> None:
|
|
419
|
+
"""Monitor graphics-related information"""
|
|
420
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt, Graphics(dvt) as graphics:
|
|
421
|
+
for stats in graphics:
|
|
422
|
+
logger.info(stats)
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
@cli.command("har")
|
|
426
|
+
def dvt_har(service_provider: ServiceProviderDep) -> None:
|
|
427
|
+
"""
|
|
428
|
+
Enable har-logging
|
|
429
|
+
|
|
430
|
+
\b
|
|
431
|
+
For more information, please read:
|
|
432
|
+
\b https://github.com/doronz88/harlogger?tab=readme-ov-file#enable-http-instrumentation-method
|
|
433
|
+
"""
|
|
434
|
+
with DvtSecureSocketProxyService(lockdown=service_provider) as dvt:
|
|
435
|
+
print("> Press Ctrl-C to abort")
|
|
436
|
+
with ActivityTraceTap(dvt, enable_http_archive_logging=True) as tap:
|
|
437
|
+
while True:
|
|
438
|
+
tap.channel.receive_message()
|