moriarty-project 0.1.20__py3-none-any.whl → 0.1.23__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/cli/app.py +5 -6
- moriarty/modules/port_scanner.py +1 -5
- {moriarty_project-0.1.20.dist-info → moriarty_project-0.1.23.dist-info}/METADATA +36 -49
- {moriarty_project-0.1.20.dist-info → moriarty_project-0.1.23.dist-info}/RECORD +7 -22
- moriarty/cli/wifippler.py +0 -124
- moriarty/modules/wifippler/__init__.py +0 -65
- moriarty/modules/wifippler/core/__init__.py +0 -80
- moriarty/modules/wifippler/core/attacks/__init__.py +0 -19
- moriarty/modules/wifippler/core/attacks/deauth.py +0 -340
- moriarty/modules/wifippler/core/attacks/handshake.py +0 -402
- moriarty/modules/wifippler/core/attacks/pmkid.py +0 -424
- moriarty/modules/wifippler/core/attacks/wep.py +0 -467
- moriarty/modules/wifippler/core/attacks/wpa.py +0 -446
- moriarty/modules/wifippler/core/attacks/wps.py +0 -474
- moriarty/modules/wifippler/core/models/__init__.py +0 -10
- moriarty/modules/wifippler/core/models/network.py +0 -216
- moriarty/modules/wifippler/core/scanner.py +0 -901
- moriarty/modules/wifippler/core/utils/__init__.py +0 -622
- moriarty/modules/wifippler/core/utils.py +0 -851
- {moriarty_project-0.1.20.dist-info → moriarty_project-0.1.23.dist-info}/WHEEL +0 -0
- {moriarty_project-0.1.20.dist-info → moriarty_project-0.1.23.dist-info}/entry_points.txt +0 -0
@@ -1,622 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Utilitários para o módulo WiFiPPLER.
|
3
|
-
"""
|
4
|
-
import os
|
5
|
-
import sys
|
6
|
-
import re
|
7
|
-
import subprocess
|
8
|
-
import shlex
|
9
|
-
import shutil
|
10
|
-
import logging
|
11
|
-
import fcntl
|
12
|
-
import struct
|
13
|
-
import array
|
14
|
-
import platform
|
15
|
-
import asyncio
|
16
|
-
from .utils import (
|
17
|
-
is_root,
|
18
|
-
get_wireless_interfaces,
|
19
|
-
get_network_interfaces,
|
20
|
-
get_monitor_interfaces,
|
21
|
-
set_monitor_mode,
|
22
|
-
restore_network_interface,
|
23
|
-
start_monitor_mode,
|
24
|
-
stop_monitor_mode,
|
25
|
-
run_command_async,
|
26
|
-
randomize_mac,
|
27
|
-
get_interface_mac,
|
28
|
-
get_interface_ip,
|
29
|
-
get_interface_netmask,
|
30
|
-
get_interface_gateway,
|
31
|
-
is_wireless_interface,
|
32
|
-
get_interface_signal,
|
33
|
-
get_interface_ssid,
|
34
|
-
get_interface_channel,
|
35
|
-
get_interface_bitrate,
|
36
|
-
create_deauth_packet,
|
37
|
-
parse_airodump_csv,
|
38
|
-
parse_airodump_stations,
|
39
|
-
run_command,
|
40
|
-
command_exists
|
41
|
-
)
|
42
|
-
from dataclasses import asdict
|
43
|
-
|
44
|
-
import netifaces
|
45
|
-
import psutil
|
46
|
-
from rich.console import Console
|
47
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn
|
48
|
-
|
49
|
-
from ..models.network import WiFiNetwork, WiFiClient, WiFiSecurityType, WiFiCipherType, WiFiAuthType
|
50
|
-
|
51
|
-
# Configuração de logging
|
52
|
-
logging.basicConfig(level=logging.INFO)
|
53
|
-
logger = logging.getLogger(__name__)
|
54
|
-
console = Console()
|
55
|
-
|
56
|
-
# Constantes
|
57
|
-
WIRELESS_EXT = "SIOCGIWNAME"
|
58
|
-
SIOCGIFHWADDR = 0x8927
|
59
|
-
SIOCGIFADDR = 0x8915
|
60
|
-
SIOCGIFNETMASK = 0x891B
|
61
|
-
SIOCGIFBRDADDR = 0x8919
|
62
|
-
SIOCGIFMTU = 0x8921
|
63
|
-
SIOCGIFINDEX = 0x8933
|
64
|
-
SIOCGIFNAME = 0x8910
|
65
|
-
SIOCGIFFLAGS = 0x8913
|
66
|
-
SIOCSIFFLAGS = 0x8914
|
67
|
-
|
68
|
-
# Mapeamento de códigos de segurança
|
69
|
-
SECURITY_MAP = {
|
70
|
-
'WPA2': WiFiSecurityType.WPA2,
|
71
|
-
'WPA3': WiFiSecurityType.WPA3,
|
72
|
-
'WPA2-EAP': WiFiSecurityType.WPA2_ENTERPRISE,
|
73
|
-
'WPA3-EAP': WiFiSecurityType.WPA3_ENTERPRISE,
|
74
|
-
'WEP': WiFiSecurityType.WEP,
|
75
|
-
'OPEN': WiFiSecurityType.NONE,
|
76
|
-
'NONE': WiFiSecurityType.NONE,
|
77
|
-
'WPA': WiFiSecurityType.WPA,
|
78
|
-
'WPA2-PSK': WiFiSecurityType.WPA2,
|
79
|
-
'WPA3-PSK': WiFiSecurityType.WPA3,
|
80
|
-
'WPA2-PSK-CCMP': WiFiSecurityType.WPA2,
|
81
|
-
'WPA2-PSK-TKIP': WiFiSecurityType.WPA2,
|
82
|
-
'WPA-PSK': WiFiSecurityType.WPA,
|
83
|
-
'WPA-PSK-CCMP': WiFiSecurityType.WPA,
|
84
|
-
'WPA-PSK-TKIP': WiFiSecurityType.WPA,
|
85
|
-
}
|
86
|
-
|
87
|
-
# Mapeamento de cifras
|
88
|
-
CIPHER_MAP = {
|
89
|
-
'CCMP': WiFiCipherType.CCMP,
|
90
|
-
'TKIP': WiFiCipherType.TKIP,
|
91
|
-
'WEP': WiFiCipherType.WEP_40, # Usando WEP_40 como padrão para WEP
|
92
|
-
'WEP-40': WiFiCipherType.WEP_40,
|
93
|
-
'WEP-104': WiFiCipherType.WEP_104,
|
94
|
-
'WEP-128': WiFiCipherType.WEP_104, # WEP-128 não está definido, usando WEP_104 como alternativa
|
95
|
-
'NONE': WiFiCipherType.NONE,
|
96
|
-
'GCMP': WiFiCipherType.GCMP
|
97
|
-
}
|
98
|
-
|
99
|
-
# Mapeamento de autenticação
|
100
|
-
AUTH_MAP = {
|
101
|
-
'PSK': WiFiAuthType.WPA_PSK, # Usando WPA_PSK para PSK
|
102
|
-
'WPA-PSK': WiFiAuthType.WPA_PSK,
|
103
|
-
'WPA2-PSK': WiFiAuthType.WPA2_PSK,
|
104
|
-
'WPA3-SAE': WiFiAuthType.WPA3_SAE,
|
105
|
-
'EAP': WiFiAuthType.WPA_EAP, # Usando WPA_EAP para EAP
|
106
|
-
'WPA-EAP': WiFiAuthType.WPA_EAP,
|
107
|
-
'WPA2-EAP': WiFiAuthType.WPA2_EAP,
|
108
|
-
'WPA3-EAP': WiFiAuthType.WPA3_EAP,
|
109
|
-
'OPEN': WiFiAuthType.OPEN,
|
110
|
-
'SHARED': WiFiAuthType.SHARED,
|
111
|
-
'OWE': WiFiAuthType.OWE,
|
112
|
-
'NONE': WiFiAuthType.OPEN,
|
113
|
-
}
|
114
|
-
|
115
|
-
def is_root() -> bool:
|
116
|
-
"""Verifica se o script está sendo executado como root."""
|
117
|
-
return os.geteuid() == 0
|
118
|
-
|
119
|
-
def check_dependencies() -> List[str]:
|
120
|
-
"""Verifica as dependências necessárias."""
|
121
|
-
required = ['iwconfig', 'iwlist', 'aircrack-ng', 'airodump-ng', 'aireplay-ng', 'airmon-ng']
|
122
|
-
missing = []
|
123
|
-
|
124
|
-
for dep in required:
|
125
|
-
if not command_exists(dep):
|
126
|
-
missing.append(dep)
|
127
|
-
|
128
|
-
|
129
|
-
def command_exists(cmd: str) -> bool:
|
130
|
-
"""Verifica se um comando existe no sistema."""
|
131
|
-
return shutil.which(cmd) is not None
|
132
|
-
|
133
|
-
# A função get_wireless_interfaces já está definida acima, então não precisamos duplicá-la
|
134
|
-
|
135
|
-
def run_command(cmd: Union[str, List[str]], capture_output: bool = False,
|
136
|
-
check: bool = True, **kwargs) -> subprocess.CompletedProcess:
|
137
|
-
"""
|
138
|
-
Executa um comando no shell com tratamento de erros.
|
139
|
-
|
140
|
-
Args:
|
141
|
-
cmd: Comando a ser executado (string ou lista)
|
142
|
-
capture_output: Se deve capturar a saída padrão e de erro
|
143
|
-
check: Se deve lançar uma exceção em caso de código de saída diferente de zero
|
144
|
-
**kwargs: Argumentos adicionais para subprocess.run()
|
145
|
-
|
146
|
-
Returns:
|
147
|
-
subprocess.ClosedProcess: Resultado da execução do comando
|
148
|
-
"""
|
149
|
-
if isinstance(cmd, str):
|
150
|
-
cmd = cmd.split()
|
151
|
-
|
152
|
-
kwargs.setdefault('stdout', subprocess.PIPE if capture_output else None)
|
153
|
-
kwargs.setdefault('stderr', subprocess.PIPE if capture_output else None)
|
154
|
-
kwargs.setdefault('text', True)
|
155
|
-
|
156
|
-
try:
|
157
|
-
return subprocess.run(cmd, check=check, **kwargs)
|
158
|
-
except Exception as e:
|
159
|
-
logger.error(f"Erro ao executar comando: {e}")
|
160
|
-
raise
|
161
|
-
|
162
|
-
def set_monitor_mode(interface: str, channel: int = None) -> bool:
|
163
|
-
"""Ativa o modo monitor em uma interface."""
|
164
|
-
if not is_root():
|
165
|
-
logger.error("Privilégios de root são necessários para ativar o modo monitor")
|
166
|
-
return False
|
167
|
-
|
168
|
-
try:
|
169
|
-
# Para processos que podem interferir
|
170
|
-
subprocess.run(['airmon-ng', 'check', 'kill'],
|
171
|
-
stdout=subprocess.PIPE,
|
172
|
-
stderr=subprocess.PIPE)
|
173
|
-
|
174
|
-
# Ativa o modo monitor
|
175
|
-
cmd = ['airmon-ng', 'start', interface]
|
176
|
-
if channel:
|
177
|
-
cmd.extend(['-c', str(channel)])
|
178
|
-
|
179
|
-
result = subprocess.run(cmd,
|
180
|
-
stdout=subprocess.PIPE,
|
181
|
-
stderr=subprocess.PIPE,
|
182
|
-
text=True)
|
183
|
-
|
184
|
-
if result.returncode != 0:
|
185
|
-
logger.error(f"Falha ao ativar o modo monitor: {result.stderr}")
|
186
|
-
return False
|
187
|
-
|
188
|
-
return True
|
189
|
-
|
190
|
-
except subprocess.CalledProcessError as e:
|
191
|
-
logger.error(f"Erro ao executar airmon-ng: {e}")
|
192
|
-
return False
|
193
|
-
except Exception as e:
|
194
|
-
logger.error(f"Erro inesperado: {e}")
|
195
|
-
return False
|
196
|
-
|
197
|
-
def restore_network_interface(interface: str) -> bool:
|
198
|
-
"""Restaura a interface para o modo gerenciado."""
|
199
|
-
if not is_root():
|
200
|
-
logger.error("Privilégios de root são necessários para restaurar a interface")
|
201
|
-
return False
|
202
|
-
|
203
|
-
try:
|
204
|
-
# Para o modo monitor
|
205
|
-
subprocess.run(['airmon-ng', 'stop', interface], check=True)
|
206
|
-
|
207
|
-
# Reinicia o gerenciador de rede
|
208
|
-
if command_exists('systemctl'):
|
209
|
-
subprocess.run(['systemctl', 'restart', 'NetworkManager'], check=False)
|
210
|
-
elif command_exists('service'):
|
211
|
-
subprocess.run(['service', 'network-manager', 'restart'], check=False)
|
212
|
-
|
213
|
-
return True
|
214
|
-
except subprocess.CalledProcessError as e:
|
215
|
-
logger.error(f"Erro ao parar o modo monitor: {e}")
|
216
|
-
return False
|
217
|
-
except Exception as e:
|
218
|
-
logger.error(f"Erro inesperado: {e}")
|
219
|
-
return False
|
220
|
-
|
221
|
-
def start_monitor_mode(interface: str, channel: int = None) -> Optional[str]:
|
222
|
-
"""Inicia o modo monitor em uma interface usando airmon-ng."""
|
223
|
-
if not is_root():
|
224
|
-
logger.error("Privilégios de root são necessários para iniciar o modo monitor")
|
225
|
-
return None
|
226
|
-
|
227
|
-
try:
|
228
|
-
# Para processos que podem interferir
|
229
|
-
subprocess.run(['airmon-ng', 'check', 'kill'],
|
230
|
-
stdout=subprocess.PIPE,
|
231
|
-
stderr=subprocess.PIPE)
|
232
|
-
|
233
|
-
# Inicia o modo monitor
|
234
|
-
cmd = ['airmon-ng', 'start', interface]
|
235
|
-
if channel:
|
236
|
-
cmd.extend(['-c', str(channel)])
|
237
|
-
|
238
|
-
result = subprocess.run(cmd,
|
239
|
-
stdout=subprocess.PIPE,
|
240
|
-
stderr=subprocess.PIPE,
|
241
|
-
text=True)
|
242
|
-
|
243
|
-
if result.returncode != 0:
|
244
|
-
logger.error(f"Falha ao iniciar o modo monitor: {result.stderr}")
|
245
|
-
return None
|
246
|
-
|
247
|
-
# Obtém o nome da interface em modo monitor (pode mudar, ex: wlan0 -> wlan0mon)
|
248
|
-
for line in result.stdout.split('\n'):
|
249
|
-
if 'monitor mode' in line and 'enabled' in line:
|
250
|
-
parts = line.split()
|
251
|
-
if len(parts) > 0:
|
252
|
-
return parts[0].strip('()')
|
253
|
-
|
254
|
-
return f"{interface}mon" # Padrão comum
|
255
|
-
|
256
|
-
except subprocess.CalledProcessError as e:
|
257
|
-
logger.error(f"Erro ao executar airmon-ng: {e}")
|
258
|
-
return None
|
259
|
-
except Exception as e:
|
260
|
-
logger.error(f"Erro inesperado: {e}")
|
261
|
-
return None
|
262
|
-
|
263
|
-
def stop_monitor_mode(interface: str) -> bool:
|
264
|
-
"""Para o modo monitor em uma interface usando airmon-ng."""
|
265
|
-
if not is_root():
|
266
|
-
logger.error("Privilégios de root são necessários para parar o modo monitor")
|
267
|
-
return False
|
268
|
-
|
269
|
-
try:
|
270
|
-
# Para o modo monitor
|
271
|
-
subprocess.run(['airmon-ng', 'stop', interface], check=True)
|
272
|
-
|
273
|
-
# Reinicia o gerenciador de rede
|
274
|
-
if command_exists('systemctl'):
|
275
|
-
subprocess.run(['systemctl', 'restart', 'NetworkManager'], check=False)
|
276
|
-
elif command_exists('service'):
|
277
|
-
subprocess.run(['service', 'network-manager', 'restart'], check=False)
|
278
|
-
|
279
|
-
return True
|
280
|
-
except subprocess.CalledProcessError as e:
|
281
|
-
logger.error(f"Erro ao parar modo monitor: {e}")
|
282
|
-
return False
|
283
|
-
except Exception as e:
|
284
|
-
logger.error(f"Erro inesperado: {e}")
|
285
|
-
return False
|
286
|
-
|
287
|
-
def get_interface_mac(interface: str) -> Optional[str]:
|
288
|
-
"""Obtém o endereço MAC de uma interface de rede."""
|
289
|
-
try:
|
290
|
-
with open(f"/sys/class/net/{interface}/address") as f:
|
291
|
-
return f.read().strip()
|
292
|
-
except (IOError, FileNotFoundError):
|
293
|
-
return None
|
294
|
-
|
295
|
-
def get_interface_ip(interface: str) -> Optional[str]:
|
296
|
-
"""Obtém o endereço IP de uma interface de rede."""
|
297
|
-
try:
|
298
|
-
return netifaces.ifaddresses(interface)[netifaces.AF_INET][0]['addr']
|
299
|
-
except (KeyError, IndexError):
|
300
|
-
return None
|
301
|
-
|
302
|
-
def get_interface_netmask(interface: str) -> Optional[str]:
|
303
|
-
"""Obtém a máscara de rede de uma interface de rede."""
|
304
|
-
try:
|
305
|
-
return netifaces.ifaddresses(interface)[netifaces.AF_INET][0]['netmask']
|
306
|
-
except (KeyError, IndexError):
|
307
|
-
return None
|
308
|
-
|
309
|
-
def get_interface_gateway(interface: str) -> Optional[str]:
|
310
|
-
"""Obtém o gateway padrão de uma interface de rede."""
|
311
|
-
try:
|
312
|
-
gateways = netifaces.gateways()
|
313
|
-
return gateways['default'][netifaces.AF_INET][0]
|
314
|
-
except (KeyError, IndexError):
|
315
|
-
return None
|
316
|
-
|
317
|
-
def is_wireless_interface(interface: str) -> bool:
|
318
|
-
"""Verifica se uma interface é sem fio."""
|
319
|
-
try:
|
320
|
-
with open(f"/sys/class/net/{interface}/wireless/uevent", 'r') as f:
|
321
|
-
return True
|
322
|
-
except (IOError, FileNotFoundError):
|
323
|
-
return False
|
324
|
-
|
325
|
-
def get_interface_signal(interface: str) -> Optional[int]:
|
326
|
-
"""Obtém a intensidade do sinal de uma interface sem fio em dBm."""
|
327
|
-
try:
|
328
|
-
result = subprocess.run(
|
329
|
-
['iwconfig', interface],
|
330
|
-
stdout=subprocess.PIPE,
|
331
|
-
stderr=subprocess.PIPE,
|
332
|
-
text=True
|
333
|
-
)
|
334
|
-
|
335
|
-
if result.returncode != 0:
|
336
|
-
return None
|
337
|
-
|
338
|
-
match = re.search(r'Signal level=(-?\d+) dBm', result.stdout)
|
339
|
-
if match:
|
340
|
-
return int(match.group(1))
|
341
|
-
return None
|
342
|
-
except (subprocess.SubprocessError, ValueError):
|
343
|
-
return None
|
344
|
-
|
345
|
-
def get_interface_ssid(interface: str) -> Optional[str]:
|
346
|
-
"""Obtém o SSID ao qual a interface está conectada."""
|
347
|
-
try:
|
348
|
-
result = subprocess.run(
|
349
|
-
['iwgetid', '-r', interface],
|
350
|
-
stdout=subprocess.PIPE,
|
351
|
-
stderr=subprocess.PIPE,
|
352
|
-
text=True
|
353
|
-
)
|
354
|
-
|
355
|
-
if result.returncode == 0 and result.stdout.strip():
|
356
|
-
return result.stdout.strip()
|
357
|
-
return None
|
358
|
-
except (subprocess.SubprocessError, FileNotFoundError):
|
359
|
-
return None
|
360
|
-
|
361
|
-
def get_interface_channel(interface: str) -> Optional[int]:
|
362
|
-
"""Obtém o canal em que a interface está operando."""
|
363
|
-
try:
|
364
|
-
result = subprocess.run(
|
365
|
-
['iwlist', interface, 'channel'],
|
366
|
-
stdout=subprocess.PIPE,
|
367
|
-
stderr=subprocess.PIPE,
|
368
|
-
text=True
|
369
|
-
)
|
370
|
-
|
371
|
-
if result.returncode != 0:
|
372
|
-
return None
|
373
|
-
|
374
|
-
match = re.search(r'Channel (\d+)', result.stdout)
|
375
|
-
if match:
|
376
|
-
return int(match.group(1))
|
377
|
-
return None
|
378
|
-
except (subprocess.SubprocessError, ValueError):
|
379
|
-
return None
|
380
|
-
|
381
|
-
def get_interface_bitrate(interface: str) -> Optional[float]:
|
382
|
-
"""Obtém a taxa de transmissão da interface em Mbps."""
|
383
|
-
try:
|
384
|
-
result = subprocess.run(
|
385
|
-
['iwconfig', interface],
|
386
|
-
stdout=subprocess.PIPE,
|
387
|
-
stderr=subprocess.PIPE,
|
388
|
-
text=True
|
389
|
-
)
|
390
|
-
|
391
|
-
if result.returncode != 0:
|
392
|
-
return None
|
393
|
-
|
394
|
-
match = re.search(r'Bit Rate[:=]([\d.]+) (\w+)', result.stdout)
|
395
|
-
if match:
|
396
|
-
rate, unit = match.groups()
|
397
|
-
rate = float(rate)
|
398
|
-
if unit == 'Mb/s':
|
399
|
-
return rate
|
400
|
-
elif unit == 'Kb/s':
|
401
|
-
return rate / 1000
|
402
|
-
return None
|
403
|
-
except (subprocess.SubprocessError, ValueError):
|
404
|
-
return None
|
405
|
-
|
406
|
-
# Funções para manipulação de pacotes
|
407
|
-
def create_deauth_packet(bssid: str, client: str, reason: int = 7, count: int = 1) -> bytes:
|
408
|
-
"""Cria um pacote de desautenticação."""
|
409
|
-
# Cabeçalho do pacote 802.11
|
410
|
-
frame = [
|
411
|
-
0x00, 0x00, 0x0c, 0x00, # Radiotap header (versão, pad, len, presente)
|
412
|
-
0x04, 0x80, 0x00, 0x00, # Presente flags
|
413
|
-
0x00, 0x00, 0x00, 0x00, # Timestamp
|
414
|
-
0x00, 0x00, 0x00, 0x00, # Flags e taxa de dados
|
415
|
-
0x00, 0x00, # Canal, sinal, ruído, etc.
|
416
|
-
0x00, 0x00, 0x00, 0x00, # MCS conhecido, flags, mcs
|
417
|
-
0x00, 0x00, 0x00, 0x00, # A-MPDU
|
418
|
-
|
419
|
-
# Cabeçalho 802.11 (tipo Management, subtipo Deauthentication)
|
420
|
-
0x00, 0x00, 0x0c, 0x00, # Controle de versão, tipo, subtipo, etc.
|
421
|
-
0x00, 0x00, # Duração
|
422
|
-
]
|
423
|
-
|
424
|
-
# Endereço de destino (broadcast ou cliente específico)
|
425
|
-
dest = [int(x, 16) for x in client.split(':')]
|
426
|
-
frame.extend(dest)
|
427
|
-
|
428
|
-
# Endereço de origem (BSSID)
|
429
|
-
src = [int(x, 16) for x in bssid.split(':')]
|
430
|
-
frame.extend(src)
|
431
|
-
|
432
|
-
# BSSID (mesmo que origem para redes de infraestrutura)
|
433
|
-
frame.extend(src)
|
434
|
-
|
435
|
-
# Número de sequência
|
436
|
-
frame.extend([0x00, 0x00])
|
437
|
-
|
438
|
-
# Código de motivo (2 bytes, little-endian)
|
439
|
-
frame.extend([reason, 0x00])
|
440
|
-
|
441
|
-
return bytes(frame) * count
|
442
|
-
|
443
|
-
# Funções para execução de comandos
|
444
|
-
async def run_command_async(cmd: Union[str, List[str]], **kwargs) -> subprocess.CompletedProcess:
|
445
|
-
"""
|
446
|
-
Executa um comando de forma assíncrona.
|
447
|
-
|
448
|
-
Args:
|
449
|
-
cmd: Comando a ser executado (string ou lista)
|
450
|
-
**kwargs: Argumentos adicionais para asyncio.create_subprocess_exec()
|
451
|
-
|
452
|
-
Returns:
|
453
|
-
subprocess.CompletedProcess: Resultado da execução do comando
|
454
|
-
"""
|
455
|
-
if isinstance(cmd, str):
|
456
|
-
cmd = cmd.split()
|
457
|
-
|
458
|
-
process = await asyncio.create_subprocess_exec(
|
459
|
-
*cmd,
|
460
|
-
stdout=asyncio.subprocess.PIPE,
|
461
|
-
stderr=asyncio.subprocess.PIPE,
|
462
|
-
**kwargs
|
463
|
-
)
|
464
|
-
|
465
|
-
stdout, stderr = await process.communicate()
|
466
|
-
|
467
|
-
return subprocess.CompletedProcess(
|
468
|
-
args=cmd,
|
469
|
-
returncode=process.returncode,
|
470
|
-
stdout=stdout,
|
471
|
-
stderr=stderr
|
472
|
-
)
|
473
|
-
|
474
|
-
# Funções para processamento de saída
|
475
|
-
def parse_airodump_csv(csv_file: str) -> List[WiFiNetwork]:
|
476
|
-
"""Analisa o arquivo CSV gerado pelo airodump-ng."""
|
477
|
-
networks = []
|
478
|
-
|
479
|
-
try:
|
480
|
-
with open(csv_file, 'r') as f:
|
481
|
-
lines = f.readlines()
|
482
|
-
|
483
|
-
# Encontra o início dos dados das redes
|
484
|
-
start_idx = 0
|
485
|
-
for i, line in enumerate(lines):
|
486
|
-
if line.startswith('BSSID, First time seen,'):
|
487
|
-
start_idx = i + 1
|
488
|
-
break
|
489
|
-
|
490
|
-
# Processa as redes
|
491
|
-
for line in lines[start_idx:]:
|
492
|
-
line = line.strip()
|
493
|
-
if not line or line.startswith('Station'):
|
494
|
-
break
|
495
|
-
|
496
|
-
# Extrai os campos da linha
|
497
|
-
parts = [p.strip() for p in line.split(',')]
|
498
|
-
if len(parts) < 14: # Número mínimo de campos esperados
|
499
|
-
continue
|
500
|
-
|
501
|
-
bssid = parts[0].strip()
|
502
|
-
first_seen = parts[1].strip()
|
503
|
-
last_seen = parts[2].strip()
|
504
|
-
channel = int(parts[3].strip())
|
505
|
-
speed = parts[4].strip()
|
506
|
-
privacy = parts[5].strip()
|
507
|
-
cipher = parts[6].strip()
|
508
|
-
auth = parts[7].strip()
|
509
|
-
power = int(parts[8].strip())
|
510
|
-
beacons = int(parts[9].strip())
|
511
|
-
iv = int(parts[10].strip())
|
512
|
-
ip = parts[11].strip()
|
513
|
-
id_len = int(parts[12].strip())
|
514
|
-
essid = parts[13].strip()
|
515
|
-
|
516
|
-
# Cria o objeto da rede
|
517
|
-
network = WiFiNetwork(
|
518
|
-
bssid=bssid,
|
519
|
-
ssid=essid,
|
520
|
-
channel=channel,
|
521
|
-
signal=power,
|
522
|
-
encryption=privacy,
|
523
|
-
cipher=cipher,
|
524
|
-
authentication=auth,
|
525
|
-
first_seen=first_seen,
|
526
|
-
last_seen=last_seen,
|
527
|
-
speed=speed,
|
528
|
-
beacons=beacons,
|
529
|
-
iv=iv,
|
530
|
-
ip=ip if ip != '0.0.0.0' else None
|
531
|
-
)
|
532
|
-
|
533
|
-
networks.append(network)
|
534
|
-
|
535
|
-
except Exception as e:
|
536
|
-
logger.error(f"Erro ao analisar arquivo CSV: {e}")
|
537
|
-
|
538
|
-
return networks
|
539
|
-
|
540
|
-
def parse_airodump_stations(csv_file: str) -> List[WiFiClient]:
|
541
|
-
"""Analisa a seção de estações do arquivo CSV do airodump-ng."""
|
542
|
-
clients = []
|
543
|
-
|
544
|
-
try:
|
545
|
-
with open(csv_file, 'r') as f:
|
546
|
-
lines = f.readlines()
|
547
|
-
|
548
|
-
# Encontra o início da seção de estações
|
549
|
-
start_idx = 0
|
550
|
-
for i, line in enumerate(lines):
|
551
|
-
if line.startswith('Station MAC,'):
|
552
|
-
start_idx = i + 1
|
553
|
-
break
|
554
|
-
|
555
|
-
# Processa as estações
|
556
|
-
for line in lines[start_idx:]:
|
557
|
-
line = line.strip()
|
558
|
-
if not line:
|
559
|
-
continue
|
560
|
-
|
561
|
-
# Extrai os campos da linha
|
562
|
-
parts = [p.strip() for p in line.split(',')]
|
563
|
-
if len(parts) < 6: # Número mínimo de campos esperados
|
564
|
-
continue
|
565
|
-
|
566
|
-
mac = parts[0].strip()
|
567
|
-
first_seen = parts[1].strip()
|
568
|
-
last_seen = parts[2].strip()
|
569
|
-
power = int(parts[3].strip())
|
570
|
-
packets = int(parts[4].strip())
|
571
|
-
bssid = parts[5].strip()
|
572
|
-
|
573
|
-
# Cria o objeto do cliente
|
574
|
-
client = WiFiClient(
|
575
|
-
mac=mac,
|
576
|
-
bssid=bssid,
|
577
|
-
signal=power,
|
578
|
-
packets=packets,
|
579
|
-
first_seen=first_seen,
|
580
|
-
last_seen=last_seen
|
581
|
-
)
|
582
|
-
|
583
|
-
clients.append(client)
|
584
|
-
|
585
|
-
except Exception as e:
|
586
|
-
logger.error(f"Erro ao analisar estações do arquivo CSV: {e}")
|
587
|
-
|
588
|
-
return clients
|
589
|
-
|
590
|
-
def randomize_mac(interface: str) -> bool:
|
591
|
-
"""
|
592
|
-
Aleatoriza o endereço MAC de uma interface de rede.
|
593
|
-
|
594
|
-
Args:
|
595
|
-
interface: Nome da interface de rede
|
596
|
-
|
597
|
-
Returns:
|
598
|
-
bool: True se bem-sucedido, False caso contrário
|
599
|
-
"""
|
600
|
-
if not is_root():
|
601
|
-
logger.error("Privilégios de root são necessários para alterar o endereço MAC")
|
602
|
-
return False
|
603
|
-
|
604
|
-
try:
|
605
|
-
# Gera um endereço MAC aleatório
|
606
|
-
import random
|
607
|
-
new_mac = ':'.join(['%02x' % random.randint(0x00, 0xff) for _ in range(6)])
|
608
|
-
|
609
|
-
# Desativa a interface
|
610
|
-
subprocess.run(['ip', 'link', 'set', interface, 'down'], check=True)
|
611
|
-
|
612
|
-
# Define o novo endereço MAC
|
613
|
-
subprocess.run(['ip', 'link', 'set', 'dev', interface, 'address', new_mac], check=True)
|
614
|
-
|
615
|
-
# Reativa a interface
|
616
|
-
subprocess.run(['ip', 'link', 'set', interface, 'up'], check=True)
|
617
|
-
|
618
|
-
logger.info(f"Endereço MAC da interface {interface} alterado para {new_mac}")
|
619
|
-
return True
|
620
|
-
except subprocess.CalledProcessError as e:
|
621
|
-
logger.error(f"Falha ao aleatorizar o endereço MAC: {e}")
|
622
|
-
return False
|