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.

Files changed (143) hide show
  1. misc/plist_sniffer.py +15 -15
  2. misc/remotexpc_sniffer.py +29 -28
  3. pymobiledevice3/__main__.py +128 -102
  4. pymobiledevice3/_version.py +2 -2
  5. pymobiledevice3/bonjour.py +26 -49
  6. pymobiledevice3/ca.py +32 -24
  7. pymobiledevice3/cli/activation.py +7 -7
  8. pymobiledevice3/cli/afc.py +19 -19
  9. pymobiledevice3/cli/amfi.py +4 -4
  10. pymobiledevice3/cli/apps.py +51 -39
  11. pymobiledevice3/cli/backup.py +58 -32
  12. pymobiledevice3/cli/bonjour.py +25 -18
  13. pymobiledevice3/cli/cli_common.py +112 -81
  14. pymobiledevice3/cli/companion_proxy.py +4 -4
  15. pymobiledevice3/cli/completions.py +10 -10
  16. pymobiledevice3/cli/crash.py +37 -31
  17. pymobiledevice3/cli/developer.py +602 -520
  18. pymobiledevice3/cli/diagnostics.py +38 -33
  19. pymobiledevice3/cli/lockdown.py +79 -74
  20. pymobiledevice3/cli/mounter.py +85 -68
  21. pymobiledevice3/cli/notification.py +10 -10
  22. pymobiledevice3/cli/pcap.py +19 -14
  23. pymobiledevice3/cli/power_assertion.py +12 -10
  24. pymobiledevice3/cli/processes.py +10 -10
  25. pymobiledevice3/cli/profile.py +88 -77
  26. pymobiledevice3/cli/provision.py +17 -17
  27. pymobiledevice3/cli/remote.py +186 -110
  28. pymobiledevice3/cli/restore.py +43 -40
  29. pymobiledevice3/cli/springboard.py +30 -28
  30. pymobiledevice3/cli/syslog.py +85 -58
  31. pymobiledevice3/cli/usbmux.py +21 -20
  32. pymobiledevice3/cli/version.py +3 -2
  33. pymobiledevice3/cli/webinspector.py +157 -79
  34. pymobiledevice3/common.py +1 -1
  35. pymobiledevice3/exceptions.py +154 -60
  36. pymobiledevice3/irecv.py +49 -53
  37. pymobiledevice3/irecv_devices.py +1489 -492
  38. pymobiledevice3/lockdown.py +394 -241
  39. pymobiledevice3/lockdown_service_provider.py +5 -7
  40. pymobiledevice3/osu/os_utils.py +18 -9
  41. pymobiledevice3/osu/posix_util.py +28 -15
  42. pymobiledevice3/osu/win_util.py +14 -8
  43. pymobiledevice3/pair_records.py +19 -19
  44. pymobiledevice3/remote/common.py +4 -4
  45. pymobiledevice3/remote/core_device/app_service.py +94 -67
  46. pymobiledevice3/remote/core_device/core_device_service.py +17 -14
  47. pymobiledevice3/remote/core_device/device_info.py +5 -5
  48. pymobiledevice3/remote/core_device/diagnostics_service.py +10 -8
  49. pymobiledevice3/remote/core_device/file_service.py +47 -33
  50. pymobiledevice3/remote/remote_service_discovery.py +53 -35
  51. pymobiledevice3/remote/remotexpc.py +62 -41
  52. pymobiledevice3/remote/tunnel_service.py +371 -293
  53. pymobiledevice3/remote/utils.py +12 -11
  54. pymobiledevice3/remote/xpc_message.py +145 -125
  55. pymobiledevice3/resources/dsc_uuid_map.py +19 -19
  56. pymobiledevice3/resources/firmware_notifications.py +16 -16
  57. pymobiledevice3/restore/asr.py +27 -27
  58. pymobiledevice3/restore/base_restore.py +90 -47
  59. pymobiledevice3/restore/consts.py +87 -66
  60. pymobiledevice3/restore/device.py +11 -11
  61. pymobiledevice3/restore/fdr.py +46 -46
  62. pymobiledevice3/restore/ftab.py +19 -19
  63. pymobiledevice3/restore/img4.py +130 -133
  64. pymobiledevice3/restore/mbn.py +35 -54
  65. pymobiledevice3/restore/recovery.py +125 -135
  66. pymobiledevice3/restore/restore.py +524 -523
  67. pymobiledevice3/restore/restore_options.py +122 -115
  68. pymobiledevice3/restore/restored_client.py +25 -22
  69. pymobiledevice3/restore/tss.py +378 -270
  70. pymobiledevice3/service_connection.py +50 -46
  71. pymobiledevice3/services/accessibilityaudit.py +136 -126
  72. pymobiledevice3/services/afc.py +350 -291
  73. pymobiledevice3/services/amfi.py +21 -18
  74. pymobiledevice3/services/companion.py +23 -19
  75. pymobiledevice3/services/crash_reports.py +60 -46
  76. pymobiledevice3/services/debugserver_applist.py +3 -3
  77. pymobiledevice3/services/device_arbitration.py +8 -8
  78. pymobiledevice3/services/device_link.py +55 -47
  79. pymobiledevice3/services/diagnostics.py +971 -968
  80. pymobiledevice3/services/dtfetchsymbols.py +8 -8
  81. pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
  82. pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
  83. pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
  84. pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
  85. pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
  86. pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
  87. pymobiledevice3/services/dvt/instruments/device_info.py +11 -11
  88. pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
  89. pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
  90. pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
  91. pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
  92. pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
  93. pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
  94. pymobiledevice3/services/dvt/instruments/process_control.py +25 -10
  95. pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
  96. pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
  97. pymobiledevice3/services/dvt/testmanaged/xcuitest.py +40 -50
  98. pymobiledevice3/services/file_relay.py +10 -10
  99. pymobiledevice3/services/heartbeat.py +8 -7
  100. pymobiledevice3/services/house_arrest.py +12 -15
  101. pymobiledevice3/services/installation_proxy.py +119 -100
  102. pymobiledevice3/services/lockdown_service.py +12 -5
  103. pymobiledevice3/services/misagent.py +22 -19
  104. pymobiledevice3/services/mobile_activation.py +84 -72
  105. pymobiledevice3/services/mobile_config.py +330 -301
  106. pymobiledevice3/services/mobile_image_mounter.py +137 -116
  107. pymobiledevice3/services/mobilebackup2.py +188 -150
  108. pymobiledevice3/services/notification_proxy.py +11 -11
  109. pymobiledevice3/services/os_trace.py +69 -51
  110. pymobiledevice3/services/pcapd.py +306 -306
  111. pymobiledevice3/services/power_assertion.py +10 -9
  112. pymobiledevice3/services/preboard.py +4 -4
  113. pymobiledevice3/services/remote_fetch_symbols.py +16 -14
  114. pymobiledevice3/services/remote_server.py +176 -146
  115. pymobiledevice3/services/restore_service.py +16 -16
  116. pymobiledevice3/services/screenshot.py +13 -10
  117. pymobiledevice3/services/simulate_location.py +7 -7
  118. pymobiledevice3/services/springboard.py +15 -15
  119. pymobiledevice3/services/syslog.py +5 -5
  120. pymobiledevice3/services/web_protocol/alert.py +3 -3
  121. pymobiledevice3/services/web_protocol/automation_session.py +180 -176
  122. pymobiledevice3/services/web_protocol/cdp_screencast.py +44 -36
  123. pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
  124. pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
  125. pymobiledevice3/services/web_protocol/driver.py +47 -45
  126. pymobiledevice3/services/web_protocol/element.py +74 -63
  127. pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
  128. pymobiledevice3/services/web_protocol/selenium_api.py +2 -2
  129. pymobiledevice3/services/web_protocol/session_protocol.py +15 -10
  130. pymobiledevice3/services/web_protocol/switch_to.py +11 -12
  131. pymobiledevice3/services/webinspector.py +127 -116
  132. pymobiledevice3/tcp_forwarder.py +35 -22
  133. pymobiledevice3/tunneld/api.py +20 -15
  134. pymobiledevice3/tunneld/server.py +212 -133
  135. pymobiledevice3/usbmux.py +183 -138
  136. pymobiledevice3/utils.py +14 -11
  137. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/METADATA +1 -1
  138. pymobiledevice3-5.0.2.dist-info/RECORD +173 -0
  139. pymobiledevice3-5.0.0.dist-info/RECORD +0 -173
  140. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/WHEEL +0 -0
  141. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/entry_points.txt +0 -0
  142. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/licenses/LICENSE +0 -0
  143. {pymobiledevice3-5.0.0.dist-info → pymobiledevice3-5.0.2.dist-info}/top_level.txt +0 -0
@@ -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 = '/usr/libexec/remoted'
12
+ REMOTED_PATH = "/usr/libexec/remoted"
13
13
 
14
14
 
15
- async def get_rsds(bonjour_timeout: float = DEFAULT_BONJOUR_TIMEOUT, udid: Optional[str] = None) -> \
16
- list[RemoteServiceDiscoveryService]:
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() != 'Darwin':
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() == 'stopped':
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() != 'Darwin':
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() == 'running':
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 Aligned, Array, Bytes, Const, CString, Default, Double, Enum, ExprAdapter, FlagsEnum, \
7
- GreedyBytes, Hex, If, Int32ul, Int64sl, Int64ul, LazyBound
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
- from construct import Pass, Prefixed, Probe, Struct, Switch, this
10
-
11
- XpcMessageType = Enum(Hex(Int32ul),
12
- NULL=0x00001000,
13
- BOOL=0x00002000,
14
- INT64=0x00003000,
15
- UINT64=0x00004000,
16
- DOUBLE=0x00005000,
17
- POINTER=0x00006000,
18
- DATE=0x00007000,
19
- DATA=0x00008000,
20
- STRING=0x00009000,
21
- UUID=0x0000a000,
22
- FD=0x0000b000,
23
- SHMEM=0x0000c000,
24
- MACH_SEND=0x0000d000,
25
- ARRAY=0x0000e000,
26
- DICTIONARY=0x0000f000,
27
- ERROR=0x00010000,
28
- CONNECTION=0x00011000,
29
- ENDPOINT=0x00012000,
30
- SERIALIZER=0x00013000,
31
- PIPE=0x00014000,
32
- MACH_RECV=0x00015000,
33
- BUNDLE=0x00016000,
34
- SERVICE=0x00017000,
35
- SERVICE_INSTANCE=0x00018000,
36
- ACTIVITY=0x00019000,
37
- FILE_TRANSFER=0x0001a000,
38
- )
39
- XpcFlags = FlagsEnum(Hex(Int32ul),
40
- ALWAYS_SET=0x00000001,
41
- PING=0x00000002,
42
- DATA_PRESENT=0x00000100,
43
- WANTING_REPLY=0x00010000,
44
- REPLY=0x00020000,
45
- FILE_TX_STREAM_REQUEST=0x00100000,
46
- FILE_TX_STREAM_RESPONSE=0x00200000,
47
- INIT_HANDSHAKE=0x00400000,
48
- )
49
- AlignedString = Aligned(4, CString('utf8'))
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('utf8')))
82
+ XpcString = Aligned(4, Prefixed(Int32ul, CString("utf8")))
59
83
  XpcUuid = Bytes(16)
60
84
  XpcFd = Int32ul
61
- XpcShmem = Struct('length' / Int32ul, Int32ul)
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
- 'key' / AlignedString,
67
- 'value' / LazyBound(lambda: XpcObject),
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
- 'msg_id' / Int64ul,
75
- 'data' / LazyBound(lambda: XpcObject),
99
+ "msg_id" / Int64ul,
100
+ "data" / LazyBound(lambda: XpcObject),
76
101
  )
77
102
  XpcObject = Struct(
78
- 'type' / XpcMessageType,
79
- 'data' / Switch(this.type, {
80
- XpcMessageType.DICTIONARY: XpcDictionary,
81
- XpcMessageType.STRING: XpcString,
82
- XpcMessageType.INT64: XpcInt64,
83
- XpcMessageType.UINT64: XpcUInt64,
84
- XpcMessageType.DOUBLE: XpcDouble,
85
- XpcMessageType.BOOL: XpcBool,
86
- XpcMessageType.NULL: XpcNull,
87
- XpcMessageType.UUID: XpcUuid,
88
- XpcMessageType.POINTER: XpcPointer,
89
- XpcMessageType.DATE: XpcDate,
90
- XpcMessageType.DATA: XpcData,
91
- XpcMessageType.FD: XpcFd,
92
- XpcMessageType.SHMEM: XpcShmem,
93
- XpcMessageType.ARRAY: XpcArray,
94
- XpcMessageType.FILE_TRANSFER: XpcFileTransfer,
95
- }, default=Probe(lookahead=1000)),
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
- 'magic' / Hex(Const(0x42133742, Int32ul)),
99
- 'protocol_version' / Hex(Const(0x00000005, Int32ul)),
100
- 'obj' / XpcObject,
128
+ "magic" / Hex(Const(0x42133742, Int32ul)),
129
+ "protocol_version" / Hex(Const(0x00000005, Int32ul)),
130
+ "obj" / XpcObject,
101
131
  )
102
132
  XpcWrapper = Struct(
103
- 'magic' / Hex(Const(0x29b00b92, Int32ul)),
104
- 'flags' / Default(XpcFlags, XpcFlags.ALWAYS_SET),
105
- 'message' / Prefixed(
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
- 'message_id' / Hex(Default(Int64ul, 0)),
109
- 'payload' / ConstructOptional(XpcPayload),
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)['s'])
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'deserialize error: {xpc_object}')
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 = {'key': key, 'value': _build_xpc_object(value)}
248
+ entry = {"key": key, "value": _build_xpc_object(value)}
223
249
  entries.append(entry)
224
250
  return {
225
- 'type': XpcMessageType.DICTIONARY,
226
- 'data': {
227
- 'count': len(entries),
228
- 'entries': entries,
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
- 'type': XpcMessageType.BOOL,
236
- 'data': payload,
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
- 'type': XpcMessageType.STRING,
243
- 'data': payload,
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
- 'type': XpcMessageType.DATA,
250
- 'data': payload,
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
- 'type': XpcMessageType.DOUBLE,
257
- 'data': payload,
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
- 'type': XpcMessageType.UUID,
264
- 'data': payload.bytes,
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
- 'type': XpcMessageType.NULL,
271
- 'data': None,
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
- 'type': XpcMessageType.UINT64,
278
- 'data': payload,
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
- 'type': XpcMessageType.INT64,
285
- 'data': payload,
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
- 'XpcUInt64Type': _build_xpc_uint64,
302
- 'XpcInt64Type': _build_xpc_int64,
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'unrecognized type for: {payload} {type(payload)}')
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'\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')
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('dyld_uuid', type=click.UUID)
38
- @click.argument('dsc', type=click.File('rb'))
39
- @click.option('-f', '--force', is_flag=True)
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('invalid dsc file')
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'dsc {dsc_uuid} is already found in dsc_uuid_map')
57
+ logger.warning(f"dsc {dsc_uuid} is already found in dsc_uuid_map")
58
58
  if not force:
59
- logger.info('exiting. use --force to force update')
59
+ logger.info("exiting. use --force to force update")
60
60
  return
61
61
  else:
62
- uuid_map[dsc_uuid] = {str(dyld_uuid): '/usr/lib/dyld'}
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 '/' not in filename:
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'offset: 0x{i:x} image: {filename} uuid: {uuid}')
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, 'w') as f:
95
+ with open(MAP_FILENAME, "w") as f:
96
96
  json.dump(uuid_map, f, indent=4)
97
97
 
98
98
 
99
- if __name__ == '__main__':
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__), 'notifications.txt')
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, 'rb') as f:
13
- return f.read().decode().split('\n')
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, 'wb') as f:
17
+ with open(NOTIFICATIONS_FILENAME, "wb") as f:
18
18
  notifications.sort()
19
- f.write('\n'.join(notifications).encode())
19
+ f.write("\n".join(notifications).encode())
20
20
 
21
21
 
22
22
  @click.command()
23
- @click.argument('root_fs', type=click.Path(dir_okay=True, file_okay=False, exists=True))
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, 'System', 'Library', 'LaunchDaemons')
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('.plist'):
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, 'rb') as f:
39
+ with open(filename, "rb") as f:
40
40
  plist = plistlib.load(f)
41
41
  except Exception:
42
- logging.exception(f'error parsing: {filename}')
42
+ logging.exception(f"error parsing: {filename}")
43
43
  continue
44
44
 
45
- launch_events = plist.get('LaunchEvents', {})
46
- notifyd_matching = launch_events.get('com.apple.notifyd.matching', {})
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'error parsing: {filename}')
50
+ logging.error(f"error parsing: {filename}")
51
51
  continue
52
- notification = v.get('Notification')
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'adding notification: {notification}')
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__ == '__main__':
63
+ if __name__ == "__main__":
64
64
  coloredlogs.install(level=logging.DEBUG)
65
65
  main()