pymobiledevice3 5.0.0__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 +466 -384
- 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.0.dist-info → pymobiledevice3-5.0.2.dist-info}/METADATA +1 -1
- pymobiledevice3-5.0.2.dist-info/RECORD +173 -0
- pymobiledevice3-5.0.0.dist-info/RECORD +0 -173
- {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/WHEEL +0 -0
- {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/top_level.txt +0 -0
pymobiledevice3/remote/utils.py
CHANGED
|
@@ -9,11 +9,12 @@ from pymobiledevice3.bonjour import DEFAULT_BONJOUR_TIMEOUT, browse_remoted
|
|
|
9
9
|
from pymobiledevice3.exceptions import AccessDeniedError
|
|
10
10
|
from pymobiledevice3.remote.remote_service_discovery import RSD_PORT, RemoteServiceDiscoveryService
|
|
11
11
|
|
|
12
|
-
REMOTED_PATH =
|
|
12
|
+
REMOTED_PATH = "/usr/libexec/remoted"
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
async def get_rsds(
|
|
16
|
-
|
|
15
|
+
async def get_rsds(
|
|
16
|
+
bonjour_timeout: float = DEFAULT_BONJOUR_TIMEOUT, udid: Optional[str] = None
|
|
17
|
+
) -> list[RemoteServiceDiscoveryService]:
|
|
17
18
|
result = []
|
|
18
19
|
with stop_remoted():
|
|
19
20
|
for answer in await browse_remoted(timeout=bonjour_timeout):
|
|
@@ -45,39 +46,39 @@ def get_remoted_process() -> psutil.Process:
|
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
def stop_remoted_if_required() -> None:
|
|
48
|
-
if platform.system() !=
|
|
49
|
+
if platform.system() != "Darwin":
|
|
49
50
|
# only Darwin systems require it
|
|
50
51
|
return
|
|
51
52
|
|
|
52
53
|
remoted = get_remoted_process()
|
|
53
54
|
if remoted is None:
|
|
54
55
|
return
|
|
55
|
-
if remoted.status() ==
|
|
56
|
+
if remoted.status() == "stopped":
|
|
56
57
|
# process already stopped, we don't need to do anything
|
|
57
58
|
return
|
|
58
59
|
|
|
59
60
|
try:
|
|
60
61
|
remoted.suspend()
|
|
61
|
-
except psutil.AccessDenied:
|
|
62
|
-
raise AccessDeniedError()
|
|
62
|
+
except psutil.AccessDenied as e:
|
|
63
|
+
raise AccessDeniedError() from e
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
def resume_remoted_if_required() -> None:
|
|
66
|
-
if platform.system() !=
|
|
67
|
+
if platform.system() != "Darwin":
|
|
67
68
|
# only Darwin systems require it
|
|
68
69
|
return
|
|
69
70
|
|
|
70
71
|
remoted = get_remoted_process()
|
|
71
72
|
if remoted is None:
|
|
72
73
|
return
|
|
73
|
-
if remoted.status() ==
|
|
74
|
+
if remoted.status() == "running":
|
|
74
75
|
# process already running, we don't need to do anything
|
|
75
76
|
return
|
|
76
77
|
|
|
77
78
|
try:
|
|
78
79
|
remoted.resume()
|
|
79
|
-
except psutil.AccessDenied:
|
|
80
|
-
raise AccessDeniedError()
|
|
80
|
+
except psutil.AccessDenied as e:
|
|
81
|
+
raise AccessDeniedError() from e
|
|
81
82
|
|
|
82
83
|
|
|
83
84
|
@contextlib.contextmanager
|
|
@@ -3,50 +3,74 @@ import uuid
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
-
from construct import
|
|
7
|
-
|
|
6
|
+
from construct import (
|
|
7
|
+
Aligned,
|
|
8
|
+
Array,
|
|
9
|
+
Bytes,
|
|
10
|
+
Const,
|
|
11
|
+
CString,
|
|
12
|
+
Default,
|
|
13
|
+
Double,
|
|
14
|
+
Enum,
|
|
15
|
+
ExprAdapter,
|
|
16
|
+
FlagsEnum,
|
|
17
|
+
GreedyBytes,
|
|
18
|
+
Hex,
|
|
19
|
+
If,
|
|
20
|
+
Int32ul,
|
|
21
|
+
Int64sl,
|
|
22
|
+
Int64ul,
|
|
23
|
+
LazyBound,
|
|
24
|
+
Pass,
|
|
25
|
+
Prefixed,
|
|
26
|
+
Probe,
|
|
27
|
+
Struct,
|
|
28
|
+
Switch,
|
|
29
|
+
this,
|
|
30
|
+
)
|
|
8
31
|
from construct import Optional as ConstructOptional
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
XpcFlags = FlagsEnum(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
32
|
+
|
|
33
|
+
XpcMessageType = Enum(
|
|
34
|
+
Hex(Int32ul),
|
|
35
|
+
NULL=0x00001000,
|
|
36
|
+
BOOL=0x00002000,
|
|
37
|
+
INT64=0x00003000,
|
|
38
|
+
UINT64=0x00004000,
|
|
39
|
+
DOUBLE=0x00005000,
|
|
40
|
+
POINTER=0x00006000,
|
|
41
|
+
DATE=0x00007000,
|
|
42
|
+
DATA=0x00008000,
|
|
43
|
+
STRING=0x00009000,
|
|
44
|
+
UUID=0x0000A000,
|
|
45
|
+
FD=0x0000B000,
|
|
46
|
+
SHMEM=0x0000C000,
|
|
47
|
+
MACH_SEND=0x0000D000,
|
|
48
|
+
ARRAY=0x0000E000,
|
|
49
|
+
DICTIONARY=0x0000F000,
|
|
50
|
+
ERROR=0x00010000,
|
|
51
|
+
CONNECTION=0x00011000,
|
|
52
|
+
ENDPOINT=0x00012000,
|
|
53
|
+
SERIALIZER=0x00013000,
|
|
54
|
+
PIPE=0x00014000,
|
|
55
|
+
MACH_RECV=0x00015000,
|
|
56
|
+
BUNDLE=0x00016000,
|
|
57
|
+
SERVICE=0x00017000,
|
|
58
|
+
SERVICE_INSTANCE=0x00018000,
|
|
59
|
+
ACTIVITY=0x00019000,
|
|
60
|
+
FILE_TRANSFER=0x0001A000,
|
|
61
|
+
)
|
|
62
|
+
XpcFlags = FlagsEnum(
|
|
63
|
+
Hex(Int32ul),
|
|
64
|
+
ALWAYS_SET=0x00000001,
|
|
65
|
+
PING=0x00000002,
|
|
66
|
+
DATA_PRESENT=0x00000100,
|
|
67
|
+
WANTING_REPLY=0x00010000,
|
|
68
|
+
REPLY=0x00020000,
|
|
69
|
+
FILE_TX_STREAM_REQUEST=0x00100000,
|
|
70
|
+
FILE_TX_STREAM_RESPONSE=0x00200000,
|
|
71
|
+
INIT_HANDSHAKE=0x00400000,
|
|
72
|
+
)
|
|
73
|
+
AlignedString = Aligned(4, CString("utf8"))
|
|
50
74
|
XpcNull = Pass
|
|
51
75
|
XpcBool = Int32ul
|
|
52
76
|
XpcInt64 = Int64sl
|
|
@@ -55,59 +79,67 @@ XpcDouble = Double
|
|
|
55
79
|
XpcPointer = None
|
|
56
80
|
XpcDate = Int64ul
|
|
57
81
|
XpcData = Aligned(4, Prefixed(Int32ul, GreedyBytes))
|
|
58
|
-
XpcString = Aligned(4, Prefixed(Int32ul, CString(
|
|
82
|
+
XpcString = Aligned(4, Prefixed(Int32ul, CString("utf8")))
|
|
59
83
|
XpcUuid = Bytes(16)
|
|
60
84
|
XpcFd = Int32ul
|
|
61
|
-
XpcShmem = Struct(
|
|
62
|
-
XpcArray = Prefixed(Int32ul, Struct(
|
|
63
|
-
'count' / Int32ul,
|
|
64
|
-
'entries' / Array(this.count, LazyBound(lambda: XpcObject))))
|
|
85
|
+
XpcShmem = Struct("length" / Int32ul, Int32ul)
|
|
86
|
+
XpcArray = Prefixed(Int32ul, Struct("count" / Int32ul, "entries" / Array(this.count, LazyBound(lambda: XpcObject))))
|
|
65
87
|
XpcDictionaryEntry = Struct(
|
|
66
|
-
|
|
67
|
-
|
|
88
|
+
"key" / AlignedString,
|
|
89
|
+
"value" / LazyBound(lambda: XpcObject),
|
|
90
|
+
)
|
|
91
|
+
XpcDictionary = Prefixed(
|
|
92
|
+
Int32ul,
|
|
93
|
+
Struct(
|
|
94
|
+
"count" / Hex(Int32ul),
|
|
95
|
+
"entries" / If(this.count > 0, Array(this.count, XpcDictionaryEntry)),
|
|
96
|
+
),
|
|
68
97
|
)
|
|
69
|
-
XpcDictionary = Prefixed(Int32ul, Struct(
|
|
70
|
-
'count' / Hex(Int32ul),
|
|
71
|
-
'entries' / If(this.count > 0, Array(this.count, XpcDictionaryEntry)),
|
|
72
|
-
))
|
|
73
98
|
XpcFileTransfer = Struct(
|
|
74
|
-
|
|
75
|
-
|
|
99
|
+
"msg_id" / Int64ul,
|
|
100
|
+
"data" / LazyBound(lambda: XpcObject),
|
|
76
101
|
)
|
|
77
102
|
XpcObject = Struct(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
"type" / XpcMessageType,
|
|
104
|
+
"data"
|
|
105
|
+
/ Switch(
|
|
106
|
+
this.type,
|
|
107
|
+
{
|
|
108
|
+
XpcMessageType.DICTIONARY: XpcDictionary,
|
|
109
|
+
XpcMessageType.STRING: XpcString,
|
|
110
|
+
XpcMessageType.INT64: XpcInt64,
|
|
111
|
+
XpcMessageType.UINT64: XpcUInt64,
|
|
112
|
+
XpcMessageType.DOUBLE: XpcDouble,
|
|
113
|
+
XpcMessageType.BOOL: XpcBool,
|
|
114
|
+
XpcMessageType.NULL: XpcNull,
|
|
115
|
+
XpcMessageType.UUID: XpcUuid,
|
|
116
|
+
XpcMessageType.POINTER: XpcPointer,
|
|
117
|
+
XpcMessageType.DATE: XpcDate,
|
|
118
|
+
XpcMessageType.DATA: XpcData,
|
|
119
|
+
XpcMessageType.FD: XpcFd,
|
|
120
|
+
XpcMessageType.SHMEM: XpcShmem,
|
|
121
|
+
XpcMessageType.ARRAY: XpcArray,
|
|
122
|
+
XpcMessageType.FILE_TRANSFER: XpcFileTransfer,
|
|
123
|
+
},
|
|
124
|
+
default=Probe(lookahead=1000),
|
|
125
|
+
),
|
|
96
126
|
)
|
|
97
127
|
XpcPayload = Struct(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
128
|
+
"magic" / Hex(Const(0x42133742, Int32ul)),
|
|
129
|
+
"protocol_version" / Hex(Const(0x00000005, Int32ul)),
|
|
130
|
+
"obj" / XpcObject,
|
|
101
131
|
)
|
|
102
132
|
XpcWrapper = Struct(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
133
|
+
"magic" / Hex(Const(0x29B00B92, Int32ul)),
|
|
134
|
+
"flags" / Default(XpcFlags, XpcFlags.ALWAYS_SET),
|
|
135
|
+
"message"
|
|
136
|
+
/ Prefixed(
|
|
106
137
|
ExprAdapter(Int64ul, lambda obj, context: obj + 8, lambda obj, context: obj - 8),
|
|
107
138
|
Struct(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
)
|
|
139
|
+
"message_id" / Hex(Default(Int64ul, 0)),
|
|
140
|
+
"payload" / ConstructOptional(XpcPayload),
|
|
141
|
+
),
|
|
142
|
+
),
|
|
111
143
|
)
|
|
112
144
|
|
|
113
145
|
|
|
@@ -170,7 +202,7 @@ def _decode_xpc_date(xpc_object) -> datetime:
|
|
|
170
202
|
|
|
171
203
|
|
|
172
204
|
def _decode_xpc_file_transfer(xpc_object) -> FileTransferType:
|
|
173
|
-
return FileTransferType(transfer_size=_decode_xpc_dictionary(xpc_object.data.data)[
|
|
205
|
+
return FileTransferType(transfer_size=_decode_xpc_dictionary(xpc_object.data.data)["s"])
|
|
174
206
|
|
|
175
207
|
|
|
176
208
|
def _decode_xpc_double(xpc_object) -> float:
|
|
@@ -198,7 +230,7 @@ def decode_xpc_object(xpc_object) -> Any:
|
|
|
198
230
|
}
|
|
199
231
|
decoder = decoders.get(xpc_object.type)
|
|
200
232
|
if decoder is None:
|
|
201
|
-
raise TypeError(f
|
|
233
|
+
raise TypeError(f"deserialize error: {xpc_object}")
|
|
202
234
|
return decoder(xpc_object)
|
|
203
235
|
|
|
204
236
|
|
|
@@ -207,82 +239,76 @@ def _build_xpc_array(payload: list) -> dict:
|
|
|
207
239
|
for entry in payload:
|
|
208
240
|
entry = _build_xpc_object(entry)
|
|
209
241
|
entries.append(entry)
|
|
210
|
-
return {
|
|
211
|
-
'type': XpcMessageType.ARRAY,
|
|
212
|
-
'data': {
|
|
213
|
-
'count': len(entries),
|
|
214
|
-
'entries': entries
|
|
215
|
-
}
|
|
216
|
-
}
|
|
242
|
+
return {"type": XpcMessageType.ARRAY, "data": {"count": len(entries), "entries": entries}}
|
|
217
243
|
|
|
218
244
|
|
|
219
245
|
def _build_xpc_dictionary(payload: dict) -> dict:
|
|
220
246
|
entries = []
|
|
221
247
|
for key, value in payload.items():
|
|
222
|
-
entry = {
|
|
248
|
+
entry = {"key": key, "value": _build_xpc_object(value)}
|
|
223
249
|
entries.append(entry)
|
|
224
250
|
return {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
251
|
+
"type": XpcMessageType.DICTIONARY,
|
|
252
|
+
"data": {
|
|
253
|
+
"count": len(entries),
|
|
254
|
+
"entries": entries,
|
|
255
|
+
},
|
|
230
256
|
}
|
|
231
257
|
|
|
232
258
|
|
|
233
259
|
def _build_xpc_bool(payload: bool) -> dict:
|
|
234
260
|
return {
|
|
235
|
-
|
|
236
|
-
|
|
261
|
+
"type": XpcMessageType.BOOL,
|
|
262
|
+
"data": payload,
|
|
237
263
|
}
|
|
238
264
|
|
|
239
265
|
|
|
240
266
|
def _build_xpc_string(payload: str) -> dict:
|
|
241
267
|
return {
|
|
242
|
-
|
|
243
|
-
|
|
268
|
+
"type": XpcMessageType.STRING,
|
|
269
|
+
"data": payload,
|
|
244
270
|
}
|
|
245
271
|
|
|
246
272
|
|
|
247
273
|
def _build_xpc_data(payload: bool) -> dict:
|
|
248
274
|
return {
|
|
249
|
-
|
|
250
|
-
|
|
275
|
+
"type": XpcMessageType.DATA,
|
|
276
|
+
"data": payload,
|
|
251
277
|
}
|
|
252
278
|
|
|
253
279
|
|
|
254
280
|
def _build_xpc_double(payload: float) -> dict:
|
|
255
281
|
return {
|
|
256
|
-
|
|
257
|
-
|
|
282
|
+
"type": XpcMessageType.DOUBLE,
|
|
283
|
+
"data": payload,
|
|
258
284
|
}
|
|
259
285
|
|
|
260
286
|
|
|
261
287
|
def _build_xpc_uuid(payload: uuid.UUID) -> dict:
|
|
262
288
|
return {
|
|
263
|
-
|
|
264
|
-
|
|
289
|
+
"type": XpcMessageType.UUID,
|
|
290
|
+
"data": payload.bytes,
|
|
265
291
|
}
|
|
266
292
|
|
|
267
293
|
|
|
268
294
|
def _build_xpc_null(payload: None) -> dict:
|
|
269
295
|
return {
|
|
270
|
-
|
|
271
|
-
|
|
296
|
+
"type": XpcMessageType.NULL,
|
|
297
|
+
"data": None,
|
|
272
298
|
}
|
|
273
299
|
|
|
274
300
|
|
|
275
301
|
def _build_xpc_uint64(payload: XpcUInt64Type) -> dict:
|
|
276
302
|
return {
|
|
277
|
-
|
|
278
|
-
|
|
303
|
+
"type": XpcMessageType.UINT64,
|
|
304
|
+
"data": payload,
|
|
279
305
|
}
|
|
280
306
|
|
|
281
307
|
|
|
282
308
|
def _build_xpc_int64(payload: XpcInt64Type) -> dict:
|
|
283
309
|
return {
|
|
284
|
-
|
|
285
|
-
|
|
310
|
+
"type": XpcMessageType.INT64,
|
|
311
|
+
"data": payload,
|
|
286
312
|
}
|
|
287
313
|
|
|
288
314
|
|
|
@@ -298,12 +324,12 @@ def _build_xpc_object(payload: Any) -> dict:
|
|
|
298
324
|
bytearray: _build_xpc_data,
|
|
299
325
|
float: _build_xpc_double,
|
|
300
326
|
uuid.UUID: _build_xpc_uuid,
|
|
301
|
-
|
|
302
|
-
|
|
327
|
+
"XpcUInt64Type": _build_xpc_uint64,
|
|
328
|
+
"XpcInt64Type": _build_xpc_int64,
|
|
303
329
|
}
|
|
304
330
|
builder = payload_builders.get(type(payload), payload_builders.get(type(payload).__name__))
|
|
305
331
|
if builder is None:
|
|
306
|
-
raise TypeError(f
|
|
332
|
+
raise TypeError(f"unrecognized type for: {payload} {type(payload)}")
|
|
307
333
|
return builder(payload)
|
|
308
334
|
|
|
309
335
|
|
|
@@ -314,13 +340,7 @@ def create_xpc_wrapper(d: dict, message_id: int = 0, wanting_reply: bool = False
|
|
|
314
340
|
if wanting_reply:
|
|
315
341
|
flags |= XpcFlags.WANTING_REPLY
|
|
316
342
|
|
|
317
|
-
xpc_payload = {
|
|
318
|
-
'message_id': message_id,
|
|
319
|
-
'payload': {'obj': _build_xpc_object(d)}
|
|
320
|
-
}
|
|
343
|
+
xpc_payload = {"message_id": message_id, "payload": {"obj": _build_xpc_object(d)}}
|
|
321
344
|
|
|
322
|
-
xpc_wrapper = {
|
|
323
|
-
'flags': flags,
|
|
324
|
-
'message': xpc_payload
|
|
325
|
-
}
|
|
345
|
+
xpc_wrapper = {"flags": flags, "message": xpc_payload}
|
|
326
346
|
return XpcWrapper.build(xpc_wrapper)
|
|
@@ -6,10 +6,10 @@ from uuid import UUID
|
|
|
6
6
|
import click
|
|
7
7
|
import coloredlogs
|
|
8
8
|
|
|
9
|
-
MAGIC = b
|
|
10
|
-
DYLD_MAGIC = b
|
|
11
|
-
MAP_FILENAME = os.path.join(os.path.dirname(__file__),
|
|
12
|
-
PARTITIONS = (
|
|
9
|
+
MAGIC = b"\x0b\x10\x00\x00"
|
|
10
|
+
DYLD_MAGIC = b"dyld_v1"
|
|
11
|
+
MAP_FILENAME = os.path.join(os.path.dirname(__file__), "dsc_uuid_map.json")
|
|
12
|
+
PARTITIONS = ("/System", "/usr", "/Applications", "/private")
|
|
13
13
|
DYLD_UUID_OFFSET = 0x58
|
|
14
14
|
UUID_SIZE = 0x10
|
|
15
15
|
|
|
@@ -34,9 +34,9 @@ def sanitize_path(path):
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
@click.command()
|
|
37
|
-
@click.argument(
|
|
38
|
-
@click.argument(
|
|
39
|
-
@click.option(
|
|
37
|
+
@click.argument("dyld_uuid", type=click.UUID)
|
|
38
|
+
@click.argument("dsc", type=click.File("rb"))
|
|
39
|
+
@click.option("-f", "--force", is_flag=True)
|
|
40
40
|
def main(dsc, dyld_uuid, force):
|
|
41
41
|
"""
|
|
42
42
|
Simple utility to get all UUIDs used for symbolication from given DSC.
|
|
@@ -48,23 +48,23 @@ def main(dsc, dyld_uuid, force):
|
|
|
48
48
|
dsc = dsc.read()
|
|
49
49
|
|
|
50
50
|
if not dsc.startswith(DYLD_MAGIC):
|
|
51
|
-
logging.error(
|
|
51
|
+
logging.error("invalid dsc file")
|
|
52
52
|
return
|
|
53
53
|
|
|
54
|
-
dsc_uuid = str(UUID(bytes=dsc[DYLD_UUID_OFFSET:DYLD_UUID_OFFSET + UUID_SIZE]))
|
|
54
|
+
dsc_uuid = str(UUID(bytes=dsc[DYLD_UUID_OFFSET : DYLD_UUID_OFFSET + UUID_SIZE]))
|
|
55
55
|
|
|
56
56
|
if dsc_uuid in uuid_map:
|
|
57
|
-
logger.warning(f
|
|
57
|
+
logger.warning(f"dsc {dsc_uuid} is already found in dsc_uuid_map")
|
|
58
58
|
if not force:
|
|
59
|
-
logger.info(
|
|
59
|
+
logger.info("exiting. use --force to force update")
|
|
60
60
|
return
|
|
61
61
|
else:
|
|
62
|
-
uuid_map[dsc_uuid] = {str(dyld_uuid):
|
|
62
|
+
uuid_map[dsc_uuid] = {str(dyld_uuid): "/usr/lib/dyld"}
|
|
63
63
|
|
|
64
64
|
for i in range(0, len(dsc) - 4, 4):
|
|
65
65
|
# we can assume MAGIC is always aligned to 4
|
|
66
66
|
|
|
67
|
-
if dsc[i:i + 4] != MAGIC:
|
|
67
|
+
if dsc[i : i + 4] != MAGIC:
|
|
68
68
|
continue
|
|
69
69
|
|
|
70
70
|
# skip NULLs for filename pad
|
|
@@ -73,29 +73,29 @@ def main(dsc, dyld_uuid, force):
|
|
|
73
73
|
j -= 1
|
|
74
74
|
|
|
75
75
|
# read filename backwards
|
|
76
|
-
filename =
|
|
76
|
+
filename = ""
|
|
77
77
|
c = chr(dsc[j])
|
|
78
78
|
while c.isprintable():
|
|
79
79
|
filename = c + filename
|
|
80
80
|
j -= 1
|
|
81
81
|
c = chr(dsc[j])
|
|
82
82
|
|
|
83
|
-
if
|
|
83
|
+
if "/" not in filename:
|
|
84
84
|
continue
|
|
85
85
|
|
|
86
86
|
filename = sanitize_path(filename)
|
|
87
87
|
|
|
88
88
|
# read uuid
|
|
89
|
-
uuid = UUID(bytes=dsc[i + 4:i + 4 + UUID_SIZE])
|
|
89
|
+
uuid = UUID(bytes=dsc[i + 4 : i + 4 + UUID_SIZE])
|
|
90
90
|
|
|
91
|
-
logging.info(f
|
|
91
|
+
logging.info(f"offset: 0x{i:x} image: {filename} uuid: {uuid}")
|
|
92
92
|
|
|
93
93
|
uuid_map[dsc_uuid][str(uuid)] = filename
|
|
94
94
|
|
|
95
|
-
with open(MAP_FILENAME,
|
|
95
|
+
with open(MAP_FILENAME, "w") as f:
|
|
96
96
|
json.dump(uuid_map, f, indent=4)
|
|
97
97
|
|
|
98
98
|
|
|
99
|
-
if __name__ ==
|
|
99
|
+
if __name__ == "__main__":
|
|
100
100
|
coloredlogs.install(level=logging.DEBUG)
|
|
101
101
|
main()
|
|
@@ -5,61 +5,61 @@ import plistlib
|
|
|
5
5
|
import click
|
|
6
6
|
import coloredlogs
|
|
7
7
|
|
|
8
|
-
NOTIFICATIONS_FILENAME = os.path.join(os.path.dirname(__file__),
|
|
8
|
+
NOTIFICATIONS_FILENAME = os.path.join(os.path.dirname(__file__), "notifications.txt")
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def get_notifications():
|
|
12
|
-
with open(NOTIFICATIONS_FILENAME,
|
|
13
|
-
return f.read().decode().split(
|
|
12
|
+
with open(NOTIFICATIONS_FILENAME, "rb") as f:
|
|
13
|
+
return f.read().decode().split("\n")
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def save_notifications(notifications: list[str]):
|
|
17
|
-
with open(NOTIFICATIONS_FILENAME,
|
|
17
|
+
with open(NOTIFICATIONS_FILENAME, "wb") as f:
|
|
18
18
|
notifications.sort()
|
|
19
|
-
f.write(
|
|
19
|
+
f.write("\n".join(notifications).encode())
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
@click.command()
|
|
23
|
-
@click.argument(
|
|
23
|
+
@click.argument("root_fs", type=click.Path(dir_okay=True, file_okay=False, exists=True))
|
|
24
24
|
def main(root_fs):
|
|
25
25
|
"""
|
|
26
26
|
Add notifications registered to `com.apple.notifyd.matching` from a given IPSW `root_fs` (extracted filesystem)
|
|
27
27
|
into `notifications.txt`
|
|
28
28
|
"""
|
|
29
|
-
launch_daemons = os.path.join(root_fs,
|
|
29
|
+
launch_daemons = os.path.join(root_fs, "System", "Library", "LaunchDaemons")
|
|
30
30
|
|
|
31
31
|
notifications = set(get_notifications())
|
|
32
32
|
|
|
33
33
|
for filename in os.listdir(launch_daemons):
|
|
34
|
-
if not filename.endswith(
|
|
34
|
+
if not filename.endswith(".plist"):
|
|
35
35
|
continue
|
|
36
36
|
|
|
37
37
|
filename = os.path.join(launch_daemons, filename)
|
|
38
38
|
try:
|
|
39
|
-
with open(filename,
|
|
39
|
+
with open(filename, "rb") as f:
|
|
40
40
|
plist = plistlib.load(f)
|
|
41
41
|
except Exception:
|
|
42
|
-
logging.exception(f
|
|
42
|
+
logging.exception(f"error parsing: {filename}")
|
|
43
43
|
continue
|
|
44
44
|
|
|
45
|
-
launch_events = plist.get(
|
|
46
|
-
notifyd_matching = launch_events.get(
|
|
45
|
+
launch_events = plist.get("LaunchEvents", {})
|
|
46
|
+
notifyd_matching = launch_events.get("com.apple.notifyd.matching", {})
|
|
47
47
|
|
|
48
48
|
for v in notifyd_matching.values():
|
|
49
49
|
if not isinstance(v, dict):
|
|
50
|
-
logging.error(f
|
|
50
|
+
logging.error(f"error parsing: {filename}")
|
|
51
51
|
continue
|
|
52
|
-
notification = v.get(
|
|
52
|
+
notification = v.get("Notification")
|
|
53
53
|
if notification is None:
|
|
54
54
|
continue
|
|
55
55
|
|
|
56
56
|
if notification not in notifications:
|
|
57
|
-
logging.info(f
|
|
57
|
+
logging.info(f"adding notification: {notification}")
|
|
58
58
|
notifications.add(notification)
|
|
59
59
|
|
|
60
60
|
save_notifications(list(notifications))
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
if __name__ ==
|
|
63
|
+
if __name__ == "__main__":
|
|
64
64
|
coloredlogs.install(level=logging.DEBUG)
|
|
65
65
|
main()
|