moriarty-project 0.1.20__py3-none-any.whl → 0.1.22__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.
- moriarty/__init__.py +1 -1
- moriarty/modules/wifippler/__init__.py +35 -8
- moriarty/modules/wifippler/cli/__init__.py +8 -0
- moriarty/modules/wifippler/cli/commands.py +123 -0
- moriarty/modules/wifippler/core/__init__.py +39 -25
- moriarty/modules/wifippler/core/attacks/__init__.py +136 -9
- moriarty/modules/wifippler/core/attacks/deauth.py +81 -159
- moriarty/modules/wifippler/core/models/network.py +24 -0
- moriarty/modules/wifippler/core/scanner.py +17 -15
- moriarty/modules/wifippler/core/utils/__init__.py +63 -61
- moriarty/modules/wifippler/core/utils/exec.py +182 -0
- moriarty/modules/wifippler/core/utils/network.py +262 -0
- moriarty/modules/wifippler/core/utils/system.py +153 -0
- {moriarty_project-0.1.20.dist-info → moriarty_project-0.1.22.dist-info}/METADATA +2 -2
- {moriarty_project-0.1.20.dist-info → moriarty_project-0.1.22.dist-info}/RECORD +17 -13
- moriarty/modules/wifippler/core/utils.py +0 -851
- {moriarty_project-0.1.20.dist-info → moriarty_project-0.1.22.dist-info}/WHEEL +0 -0
- {moriarty_project-0.1.20.dist-info → moriarty_project-0.1.22.dist-info}/entry_points.txt +0 -0
@@ -1,851 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Utility functions for WifiPPLER.
|
3
|
-
|
4
|
-
This module provides various utility functions used throughout the WifiPPLER
|
5
|
-
package, including network interface management, dependency checking, and
|
6
|
-
other helper functions.
|
7
|
-
"""
|
8
|
-
import os
|
9
|
-
import re
|
10
|
-
import sys
|
11
|
-
import time
|
12
|
-
import logging
|
13
|
-
import subprocess
|
14
|
-
import shutil
|
15
|
-
import fcntl
|
16
|
-
import struct
|
17
|
-
import array
|
18
|
-
import socket
|
19
|
-
import platform
|
20
|
-
from typing import List, Dict, Tuple, Optional, Union, Any, Callable, Set
|
21
|
-
from dataclasses import asdict
|
22
|
-
|
23
|
-
# Configure logging
|
24
|
-
logging.basicConfig(level=logging.INFO)
|
25
|
-
logger = logging.getLogger(__name__)
|
26
|
-
|
27
|
-
# Constants
|
28
|
-
WIRELESS_EXT = 0x8B01 # SIOCGIWNAME
|
29
|
-
SIOCGIFHWADDR = 0x8927 # Get hardware address
|
30
|
-
SIOCGIFADDR = 0x8915 # Get IP address
|
31
|
-
SIOCGIFNETMASK = 0x891B # Get netmask
|
32
|
-
SIOCGIFBRDADDR = 0x8919 # Get broadcast address
|
33
|
-
SIOCGIFMTU = 0x8921 # Get MTU
|
34
|
-
SIOCGIFINDEX = 0x8933 # Get interface index
|
35
|
-
SIOCGIFNAME = 0x8910 # Get interface name
|
36
|
-
SIOCGIFFLAGS = 0x8913 # Get interface flags
|
37
|
-
SIOCSIFFLAGS = 0x8914 # Set interface flags
|
38
|
-
|
39
|
-
# Platform-specific constants
|
40
|
-
IS_LINUX = platform.system() == 'Linux'
|
41
|
-
IS_MAC = platform.system() == 'Darwin'
|
42
|
-
IS_WINDOWS = platform.system() == 'Windows'
|
43
|
-
|
44
|
-
# Network interface flags
|
45
|
-
IFF_UP = 0x1
|
46
|
-
IFF_BROADCAST = 0x2
|
47
|
-
IFF_DEBUG = 0x4
|
48
|
-
IFF_LOOPBACK = 0x8
|
49
|
-
IFF_POINTOPOINT = 0x10
|
50
|
-
IFF_NOTRAILERS = 0x20
|
51
|
-
IFF_RUNNING = 0x40
|
52
|
-
IFF_NOARP = 0x80
|
53
|
-
IFF_PROMISC = 0x100
|
54
|
-
IFF_ALLMULTI = 0x200
|
55
|
-
IFF_MASTER = 0x400
|
56
|
-
IFF_SLAVE = 0x800
|
57
|
-
IFF_MULTICAST = 0x1000
|
58
|
-
IFF_PORTSEL = 0x2000
|
59
|
-
IFF_AUTOMEDIA = 0x4000
|
60
|
-
IFF_DYNAMIC = 0x8000
|
61
|
-
|
62
|
-
def is_root() -> bool:
|
63
|
-
"""
|
64
|
-
Check if the script is running with root privileges.
|
65
|
-
|
66
|
-
Returns:
|
67
|
-
bool: True if running as root, False otherwise
|
68
|
-
"""
|
69
|
-
return os.geteuid() == 0
|
70
|
-
|
71
|
-
def check_dependencies() -> List[str]:
|
72
|
-
"""
|
73
|
-
Check for required system dependencies.
|
74
|
-
|
75
|
-
Returns:
|
76
|
-
List[str]: List of missing dependencies
|
77
|
-
"""
|
78
|
-
required = [
|
79
|
-
'iwconfig', 'iw', 'ifconfig', 'aircrack-ng', 'airodump-ng',
|
80
|
-
'aireplay-ng', 'airmon-ng', 'wash', 'reaver', 'bully', 'hcxdumptool',
|
81
|
-
'hcxpcapngtool', 'hashcat', 'tshark', 'macchanger', 'rfkill'
|
82
|
-
]
|
83
|
-
|
84
|
-
missing = []
|
85
|
-
for cmd in required:
|
86
|
-
if not command_exists(cmd):
|
87
|
-
missing.append(cmd)
|
88
|
-
|
89
|
-
return missing
|
90
|
-
|
91
|
-
def command_exists(cmd: str) -> bool:
|
92
|
-
"""
|
93
|
-
Check if a command exists in the system PATH.
|
94
|
-
|
95
|
-
Args:
|
96
|
-
cmd: Command to check
|
97
|
-
|
98
|
-
Returns:
|
99
|
-
bool: True if command exists, False otherwise
|
100
|
-
"""
|
101
|
-
return shutil.which(cmd) is not None
|
102
|
-
|
103
|
-
def get_network_interfaces() -> List[Dict[str, Any]]:
|
104
|
-
"""
|
105
|
-
Get a list of all network interfaces.
|
106
|
-
|
107
|
-
Returns:
|
108
|
-
List[Dict[str, Any]]: List of interfaces with their properties
|
109
|
-
"""
|
110
|
-
interfaces = []
|
111
|
-
|
112
|
-
if IS_LINUX:
|
113
|
-
# Linux implementation using /sys/class/net
|
114
|
-
net_path = '/sys/class/net'
|
115
|
-
if os.path.exists(net_path):
|
116
|
-
for ifname in os.listdir(net_path):
|
117
|
-
if ifname == 'lo':
|
118
|
-
continue
|
119
|
-
|
120
|
-
iface = {
|
121
|
-
'name': ifname,
|
122
|
-
'wireless': os.path.exists(f"{net_path}/{ifname}/wireless"),
|
123
|
-
'state': 'down',
|
124
|
-
'mac': get_interface_mac(ifname),
|
125
|
-
'ip': get_interface_ip(ifname),
|
126
|
-
'netmask': get_interface_netmask(ifname),
|
127
|
-
'broadcast': get_interface_broadcast(ifname),
|
128
|
-
'mtu': get_interface_mtu(ifname)
|
129
|
-
}
|
130
|
-
|
131
|
-
# Check if interface is up
|
132
|
-
try:
|
133
|
-
with open(f"{net_path}/{ifname}/operstate", 'r') as f:
|
134
|
-
state = f.read().strip()
|
135
|
-
iface['state'] = state if state in ['up', 'down'] else 'unknown'
|
136
|
-
except:
|
137
|
-
pass
|
138
|
-
|
139
|
-
interfaces.append(iface)
|
140
|
-
|
141
|
-
elif IS_MAC:
|
142
|
-
# macOS implementation using ifconfig
|
143
|
-
try:
|
144
|
-
result = subprocess.run(['ifconfig', '-l'], capture_output=True, text=True)
|
145
|
-
if result.returncode == 0:
|
146
|
-
for ifname in result.stdout.strip().split():
|
147
|
-
if ifname == 'lo0':
|
148
|
-
continue
|
149
|
-
|
150
|
-
iface = {
|
151
|
-
'name': ifname,
|
152
|
-
'wireless': ifname.startswith(('en', 'wl')), # Approximate
|
153
|
-
'state': 'unknown',
|
154
|
-
'mac': get_interface_mac(ifname),
|
155
|
-
'ip': get_interface_ip(ifname),
|
156
|
-
'netmask': get_interface_netmask(ifname),
|
157
|
-
'broadcast': get_interface_broadcast(ifname),
|
158
|
-
'mtu': get_interface_mtu(ifname)
|
159
|
-
}
|
160
|
-
|
161
|
-
# Check if interface is up
|
162
|
-
result = subprocess.run(['ifconfig', ifname], capture_output=True, text=True)
|
163
|
-
if 'status: active' in result.stdout or 'UP' in result.stdout:
|
164
|
-
iface['state'] = 'up'
|
165
|
-
else:
|
166
|
-
iface['state'] = 'down'
|
167
|
-
|
168
|
-
interfaces.append(iface)
|
169
|
-
except Exception as e:
|
170
|
-
logger.error(f"Error getting network interfaces: {e}")
|
171
|
-
|
172
|
-
return interfaces
|
173
|
-
|
174
|
-
def get_wireless_interfaces() -> List[Dict[str, Any]]:
|
175
|
-
"""
|
176
|
-
Obtém uma lista de interfaces de rede sem fio.
|
177
|
-
|
178
|
-
Returns:
|
179
|
-
List[Dict[str, Any]]: Lista de interfaces sem fio com suas propriedades.
|
180
|
-
Cada dicionário contém as seguintes chaves:
|
181
|
-
- name: Nome da interface
|
182
|
-
- mac: Endereço MAC da interface
|
183
|
-
- ip: Endereço IP da interface
|
184
|
-
- wireless: True se for uma interface sem fio
|
185
|
-
- up: True se a interface estiver ativa
|
186
|
-
- mtu: Tamanho máximo de unidade de transmissão
|
187
|
-
|
188
|
-
Raises:
|
189
|
-
RuntimeError: Se ocorrer um erro ao obter as interfaces de rede
|
190
|
-
"""
|
191
|
-
try:
|
192
|
-
interfaces = get_network_interfaces()
|
193
|
-
if not interfaces:
|
194
|
-
logger.warning("Nenhuma interface de rede encontrada")
|
195
|
-
return []
|
196
|
-
|
197
|
-
wireless_interfaces = []
|
198
|
-
for iface in interfaces:
|
199
|
-
try:
|
200
|
-
if iface.get('wireless', False):
|
201
|
-
# Adiciona informações adicionais à interface
|
202
|
-
iface_info = {
|
203
|
-
'name': iface.get('name', ''),
|
204
|
-
'mac': iface.get('mac', ''),
|
205
|
-
'ip': iface.get('ip', ''),
|
206
|
-
'wireless': True,
|
207
|
-
'up': iface.get('up', False),
|
208
|
-
'mtu': iface.get('mtu', 0)
|
209
|
-
}
|
210
|
-
wireless_interfaces.append(iface_info)
|
211
|
-
except Exception as e:
|
212
|
-
logger.error(f"Erro ao processar interface {iface.get('name', 'desconhecida')}: {e}")
|
213
|
-
continue
|
214
|
-
|
215
|
-
if not wireless_interfaces:
|
216
|
-
logger.warning("Nenhuma interface sem fio encontrada")
|
217
|
-
|
218
|
-
return wireless_interfaces
|
219
|
-
|
220
|
-
except Exception as e:
|
221
|
-
error_msg = f"Erro ao obter interfaces sem fio: {e}"
|
222
|
-
logger.error(error_msg)
|
223
|
-
raise RuntimeError(error_msg) from e
|
224
|
-
|
225
|
-
def get_interface_mac(interface: str) -> Optional[str]:
|
226
|
-
"""
|
227
|
-
Get the MAC address of a network interface.
|
228
|
-
|
229
|
-
Args:
|
230
|
-
interface: Interface name
|
231
|
-
|
232
|
-
Returns:
|
233
|
-
Optional[str]: MAC address or None if not found
|
234
|
-
"""
|
235
|
-
try:
|
236
|
-
if IS_LINUX or IS_MAC:
|
237
|
-
with open(f"/sys/class/net/{interface}/address") as f:
|
238
|
-
return f.read().strip()
|
239
|
-
except:
|
240
|
-
pass
|
241
|
-
|
242
|
-
try:
|
243
|
-
if IS_LINUX:
|
244
|
-
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
245
|
-
info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', interface[:15].encode()))
|
246
|
-
return ':'.join(f'{b:02x}' for b in info[18:24])
|
247
|
-
elif IS_MAC:
|
248
|
-
result = subprocess.run(['ifconfig', interface], capture_output=True, text=True)
|
249
|
-
match = re.search(r'([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})', result.stdout)
|
250
|
-
if match:
|
251
|
-
return match.group(0).lower()
|
252
|
-
except:
|
253
|
-
pass
|
254
|
-
|
255
|
-
return None
|
256
|
-
|
257
|
-
def get_interface_ip(interface: str) -> Optional[str]:
|
258
|
-
"""
|
259
|
-
Get the IP address of a network interface.
|
260
|
-
|
261
|
-
Args:
|
262
|
-
interface: Interface name
|
263
|
-
|
264
|
-
Returns:
|
265
|
-
Optional[str]: IP address or None if not found
|
266
|
-
"""
|
267
|
-
try:
|
268
|
-
if IS_LINUX or IS_MAC:
|
269
|
-
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
270
|
-
try:
|
271
|
-
# Works for both IPv4 and IPv6
|
272
|
-
return socket.inet_ntoa(fcntl.ioctl(
|
273
|
-
s.fileno(),
|
274
|
-
0x8915, # SIOCGIFADDR
|
275
|
-
struct.pack('256s', interface[:15].encode())
|
276
|
-
)[20:24])
|
277
|
-
except:
|
278
|
-
pass
|
279
|
-
except:
|
280
|
-
pass
|
281
|
-
|
282
|
-
try:
|
283
|
-
if IS_MAC:
|
284
|
-
result = subprocess.run(['ifconfig', interface, 'inet'], capture_output=True, text=True)
|
285
|
-
match = re.search(r'inet (\d+\.\d+\.\d+\.\d+)', result.stdout)
|
286
|
-
if match:
|
287
|
-
return match.group(1)
|
288
|
-
except:
|
289
|
-
pass
|
290
|
-
|
291
|
-
return None
|
292
|
-
|
293
|
-
def get_interface_netmask(interface: str) -> Optional[str]:
|
294
|
-
"""
|
295
|
-
Get the netmask of a network interface.
|
296
|
-
|
297
|
-
Args:
|
298
|
-
interface: Interface name
|
299
|
-
|
300
|
-
Returns:
|
301
|
-
Optional[str]: Netmask or None if not found
|
302
|
-
"""
|
303
|
-
try:
|
304
|
-
if IS_LINUX or IS_MAC:
|
305
|
-
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
306
|
-
try:
|
307
|
-
netmask = socket.inet_ntoa(fcntl.ioctl(
|
308
|
-
s.fileno(),
|
309
|
-
0x891B, # SIOCGIFNETMASK
|
310
|
-
struct.pack('256s', interface[:15].encode())
|
311
|
-
)[20:24])
|
312
|
-
return netmask
|
313
|
-
except:
|
314
|
-
pass
|
315
|
-
except:
|
316
|
-
pass
|
317
|
-
|
318
|
-
try:
|
319
|
-
if IS_MAC:
|
320
|
-
result = subprocess.run(['ifconfig', interface, 'inet'], capture_output=True, text=True)
|
321
|
-
match = re.search(r'netmask (0x[0-9a-fA-F]+)', result.stdout)
|
322
|
-
if match:
|
323
|
-
# Convert hex netmask to dotted decimal
|
324
|
-
netmask_hex = match.group(1)
|
325
|
-
netmask_int = int(netmask_hex, 16)
|
326
|
-
return socket.inet_ntoa(struct.pack('>I', netmask_int))
|
327
|
-
except:
|
328
|
-
pass
|
329
|
-
|
330
|
-
return None
|
331
|
-
|
332
|
-
def get_interface_broadcast(interface: str) -> Optional[str]:
|
333
|
-
"""
|
334
|
-
Get the broadcast address of a network interface.
|
335
|
-
|
336
|
-
Args:
|
337
|
-
interface: Interface name
|
338
|
-
|
339
|
-
Returns:
|
340
|
-
Optional[str]: Broadcast address or None if not found
|
341
|
-
"""
|
342
|
-
try:
|
343
|
-
if IS_LINUX or IS_MAC:
|
344
|
-
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
345
|
-
try:
|
346
|
-
broadcast = socket.inet_ntoa(fcntl.ioctl(
|
347
|
-
s.fileno(),
|
348
|
-
0x8919, # SIOCGIFBRDADDR
|
349
|
-
struct.pack('256s', interface[:15].encode())
|
350
|
-
)[20:24])
|
351
|
-
return broadcast
|
352
|
-
except:
|
353
|
-
pass
|
354
|
-
except:
|
355
|
-
pass
|
356
|
-
|
357
|
-
try:
|
358
|
-
if IS_MAC:
|
359
|
-
result = subprocess.run(['ifconfig', interface, 'inet'], capture_output=True, text=True)
|
360
|
-
match = re.search(r'broadcast (\d+\.\d+\.\d+\.\d+)', result.stdout)
|
361
|
-
if match:
|
362
|
-
return match.group(1)
|
363
|
-
except:
|
364
|
-
pass
|
365
|
-
|
366
|
-
return None
|
367
|
-
|
368
|
-
def get_interface_mtu(interface: str) -> Optional[int]:
|
369
|
-
"""
|
370
|
-
Get the MTU of a network interface.
|
371
|
-
|
372
|
-
Args:
|
373
|
-
interface: Interface name
|
374
|
-
|
375
|
-
Returns:
|
376
|
-
Optional[int]: MTU or None if not found
|
377
|
-
"""
|
378
|
-
try:
|
379
|
-
if IS_LINUX:
|
380
|
-
with open(f"/sys/class/net/{interface}/mtu", 'r') as f:
|
381
|
-
return int(f.read().strip())
|
382
|
-
elif IS_MAC:
|
383
|
-
result = subprocess.run(['ifconfig', interface], capture_output=True, text=True)
|
384
|
-
match = re.search(r'mtu (\d+)', result.stdout)
|
385
|
-
if match:
|
386
|
-
return int(match.group(1))
|
387
|
-
except:
|
388
|
-
pass
|
389
|
-
|
390
|
-
return None
|
391
|
-
|
392
|
-
def set_monitor_mode(interface: str, channel: int = None) -> bool:
|
393
|
-
"""
|
394
|
-
Set a wireless interface to monitor mode.
|
395
|
-
|
396
|
-
Args:
|
397
|
-
interface: Interface name
|
398
|
-
channel: Optional channel to set
|
399
|
-
|
400
|
-
Returns:
|
401
|
-
bool: True if successful, False otherwise
|
402
|
-
"""
|
403
|
-
if not is_root():
|
404
|
-
logger.error("Root privileges required to set monitor mode")
|
405
|
-
return False
|
406
|
-
|
407
|
-
try:
|
408
|
-
# Bring interface down
|
409
|
-
subprocess.run(['ip', 'link', 'set', interface, 'down'], check=True)
|
410
|
-
|
411
|
-
# Set monitor mode
|
412
|
-
subprocess.run(['iw', 'dev', interface, 'set', 'monitor', 'none'], check=True)
|
413
|
-
|
414
|
-
# Bring interface up
|
415
|
-
subprocess.run(['ip', 'link', 'set', interface, 'up'], check=True)
|
416
|
-
|
417
|
-
# Set channel if specified
|
418
|
-
if channel is not None:
|
419
|
-
subprocess.run(['iw', 'dev', interface, 'set', 'channel', str(channel)], check=True)
|
420
|
-
|
421
|
-
return True
|
422
|
-
except subprocess.CalledProcessError as e:
|
423
|
-
logger.error(f"Failed to set monitor mode: {e}")
|
424
|
-
return False
|
425
|
-
|
426
|
-
def set_managed_mode(interface: str) -> bool:
|
427
|
-
"""
|
428
|
-
Set a wireless interface to managed mode.
|
429
|
-
|
430
|
-
Args:
|
431
|
-
interface: Interface name
|
432
|
-
|
433
|
-
Returns:
|
434
|
-
bool: True if successful, False otherwise
|
435
|
-
"""
|
436
|
-
if not is_root():
|
437
|
-
logger.error("Root privileges required to set managed mode")
|
438
|
-
return False
|
439
|
-
|
440
|
-
try:
|
441
|
-
# Bring interface down
|
442
|
-
subprocess.run(['ip', 'link', 'set', interface, 'down'], check=True)
|
443
|
-
|
444
|
-
# Set managed mode
|
445
|
-
subprocess.run(['iw', 'dev', interface, 'set', 'type', 'managed'], check=True)
|
446
|
-
|
447
|
-
# Bring interface up
|
448
|
-
subprocess.run(['ip', 'link', 'set', interface, 'up'], check=True)
|
449
|
-
|
450
|
-
# Restart network manager
|
451
|
-
if command_exists('systemctl'):
|
452
|
-
subprocess.run(['systemctl', 'restart', 'NetworkManager'], check=False)
|
453
|
-
elif command_exists('service'):
|
454
|
-
subprocess.run(['service', 'network-manager', 'restart'], check=False)
|
455
|
-
|
456
|
-
return True
|
457
|
-
except subprocess.CalledProcessError as e:
|
458
|
-
logger.error(f"Failed to set managed mode: {e}")
|
459
|
-
return False
|
460
|
-
|
461
|
-
def get_monitor_interfaces() -> List[str]:
|
462
|
-
"""
|
463
|
-
Get a list of interfaces in monitor mode.
|
464
|
-
|
465
|
-
Returns:
|
466
|
-
List[str]: List of interface names in monitor mode
|
467
|
-
"""
|
468
|
-
interfaces = []
|
469
|
-
|
470
|
-
try:
|
471
|
-
if IS_LINUX:
|
472
|
-
# List all interfaces
|
473
|
-
for iface in os.listdir('/sys/class/net'):
|
474
|
-
if iface.startswith(('mon', 'wlan', 'wlp', 'wlo')):
|
475
|
-
# Check if in monitor mode
|
476
|
-
proc = subprocess.Popen(['iwconfig', iface],
|
477
|
-
stdout=subprocess.PIPE,
|
478
|
-
stderr=subprocess.PIPE)
|
479
|
-
out, _ = proc.communicate()
|
480
|
-
|
481
|
-
if b'Mode:Monitor' in out:
|
482
|
-
interfaces.append(iface)
|
483
|
-
elif IS_MAC:
|
484
|
-
# On macOS, interfaces in monitor mode typically start with 'mon'
|
485
|
-
result = subprocess.run(['ifconfig', '-l'], capture_output=True, text=True)
|
486
|
-
interfaces = [iface for iface in result.stdout.strip().split()
|
487
|
-
if iface.startswith('en') and 'monitor' in
|
488
|
-
subprocess.run(['ifconfig', iface], capture_output=True, text=True).stdout]
|
489
|
-
except Exception as e:
|
490
|
-
logger.error(f"Error getting monitor interfaces: {e}")
|
491
|
-
|
492
|
-
return interfaces
|
493
|
-
|
494
|
-
def get_interface_signal(interface: str) -> Optional[int]:
|
495
|
-
"""
|
496
|
-
Get the signal strength of a wireless interface in dBm.
|
497
|
-
|
498
|
-
Args:
|
499
|
-
interface: Interface name
|
500
|
-
|
501
|
-
Returns:
|
502
|
-
Optional[int]: Signal strength in dBm or None if not available
|
503
|
-
"""
|
504
|
-
try:
|
505
|
-
if IS_LINUX or IS_MAC:
|
506
|
-
proc = subprocess.Popen(['iwconfig', interface],
|
507
|
-
stdout=subprocess.PIPE,
|
508
|
-
stderr=subprocess.PIPE)
|
509
|
-
out, _ = proc.communicate()
|
510
|
-
|
511
|
-
# Look for signal level in the format "Signal level=-XX dBm"
|
512
|
-
match = re.search(r'Signal level=(-\d+)\s*dBm', out.decode('utf-8', 'ignore'))
|
513
|
-
if match:
|
514
|
-
return int(match.group(1))
|
515
|
-
except:
|
516
|
-
pass
|
517
|
-
|
518
|
-
return None
|
519
|
-
|
520
|
-
def get_interface_ssid(interface: str) -> Optional[str]:
|
521
|
-
"""
|
522
|
-
Get the SSID that a wireless interface is connected to.
|
523
|
-
|
524
|
-
Args:
|
525
|
-
interface: Interface name
|
526
|
-
|
527
|
-
Returns:
|
528
|
-
Optional[str]: SSID or None if not connected
|
529
|
-
"""
|
530
|
-
try:
|
531
|
-
if IS_LINUX:
|
532
|
-
proc = subprocess.Popen(['iwconfig', interface],
|
533
|
-
stdout=subprocess.PIPE,
|
534
|
-
stderr=subprocess.PIPE)
|
535
|
-
out, _ = proc.communicate()
|
536
|
-
|
537
|
-
# Look for ESSID in the format "ESSID:"MyWiFi""
|
538
|
-
match = re.search(r'ESSID:"([^"]+)"', out.decode('utf-8', 'ignore'))
|
539
|
-
if match and match.group(1) != 'off/any':
|
540
|
-
return match.group(1)
|
541
|
-
elif IS_MAC:
|
542
|
-
result = subprocess.run(['/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport', '-I'],
|
543
|
-
capture_output=True, text=True)
|
544
|
-
match = re.search(r' SSID: (.+)', result.stdout)
|
545
|
-
if match:
|
546
|
-
return match.group(1).strip()
|
547
|
-
except:
|
548
|
-
pass
|
549
|
-
|
550
|
-
return None
|
551
|
-
|
552
|
-
def get_interface_channel(interface: str) -> Optional[int]:
|
553
|
-
"""
|
554
|
-
Get the current channel of a wireless interface.
|
555
|
-
|
556
|
-
Args:
|
557
|
-
interface: Interface name
|
558
|
-
|
559
|
-
Returns:
|
560
|
-
Optional[int]: Channel number or None if not available
|
561
|
-
"""
|
562
|
-
try:
|
563
|
-
if IS_LINUX or IS_MAC:
|
564
|
-
proc = subprocess.Popen(['iwconfig', interface],
|
565
|
-
stdout=subprocess.PIPE,
|
566
|
-
stderr=subprocess.PIPE)
|
567
|
-
out, _ = proc.communicate()
|
568
|
-
|
569
|
-
# Look for channel in the format "Channel:XX"
|
570
|
-
match = re.search(r'Channel:(\d+)', out.decode('utf-8', 'ignore'))
|
571
|
-
if match:
|
572
|
-
return int(match.group(1))
|
573
|
-
except:
|
574
|
-
pass
|
575
|
-
|
576
|
-
return None
|
577
|
-
|
578
|
-
def get_interface_bitrate(interface: str) -> Optional[float]:
|
579
|
-
"""
|
580
|
-
Get the current bitrate of a wireless interface in Mbps.
|
581
|
-
|
582
|
-
Args:
|
583
|
-
interface: Interface name
|
584
|
-
|
585
|
-
Returns:
|
586
|
-
Optional[float]: Bitrate in Mbps or None if not available
|
587
|
-
"""
|
588
|
-
try:
|
589
|
-
if IS_LINUX or IS_MAC:
|
590
|
-
proc = subprocess.Popen(['iwconfig', interface],
|
591
|
-
stdout=subprocess.PIPE,
|
592
|
-
stderr=subprocess.PIPE)
|
593
|
-
out, _ = proc.communicate()
|
594
|
-
|
595
|
-
# Look for bit rate in the format "Bit Rate=XX Mb/s"
|
596
|
-
match = re.search(r'Bit Rate[:=](\d+(?:\.\d+)?)\s*Mb/s', out.decode('utf-8', 'ignore'))
|
597
|
-
if match:
|
598
|
-
return float(match.group(1))
|
599
|
-
except:
|
600
|
-
pass
|
601
|
-
|
602
|
-
return None
|
603
|
-
|
604
|
-
def randomize_mac(interface: str) -> bool:
|
605
|
-
"""
|
606
|
-
Randomize the MAC address of a network interface.
|
607
|
-
|
608
|
-
Args:
|
609
|
-
interface: Interface name
|
610
|
-
|
611
|
-
Returns:
|
612
|
-
bool: True if successful, False otherwise
|
613
|
-
"""
|
614
|
-
if not is_root():
|
615
|
-
logger.error("Root privileges required to change MAC address")
|
616
|
-
return False
|
617
|
-
|
618
|
-
try:
|
619
|
-
# Generate a random MAC address
|
620
|
-
import random
|
621
|
-
new_mac = ':'.join(['%02x' % random.randint(0x00, 0xff) for _ in range(6)])
|
622
|
-
|
623
|
-
# Bring interface down
|
624
|
-
subprocess.run(['ip', 'link', 'set', interface, 'down'], check=True)
|
625
|
-
|
626
|
-
# Set new MAC address
|
627
|
-
subprocess.run(['ip', 'link', 'set', 'dev', interface, 'address', new_mac], check=True)
|
628
|
-
|
629
|
-
# Bring interface up
|
630
|
-
subprocess.run(['ip', 'link', 'set', interface, 'up'], check=True)
|
631
|
-
|
632
|
-
return True
|
633
|
-
except subprocess.CalledProcessError as e:
|
634
|
-
logger.error(f"Failed to randomize MAC address: {e}")
|
635
|
-
return False
|
636
|
-
|
637
|
-
def run_command(cmd: Union[str, List[str]], capture_output: bool = False,
|
638
|
-
check: bool = True, **kwargs) -> subprocess.CompletedProcess:
|
639
|
-
"""
|
640
|
-
Run a shell command with error handling.
|
641
|
-
|
642
|
-
Args:
|
643
|
-
cmd: Command to run (string or list)
|
644
|
-
capture_output: Whether to capture stdout/stderr
|
645
|
-
check: Whether to raise an exception on non-zero exit code
|
646
|
-
**kwargs: Additional arguments to subprocess.run()
|
647
|
-
|
648
|
-
Returns:
|
649
|
-
subprocess.CompletedProcess: Command execution result
|
650
|
-
"""
|
651
|
-
if isinstance(cmd, str):
|
652
|
-
cmd = cmd.split()
|
653
|
-
|
654
|
-
kwargs.setdefault('stdout', subprocess.PIPE if capture_output else None)
|
655
|
-
kwargs.setdefault('stderr', subprocess.PIPE if capture_output else None)
|
656
|
-
kwargs.setdefault('text', True)
|
657
|
-
|
658
|
-
try:
|
659
|
-
return subprocess.run(cmd, check=check, **kwargs)
|
660
|
-
except subprocess.CalledProcessError as e:
|
661
|
-
logger.error(f"Command failed with exit code {e.returncode}: {' '.join(cmd)}")
|
662
|
-
if capture_output and e.stderr:
|
663
|
-
logger.error(f"Error output: {e.stderr.strip()}")
|
664
|
-
raise
|
665
|
-
|
666
|
-
async def run_command_async(cmd: Union[str, List[str]], **kwargs) -> subprocess.CompletedProcess:
|
667
|
-
"""
|
668
|
-
Run a shell command asynchronously.
|
669
|
-
|
670
|
-
Args:
|
671
|
-
cmd: Command to run (string or list)
|
672
|
-
**kwargs: Additional arguments to asyncio.create_subprocess_exec()
|
673
|
-
|
674
|
-
Returns:
|
675
|
-
subprocess.CompletedProcess: Command execution result
|
676
|
-
"""
|
677
|
-
if isinstance(cmd, str):
|
678
|
-
cmd = cmd.split()
|
679
|
-
|
680
|
-
process = await asyncio.create_subprocess_exec(
|
681
|
-
*cmd,
|
682
|
-
stdout=asyncio.subprocess.PIPE,
|
683
|
-
stderr=asyncio.subprocess.PIPE,
|
684
|
-
**kwargs
|
685
|
-
)
|
686
|
-
|
687
|
-
stdout, stderr = await process.communicate()
|
688
|
-
|
689
|
-
return subprocess.CompletedProcess(
|
690
|
-
args=cmd,
|
691
|
-
returncode=process.returncode,
|
692
|
-
stdout=stdout.decode() if stdout else '',
|
693
|
-
stderr=stderr.decode() if stderr else ''
|
694
|
-
)
|
695
|
-
|
696
|
-
def get_wireless_drivers() -> List[Dict[str, str]]:
|
697
|
-
"""
|
698
|
-
Get a list of wireless drivers and their information.
|
699
|
-
|
700
|
-
Returns:
|
701
|
-
List[Dict[str, str]]: List of driver information
|
702
|
-
"""
|
703
|
-
drivers = []
|
704
|
-
|
705
|
-
if IS_LINUX:
|
706
|
-
try:
|
707
|
-
# Check loaded kernel modules
|
708
|
-
with open('/proc/modules', 'r') as f:
|
709
|
-
for line in f:
|
710
|
-
module = line.split()[0]
|
711
|
-
if any(x in module.lower() for x in ['wlan', 'wireless', '80211', 'ath', 'rtl', 'rtw', 'mt76']):
|
712
|
-
drivers.append({
|
713
|
-
'name': module,
|
714
|
-
'type': 'kernel',
|
715
|
-
'status': 'loaded'
|
716
|
-
})
|
717
|
-
|
718
|
-
# Check loaded kernel modules with modinfo
|
719
|
-
for driver in drivers[:]: # Iterate over a copy of the list
|
720
|
-
try:
|
721
|
-
result = subprocess.run(['modinfo', driver['name']],
|
722
|
-
capture_output=True, text=True)
|
723
|
-
if result.returncode == 0:
|
724
|
-
# Parse modinfo output
|
725
|
-
info = {}
|
726
|
-
for line in result.stdout.splitlines():
|
727
|
-
if ':' in line:
|
728
|
-
key, value = line.split(':', 1)
|
729
|
-
info[key.strip()] = value.strip()
|
730
|
-
|
731
|
-
# Update driver info
|
732
|
-
driver.update({
|
733
|
-
'description': info.get('description', ''),
|
734
|
-
'version': info.get('version', ''),
|
735
|
-
'author': info.get('author', ''),
|
736
|
-
'license': info.get('license', '')
|
737
|
-
})
|
738
|
-
except:
|
739
|
-
pass
|
740
|
-
|
741
|
-
# Check for USB wireless devices
|
742
|
-
if os.path.exists('/sys/bus/usb/drivers'):
|
743
|
-
for driver in os.listdir('/sys/bus/usb/drivers'):
|
744
|
-
if any(x in driver.lower() for x in ['wlan', 'wireless', '80211', 'ath', 'rtl', 'rtw']):
|
745
|
-
drivers.append({
|
746
|
-
'name': driver,
|
747
|
-
'type': 'usb',
|
748
|
-
'status': 'available'
|
749
|
-
})
|
750
|
-
|
751
|
-
# Check for PCI wireless devices
|
752
|
-
if os.path.exists('/sys/bus/pci/drivers'):
|
753
|
-
for driver in os.listdir('/sys/bus/pci/drivers'):
|
754
|
-
if any(x in driver.lower() for x in ['wlan', 'wireless', '80211', 'ath', 'rtl', 'rtw']):
|
755
|
-
drivers.append({
|
756
|
-
'name': driver,
|
757
|
-
'type': 'pci',
|
758
|
-
'status': 'available'
|
759
|
-
})
|
760
|
-
|
761
|
-
except Exception as e:
|
762
|
-
logger.error(f"Error getting wireless drivers: {e}")
|
763
|
-
|
764
|
-
return drivers
|
765
|
-
|
766
|
-
def check_wireless_extensions(interface: str) -> bool:
|
767
|
-
"""
|
768
|
-
Check if a network interface supports wireless extensions.
|
769
|
-
|
770
|
-
Args:
|
771
|
-
interface: Interface name
|
772
|
-
|
773
|
-
Returns:
|
774
|
-
bool: True if wireless extensions are supported, False otherwise
|
775
|
-
"""
|
776
|
-
try:
|
777
|
-
if IS_LINUX:
|
778
|
-
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
779
|
-
try:
|
780
|
-
# Try to get wireless name
|
781
|
-
fcntl.ioctl(s.fileno(), WIRELESS_EXT, interface.encode() + b'\x00' * 32)
|
782
|
-
return True
|
783
|
-
except IOError:
|
784
|
-
return False
|
785
|
-
elif IS_MAC:
|
786
|
-
# macOS always supports wireless extensions
|
787
|
-
return True
|
788
|
-
else:
|
789
|
-
return False
|
790
|
-
except:
|
791
|
-
return False
|
792
|
-
|
793
|
-
def get_wireless_capabilities(interface: str) -> Dict[str, Any]:
|
794
|
-
"""
|
795
|
-
Get the wireless capabilities of an interface.
|
796
|
-
|
797
|
-
Args:
|
798
|
-
interface: Interface name
|
799
|
-
|
800
|
-
Returns:
|
801
|
-
Dict[str, Any]: Dictionary of wireless capabilities
|
802
|
-
"""
|
803
|
-
capabilities = {
|
804
|
-
'monitor': False,
|
805
|
-
'injection': False,
|
806
|
-
'frequency_bands': [],
|
807
|
-
'encryption': [],
|
808
|
-
'modes': []
|
809
|
-
}
|
810
|
-
|
811
|
-
if not check_wireless_extensions(interface):
|
812
|
-
return capabilities
|
813
|
-
|
814
|
-
try:
|
815
|
-
# Check if interface supports monitor mode
|
816
|
-
result = subprocess.run(['iw', 'phy', interface, 'info'],
|
817
|
-
capture_output=True, text=True)
|
818
|
-
|
819
|
-
if 'monitor' in result.stdout.lower():
|
820
|
-
capabilities['monitor'] = True
|
821
|
-
|
822
|
-
# Check for packet injection support
|
823
|
-
if 'RX invalid nwid' in result.stdout:
|
824
|
-
capabilities['injection'] = True
|
825
|
-
|
826
|
-
# Check supported frequency bands
|
827
|
-
if '5180 MHz' in result.stdout:
|
828
|
-
capabilities['frequency_bands'].append('5GHz')
|
829
|
-
if '2412 MHz' in result.stdout:
|
830
|
-
capabilities['frequency_bands'].append('2.4GHz')
|
831
|
-
|
832
|
-
# Check supported encryption types
|
833
|
-
if 'WPA' in result.stdout:
|
834
|
-
capabilities['encryption'].append('WPA')
|
835
|
-
if 'WPA2' in result.stdout:
|
836
|
-
capabilities['encryption'].append('WPA2')
|
837
|
-
if 'WEP' in result.stdout:
|
838
|
-
capabilities['encryption'].append('WEP')
|
839
|
-
|
840
|
-
# Check supported modes
|
841
|
-
if 'AP' in result.stdout:
|
842
|
-
capabilities['modes'].append('AP')
|
843
|
-
if 'station' in result.stdout.lower():
|
844
|
-
capabilities['modes'].append('station')
|
845
|
-
if 'monitor' in result.stdout.lower():
|
846
|
-
capabilities['modes'].append('monitor')
|
847
|
-
|
848
|
-
except Exception as e:
|
849
|
-
logger.error(f"Error getting wireless capabilities: {e}")
|
850
|
-
|
851
|
-
return capabilities
|