昨日因為手痕,用python寫了個MD5的暴力破解器。
在寫的過程中換了個計算第n個遍歷字串的方法,做了version 0.2。
然後發現產生的序列有點問題,就換回了v0.1然後再把它完善。 效率不高。
在我Intel Core Duo T6670上單目標字串的測試效率是 5W string/sec
本來想用多線程 用了才發現 python 假多線程, 真悲劇。
好了貼下代碼給有需要的你:
#!/usr/bin/env python<br /># -*- coding: utf-8 -*-<br />"""<br />MD5 Checker<br />version: 0.3beta<br />speed: 5W/s<br />author : zagfai<br />time : 2011.4.9<br />The MIT License<br />Copyright (c) 2011 Foshan University zagfai<br />Permission is hereby granted, free of charge, to any person obtaining a copy<br />of this software and associated documentation files (the "Software"), to deal<br />in the Software without restriction, including without limitation the rights<br />to use, copy, modify, merge, publish, distribute, sublicense, and/or sell<br />copies of the Software, and to permit persons to whom the Software is<br />furnished to do so, subject to the following conditions:<br />The above copyright notice and this permission notice shall be included in<br />all copies or substantial portions of the Software.<br />THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br />IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br />FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE<br />AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br />LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,<br />OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN<br />THE SOFTWARE.</p><p>http://www.opensource.org/licenses/mit-license.php</p><p>"""<br />import os<br />import sys<br />import md5<br />import time<br />import string<br />import threading<br />ISOTIMEFORMAT='%y/%m/%d/%X'<br />class PasswordTraveler():<br />"""<br />Use to make a travel from 0 to Unlimited.</p><p>next():<br />Return next password from pw_str to the next.<br />This function take the NEXT follow this:<br />0 ~ 9 then A ~ Z then a ~ z</p><p>value():<br />Return the value of this password</p><p>set(start_string):<br />Set the startup positon</p><p>__fix():<br />Not for public use, only make the string in Format we use.<br />You can modify this if you want to enlarge the searching or enclose.<br />"""<br />pw_str = '0'</p><p>def __init__(self, start_up=None):<br />if(start_up != None):<br />self.pw_str = start_up</p><p>def next(self):<br />low_chr = chr(ord(self.pw_str[0])+1)<br />self.pw_str = str(low_chr) + self.pw_str[1:]<br />self.__fix(0, len(self.pw_str), ord(low_chr))</p><p>def value(self):<br />return self.pw_str<br />def set(self, new_str):<br />self.pw_str = new_str</p><p>def __fix(self, pos, length, chr_i):</p><p>#print pos, length, chr_i<br />#print "String:" + self.pw_str<br />if(chr_i == 58):<br />if(pos == length-1):<br />self.pw_str = self.pw_str[:length-1] + 'A'<br />return<br />self.pw_str = self.pw_str[:pos] + 'A' + self.pw_str[pos+1:length]</p><p>elif(chr_i == 91):<br />if(pos == length-1):<br />self.pw_str = self.pw_str[:length-1] + 'a'<br />return<br />self.pw_str = self.pw_str[:pos] + 'a' + self.pw_str[pos+1:length]</p><p>elif(chr_i == 123):<br />if(pos == length-1):<br />self.pw_str = self.pw_str[:length-1] + '00'<br />return<br />self.pw_str = self.pw_str[:pos] + '0' + str(chr(ord(self.pw_str[pos+1])+1)) + self.pw_str[pos+2:length]<br />self.__fix(pos+1, length, ord(self.pw_str[pos+1]))<br />pass<br />class Loger():<br />"""<br />Use to log the Calculations.<br />"""<br />__path="log.txt"<br />def __init__(self, path=None):<br />if(path != None):<br />self.__path = path<br />def a(self, st):<br />try:<br />fp = open(self.__path, "a")<br />fp.write('['+ time.strftime( ISOTIMEFORMAT, time.localtime() ) +']' + st + '/n')<br />fp.close()<br />except(IOError):<br />print "Write result error."<br />pass<br />class Config():<br />"""<br />Config modifier.<br />"""<br />lt = ""<br />loaded = 0<br />def __init__(self):<br />pass<br />lt = ""<br />self.l()<br />def l(self):<br /># Read config.ini<br />try:<br />fp = open("config.ini")<br />self.lt = fp.read()<br />#for i in xt:<br />#lt.append( i[:-1].split("->") )<br />#print lt<br />fp.close()<br />self.loaded = 1<br />except(IOError):<br />lg.a("Config load error.")</p><p>def w(self, st):<br />try:<br />fp = open("config.ini","w")<br />fp.write(st)<br />fp.close()<br />except(IOError):<br />lg.a("Write config error.")</p><p>def at(self):<br />return self.lt</p><p>class Checker(threading.Thread):<br />__result = []<br />__endp = None<br />__ck_str = None</p><p>def __init__(self, begin_str):<br />threading.Thread.__init__(self)<br />self.__ck_str = begin_str<br />self.__result = []<br />self.__endp = None</p><p>def run(self):<br />tt = time.time()</p><p>x = PasswordTraveler(self.__ck_str)<br />for i in xrange(ONCE_CHECK):<br />md5_str = md5.new(x.value()).hexdigest()<br />for cip in ct:<br />if(cip == md5_str):<br />self.__result.append(x.value() + ' for ' + cip)<br />x.next()<br />self.__endp = x.value()<br />lg.a(self.getName() + " Start at " + self.__ck_str + " to " + x.value()+" in "+str(time.time()-tt) +" secs")</p><p>def get_result(self):<br />return self.__result, self.__endp<br />def readin():<br /># Read ciphertext<br />try:<br />fp = open("ciphertext")<br />xt = fp.readlines()<br />for i in xt:<br />ct.append(i[:-1])<br />#print ct<br />fp.close()<br />except(IOError):<br />lg.a("Input file error.")<br />def saved(list):<br />try:<br />fp = open("result.txt","a")<br />fp.writelines(list[0])<br />fp.write('/n')<br />fp.close()<br />except(IOError):<br />lg.a("Write result error.")<br />ct = []<br />lg = Loger()<br />cg = Config()<br />ONCE_CHECK = 100000 #242234 968936 726702<br />def main():<br />readin()<br />if len(sys.argv) < 2:<br />starter = '0'<br />if(cg.loaded == 1):<br />starter = cg.at()<br />else:<br />starter = sys.argv[1]<br />lg.a("Start at: " + starter)</p><p>while 1:<br />ck = Checker(starter)<br />ck.start()<br />while ck.isAlive():<br />continue<br />if(len(ck.get_result()[0]) >0):<br />saved(ck.get_result())<br />starter = ck.get_result()[1]<br />cg.w(starter)</p><p>if __name__ == "__main__":<br />main()
我已經把類搞好了
但你需要準備至少1份檔案才能開始運行:
ciphertext 檔案 (文本,但不以txt結尾) 該文本用來輸入目標MD5密碼 每行32位, 每個密碼1行
PasswordTraveler() 是測試字串的對象, 調用 .next() 使其變成下一個要測試的字串
Loger() 是要來記錄日誌的類
Config() 是要來讀取和寫入設定檔 以便下一次繼續遍歷的
Checker() 就更不用說了 是要來測試的
這個暴力破解程式遍歷的是 0~9, A~Z 和 a~z , 如果你有需要修改, 請讀懂PasswordTraveler()裏面的 __fix()函數然後作修改
調整單次測試個數 修改ONCE_CHECK = xxxxx .
要加鹽(salt)什麽的, 修改Checker() -> run() 裏面的 md5_str = md5.new(x.value() ).hexdigest()
基本就這麼多. have your nice time.~ 0.0
第一次運行如果你沒設置config他會記錄 load error. 然後產生config.ini.
如果你需要更多的幫助,可以聯繫我. 地址: me {at} zagfai.info