丢失BIP38 password的挽救措施
我弄丢了我钱包的密码,钱包是基于BIP38,只有助记词无法恢复钱包,我编写了基于CPU密码破解程序,以及自动生成密码的工具,最终我找回了密码重新获得了钱包的所有权。
如果你的密码不算复杂或者你对密码还有一点记忆,你就还有希望找回密码。
如果你的密码非常复杂,并且对密码完全没有印象,那么你还可以尝试编写使用GPU的程序来爆破密码,你仍然有希望找回密码。
本文记录我找回密码的过程。
https://github.com/suisuiyiyi/BIP38PasswordBruteForce
如果对你有帮助可以打赏我bitcoin地址:******
WalletWasabi实际上内置提供了WasabiPasswordFinder项目,当我丢失密码我首先尝试了这个,他会要求你输入一个相似密码,然后尝试正确密码。通过这个程序我没能找回我的密码,当你密码某一位错误时候,这个程序可能能帮你找到正确密码,而我则对我的密码没有任何印象了。
通过https://github.com/bitcoin/bips/blob/master/bip-0038.mediawiki可以找到关于bip38的说明资料。
实际上WalletWasabi使用的就是 Compression, no EC multiply 的方式。
我想通过python-graphenelib库来实现bip38的encrypt和decrypt,但发现python-graphenelib库bip38的decrypt不支持compression
assert flagbyte == b"\xc0", "Flagbyte has to be 0xc0"
xe0表示使用compression,xc0表示no compression,python-graphenelib库bip38 decrypt只支持xc0,所有并不能用来让我实现爆破密码
后来我找到了pybip38库,但这个库并不能直接运行,需要将decrypt方法的
cipher = AES.new(key)
修改为
cipher = AES.new(key, AES.MODE_ECB)
然后我提取了decrypt到我的暴力破解程序,此时我仍然对找回密码不抱希望,因为使用cpu计算即使是6位字母数字特殊符号组合也需要耗费很夸张的时间,而我的密码可能更长,我甚至打算编写gpu运算的程序或者是等计算机硬件发展二十年再来破解我的密码。
不愿意放弃的我整理了我密码的常用字段,生成了一份字典,意想不到的破解出了密码。。。
最后我编写了一个小脚本,帮助那些和我一样忘记密码的人。
import itertools import sys from binascii import unhexlify import datetime import scrypt import threading from Crypto.Cipher import AES from simplebitcoinfuncs import normalize_input, b58d, hexstrlify, dechex, privtopub, compress, pubtoaddress, b58e, \ multiplypriv from simplebitcoinfuncs.ecmath import N from simplebitcoinfuncs.hexhashes import hash256 def simple_aes_decrypt(msg, key): assert len(msg) == 16 assert len(key) == 32 cipher = AES.new(key, AES.MODE_ECB) msg = hexstrlify(cipher.decrypt(msg)) while msg[-2:] == '7b': # Can't use rstrip for multiple chars msg = msg[:-2] for i in range((32 - len(msg)) // 2): msg = msg + '7b' assert len(msg) == 32 return unhexlify(msg) def bip38decrypt(password, encpriv, outputlotsequence=False): password = normalize_input(password, False, True) encpriv = b58d(encpriv) assert len(encpriv) == 78 prefix = encpriv[:4] assert prefix == '0142' or prefix == '0143' flagbyte = encpriv[4:6] if prefix == '0142': salt = unhexlify(encpriv[6:14]) msg1 = unhexlify(encpriv[14:46]) msg2 = unhexlify(encpriv[46:]) scrypthash = hexstrlify(scrypt.hash(password, salt, 16384, 8, 8, 64)) key = unhexlify(scrypthash[64:]) msg1 = hexstrlify(simple_aes_decrypt(msg1, key)) msg2 = hexstrlify(simple_aes_decrypt(msg2, key)) half1 = int(msg1, 16) ^ int(scrypthash[:32], 16) half2 = int(msg2, 16) ^ int(scrypthash[32:64], 16) priv = dechex(half1, 16) + dechex(half2, 16) if int(priv, 16) == 0 or int(priv, 16) >= N: if outputlotsequence: return False, False, False else: return False pub = privtopub(priv, False) if flagbyte in COMPRESSION_FLAGBYTES: privcompress = '01' pub = compress(pub) else: privcompress = '' address = pubtoaddress(pub, '00') try: addrhex = hexstrlify(address) except: addrhex = hexstrlify(bytearray(address, 'ascii')) addresshash = hash256(addrhex)[:8] if addresshash == encpriv[6:14]: priv = b58e('80' + priv + privcompress) if outputlotsequence: return priv, False, False else: return priv else: if outputlotsequence: return False, False, False else: return False else: owner_entropy = encpriv[14:30] enchalf1half1 = encpriv[30:46] enchalf2 = encpriv[46:] if flagbyte in LOTSEQUENCE_FLAGBYTES: lotsequence = owner_entropy[8:] owner_salt = owner_entropy[:8] else: lotsequence = False owner_salt = owner_entropy salt = unhexlify(owner_salt) prefactor = hexstrlify(scrypt.hash(password, salt, 16384, 8, 8, 32)) if lotsequence is False: passfactor = prefactor else: passfactor = hash256(prefactor + owner_entropy) if int(passfactor, 16) == 0 or int(passfactor, 16) >= N: if outputlotsequence: return False, False, False else: return False passpoint = privtopub(passfactor, True) password = unhexlify(passpoint) salt = unhexlify(encpriv[6:14] + owner_entropy) encseedb = hexstrlify(scrypt.hash(password, salt, 1024, 1, 1, 64)) key = unhexlify(encseedb[64:]) tmp = hexstrlify(simple_aes_decrypt(unhexlify(enchalf2), key)) enchalf1half2_seedblastthird = int(tmp, 16) ^ int(encseedb[32:64], 16) enchalf1half2_seedblastthird = dechex(enchalf1half2_seedblastthird, 16) enchalf1half2 = enchalf1half2_seedblastthird[:16] enchalf1 = enchalf1half1 + enchalf1half2 seedb = hexstrlify(simple_aes_decrypt(unhexlify(enchalf1), key)) seedb = int(seedb, 16) ^ int(encseedb[:32], 16) seedb = dechex(seedb, 16) + enchalf1half2_seedblastthird[16:] assert len(seedb) == 48 # I want to except for this and be alerted to it try: factorb = hash256(seedb) assert int(factorb, 16) != 0 assert not int(factorb, 16) >= N except: if outputlotsequence: return False, False, False else: return False priv = multiplypriv(passfactor, factorb) pub = privtopub(priv, False) if flagbyte in COMPRESSION_FLAGBYTES: privcompress = '01' pub = compress(pub) else: privcompress = '' address = pubtoaddress(pub, '00') try: addrhex = hexstrlify(address) except: addrhex = hexstrlify(bytearray(address, 'ascii')) addresshash = hash256(addrhex)[:8] if addresshash == encpriv[6:14]: priv = b58e('80' + priv + privcompress) if outputlotsequence: if lotsequence is not False: lotsequence = int(lotsequence, 16) sequence = lotsequence % 4096 lot = (lotsequence - sequence) // 4096 return priv, lot, sequence else: return priv, False, False else: return priv else: if outputlotsequence: return False, False, False else: return False def testPassword(pwd): try: if bip38decrypt(pwd, encryptedSecret) != False: pwdLenth = 22 + len(pwd) print("\n\n" + "#" * pwdLenth + "\n## PASSWORD FOUND: {pwd} ##\n".format(pwd=pwd) + "#" * pwdLenth + "\n") global flag flag = 1 except: pass finally: td.release() if __name__ == '__main__': COMPRESSION_FLAGBYTES = ['20', '24', '28', '2c', '30', '34', '38', '3c', 'e0', 'e8', 'f0', 'f8'] LOTSEQUENCE_FLAGBYTES = ['04', '0c', '14', '1c', '24', '2c', '34', '3c'] encryptedSecret = input( "Input The EncryptedSecret(eg: 6PYLPNjNnGg9ofuKydMvGDxqTEwxDqfykvGesNHQyRkFTpkjmvzSXmHLnc)\ndefault is 6PYK9gL2qKNYatT6Jrong5CaAJ2d6mv5iNBYfrjwuTbDXLaYvzcncZ3uVQ\n--> ") if len(encryptedSecret) == 0: encryptedSecret = "6PYK9gL2qKNYatT6Jrong5CaAJ2d6mv5iNBYfrjwuTbDXLaYvzcncZ3uVQ" threadNum = input("Input The Number Of Threads(eg: 25)\ndefault is 25\n--> ") if len(threadNum) == 0: threadNum = 25 pwdKeys = input( "Input All The Characters If Your Password Might Contain(eg: 123 test 1024 . p@ss lol 0-0 shit)\ndefault is 0 1 2 3 4 5 6 7 8 9\n--> ").split() if len(pwdKeys) == 0: pwdKeys = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] td = threading.BoundedSemaphore(int(threadNum)) threadlist = [] print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) num = 0 flag = 0 for i in range(len(pwdKeys)): if flag == 1: break if i + 1 > 6: break for pwd in itertools.product(pwdKeys, repeat=i + 1): if flag == 1: break password = "".join(pwd) if len(password) <= 18: num += 1 msg = 'Test Password {num} , {password}'.format(num=num, password=password) sys.stdout.write('\r' + msg) sys.stdout.flush() td.acquire() t = threading.Thread(target=testPassword, args=(password,)) t.start() threadlist.append(t) for x in threadlist: x.join() print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
标签: bitcoin
你是衣冠楚楚的人 而我只是一个打满补丁的猴子
-
小博客一个,没必要伤害她
热门文章
存档
标签
最新评论
- yz
想想你喜欢什么,想做什么,找好一个自己的... - 小屿
@Jahan:testfun1024#p... - Jahan
Hello dear Xia0 i a... - brave
@万:你的手机应该是anroid7.0以... - jhsy
新版的cookie机制应该又变了. 而且... - 小屿
@janto:无兴趣 - janto
新版的这些好像不起作用了,deviceI... - hunk
正在研究,可否发一份新源码?todz$1... - miffy
请问可以加个好友咨询下吗? - vegetableChicken
@Snkrs:我也遇到和你一样的问题了,...
发表评论: