Skip to content

Instantly share code, notes, and snippets.

@flaf
Last active May 18, 2020 08:28
Show Gist options
  • Save flaf/11f3313ce8deaffc34c72a6862df7e1f to your computer and use it in GitHub Desktop.
Save flaf/11f3313ce8deaffc34c72a6862df7e1f to your computer and use it in GitHub Desktop.
python-and-multiprocessing
#!/usr/bin/env python3
import string
import itertools
import hashlib
import json
from multiprocessing import Pool, Manager
U = string.ascii_uppercase # ['A', 'B', ..., 'Z']
L = string.ascii_lowercase # ['a', 'b', ..., 'z']
D = string.digits # [0, 1, ..., 9]
C = ['#','!','?','*','%']
# Le point de départ est que j'ai un fichier JSON de cette forme là :
#
# {
# 'c17c15bc2e54dfe3b6f7b25f084cd25fec662b16': 'username1',
# '6187b6bc8a68cb1330251f427603de32900443d7': 'username2',
# '374582699bb845bd8cc3984627118af56ba7ab98': 'username3',
# ...
# }
#
# Il contient environ 600 000 entrées. Une clé est simplement le sha1sum du
# mot de passe d'un utilisateur, la valeur est le login d'un utilisateur.
#
# Postulat: on sait que tous les mots de passe sont de la forme :
#
# <u1><u1><d1><d2><l1><l2><d3><c1> avec u1, u2 dans U
# d1, d2, d3 dans D
# l1, l2 dans L
# et c1 dans C
#
# Comme par exemple: AB34xy5*
#
# Au total, ça fait un peu plus de 2 milliards de combinaisons possibles pour
# les mots de passe. Mon ordinateur dispose de 8 coeurs de CPU. Le but du
# programme est simplement calculer le sha1sum de ces 2 milliards de mots de
# passe afin de retrouver le mot de passe de chacun des utilisateurs du
# fichier JSON.
#
# Comme il y a 2 milliards de sha1sum à calculer, il faut tenter de
# paralléliser le mieux possible avec le multiprocessing. On va découper les
# calculs des sha1sum en plusieurs « paquets » disjoints pour les workers.
# Il y aura :
#
# - le worker « 0 » qui va calculer tous les sha1sum des mots de passe de la forme <u1><u1><d1><d2><l1><l2>0<c1>
# - le worker « 1 » qui va calculer tous les sha1sum des mots de passe de la forme <u1><u1><d1><d2><l1><l2>1<c1>
# - ...
# - le worker « 9 » qui va calculer tous les sha1sum des mots de passe de la forme <u1><u1><d1><d2><l1><l2>9<c1>
#
# Ça fera 10 workers pour mes 8 coeurs de CPU. J'aurais préféré 8 workers pour
# mes 8 coeurs (pour avoir 1 worker <=> 1 coeur de CPU) mais j'ai voulu faire
# un découpage simple alors tant pis.
# On récupère donc le fameux fichiers JSON que l'on charge sous la forme d'un
# dictionnaire Python.
with open('sha1sum_users.json', 'r') as f:
sha1sum_users = json.load(f)
# Et là, j'ai voulu faire un truc propre et classe. J'ai voulu que mes workers
# se partagent :
#
# 1. Le dictionnaire sha1sum_users pour que, dès qu'un worker calcule un
# sha1sum qu'il trouve dans ce dictionnaire, il supprime l'entrée dans ce
# dictionnaire. De cette manière, au fur et à mesure que les workers
# trouvent des mots de passe d'utilisateurs, le dictionnaire sha1sum_users
# se vide.
#
# 2. Un dictionnaire USERS_PASSWORDS, initialement vide, qui contiendrait des
# entrées de la forme:
#
# "username": "mot-de-passe-en-clair"
#
# Comme ça, dès qu'un worker trouve un mot de passe d'un utilisateur,
# il remplisse ce dictionnaire. Comme ça, à la fin de l'exécution des
# workers, on récupère un dictionnaire tout propre avec tous les
# identifiants.
#
# Dans le code, cela aurait donné quelque chose comme ça :
#
# manager = Manager()
# SHA1SUM_USERS = manager.dict(sha1sum_users)
# USERS_PASSWORDS = manager.dict()
#
# Et dans le code des workers, on aurait mis à jour ces deux dictionnaires.
# Seulement voilà, apparemment ça plombe les performances (ou alors je m'y
# prends mal). Il doit y avoir des locks sur ces dictionnaires posés par
# Python et cela engendre de la communication inter-processus qui ralentit
# tout. Au lieu d'avoir des CPU à 100% avec des workers qui calculent des
# sha1sum à tour de bras, j'ai des CPU à 20% environ, probablement en pause à
# cause des locks sur les dictionnaires et des communications entre les
# workers et le processus principal. C'est dommage, d'autant plus que 99.9% du
# temps les workers ne font que de l'accès en lecture sur ces objets
# dictionnaires (voir ne font rien du tout). Mais j'ai l'impression que,
# même en simple lecture, tout est ralentit malgré tout (ou alors, encore
# une fois, je m'y suis mal pris).
#
# Du coup, je laisse tomber le manager (snif), chaque worker va travailler
# tout seul dans son coin de manière indépendante et je vais devoir mon
# contenter de « print » dégueulasses dès qu'un mot de passe est trouvé.
# C'est naze mais je n'ai pas mieux pour l'instant.
# L'alternative avec un manager est donc abandonnée pour l'instant. On
# commente.
#
#manager = Manager()
#SHA1SUM_USERS = manager.dict(sha1sum_users)
#USERS_PASSWORDS = manager.dict()
def worker(d3):
for (u1, u2, d1, d2, l1, l2, c1) in itertools.product(U, U, D, D, L, L, C):
pwd = f'{u1}{u2}{d1}{d2}{l1}{l2}{d3}{c1}'
sha1 = hashlib.sha1(str.encode(pwd)).hexdigest()
try:
user = sha1sum_users[sha1]
# Pas d'exception KeyError, ça veut dire qu'on a trouvé un mot de
# passe d'un utilisateur \o/. On peut donc faire une « print »
# tout pourri.
print(f'"{user}": "{pwd}"')
# Dans un monde idéal avec notre manager, on mettrait à jour nos
# deux dictionnaires partagés.
#
#USERS_PASSWORDS[user] = pwd # <= ajout du login/password
#del SHA1SUM_USERS[sha1] # <= suppression de la clé sha1
except KeyError:
# On a eu une exception, le sha1 calculé n'est pas présent dans le
# JSON. On passe au prochain calcul de sha1.
continue
pool = Pool()
for d3 in D:
pool.apply_async(worker, args=(d3,))
pool.close()
pool.join()
# Idéalement avec notre manager, on aurait pu récupérer notre dictionnaire
# USERS_PASSWORDS avec tous les couples login/password. Mais cette option
# n'a pas été retenue faute de performance.
#
#with open('result.json', 'w') as f:
# json.dump(USERS_PASSWORDS, f, indent=4)
#
# On se contentera des « print » tout pourris des workers.
#
# Pour 60691 entrées dans le fichier JSON sha1sum_users.json,
# avec 8 coeurs de CPU (processeur Intel(R) Xeon(R) W-2125 CPU 4.00GHz),
# le programme s'est exécuté pendant environ 9 minutes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment