You can also use this tool to extract your private keys so you can sweep them into a new wallet.
Yeah over the years i became too familiar with pywallet. had/still has some bugs now and then but it does the trick.
I do have an issue with this wallet file. i have different occurances of a key in the key pool i can decode the data after reading it using pywallet or any other manual berkeley database access methods.
I used
also checked with
i came to understanding some keys may be missing or misplaced, didnt know that my keypool data had timestamp it was created at, it was useful. also the version the wallet software used to create it. its useful because if i load the wallet file into another version and generate new keys it stands out still but it still doesn't find a key. so i need a wallet with the least amount of keys in the keypool right.
i found one wallet with a consistent key pool however, the last oldest keys are a few hours and 200 counts off from the 2 keys that are missing the private keys.
btw check this out https://kewde.github.io/corrupted-bitcoin-wallet its a really useful method. inspired me to look at other methods.
i noticed today if i load the wallet file and use hxd.exe to read the memory, the private keys are stacked on top of each other (without the der or whatever you call it, just the ~64 char hex), if i view at 32 byte width.
if one key starts with 022c9992019292290s8sshns and has private hex of 4292ff8f39393930222
and the pubkey i am looking for is for example 022c89383839u9ddffff i just need to search for 022c9992019292290s8sshns and find 4292ff8f39393930222
and then wherever 4292ff8f39393930222is located there will be keys stacked over and below it, without any other data just 32 byte keys concatenated.
all priv hex keys surrounding the 4292ff8f39393930222 will have pubkeys with similiar prefix to 022c9992019292290s8sshns
dunno why im sharing this finding its just useless for me because my key is stuck, and i have life savings stuck on this charge address.
anyway today i edited
# This software is
# Copyright (c) 2012-2018 Dhiru Kholia
# Copyright (c) 2019 Solar Designer
# Copyright (c) 2019 exploide
# Redistribution and use in source and binary forms, with or without
# modification, are permitted. (This is a heavily cut-down "BSD license".)
#
# While the above applies to the stated copyright holders' contributions,
# this software is also dual-licensed under the MIT License, to be certain
# of license compatibility with that of the components listed below.
#
# This script (bitcoin2john.py) might still contain portions of jackjack's
# pywallet.py [1] which is forked from Joric's pywallet.py whose licensing
# information follows,
#
# [1] https://github.com/jackjack-jj/pywallet
#
# PyWallet 1.2.1 (Public Domain)
# http://github.com/joric/pywallet
# Most of the actual PyWallet code placed in the public domain.
# PyWallet includes portions of free software, listed below.
#
# BitcoinTools (wallet.dat handling code, MIT License)
# https://github.com/gavinandresen/bitcointools
# Copyright (c) 2010 Gavin Andresen
import binascii
import logging
import struct
import sys
try:
from bsddb.db import *
except:
try:
from bsddb3.db import *
except:
sys.stderr.write("Error: This script needs bsddb3 to be installed!\n")
sys.exit(1)
json_db = {}
def hexstr(bytestr):
return binascii.hexlify(bytestr).decode('ascii')
# bitcointools wallet.dat handling code
class SerializationError(Exception):
""" Thrown when there's a problem deserializing or serializing """
class BCDataStream(object):
def __init__(self):
self.input = None
self.read_cursor = 0
def clear(self):
self.input = None
self.read_cursor = 0
def write(self, bytes): # Initialize with string of bytes
if self.input is None:
self.input = bytes
else:
self.input += bytes
def read_string(self):
# Strings are encoded depending on length:
# 0 to 252 : 1-byte-length followed by bytes (if any)
# 253 to 65,535 : byte'253' 2-byte-length followed by bytes
# 65,536 to 4,294,967,295 : byte '254' 4-byte-length followed by bytes
# ... and the Bitcoin client is coded to understand:
# greater than 4,294,967,295 : byte '255' 8-byte-length followed by bytes of string
# ... but I don't think it actually handles any strings that big.
if self.input is None:
raise SerializationError("call write(bytes) before trying to deserialize")
try:
length = self.read_compact_size()
except IndexError:
raise SerializationError("attempt to read past end of buffer")
return self.read_bytes(length).decode('ascii')
def read_bytes(self, length):
try:
result = self.input[self.read_cursor:self.read_cursor + length]
self.read_cursor += length
return result
except IndexError:
raise SerializationError("attempt to read past end of buffer")
return ''
def read_uint32(self): return self._read_num('
def read_compact_size(self):
size = self.input[self.read_cursor]
if isinstance(size, str):
size = ord(self.input[self.read_cursor])
self.read_cursor += 1
if size == 253:
size = self._read_num('
size = self._read_num(' elif size == 255:
size = self._read_num(' return size
def _read_num(self, format):
(i,) = struct.unpack_from(format, self.input, self.read_cursor)
self.read_cursor += struct.calcsize(format)
return i
def open_wallet(walletfile):
db = DB()
DB_TYPEOPEN = DB_RDONLY
flags = DB_THREAD | DB_TYPEOPEN
try:
r = db.open(walletfile, "main", DB_BTREE, flags)
except DBError as e:
logging.error(e)
r = True
if r is not None:
logging.error("Couldn't open wallet.dat/main. Try quitting Bitcoin and running this again.")
logging.error("See our doc/README.bitcoin for how to setup and use this script correctly.")
sys.exit(1)
return db
def parse_wallet(db, item_callback):
kds = BCDataStream()
vds = BCDataStream()
for (key, value) in db.items():
d = { }
kds.clear(); kds.write(key)
vds.clear(); vds.write(value)
type = kds.read_string()
d["__key__"] = key
d["__value__"] = value
d["__type__"] = type
try:
if type == "mkey":
#d['nID'] = kds.read_uint32()
d['encrypted_key'] = vds.read_bytes(vds.read_compact_size())
d['salt'] = vds.read_bytes(vds.read_compact_size())
d['nDerivationMethod'] = vds.read_uint32()
d['nDerivationIterations'] = vds.read_uint32()
#d['otherParams'] = vds.read_string()
item_callback(type, d)
except Exception:
sys.stderr.write("ERROR parsing wallet.dat, type %s\n" % type)
sys.stderr.write("key data in hex: %s\n" % hexstr(key))
sys.stderr.write("value data in hex: %s\n" % hexstr(value))
sys.exit(1)
# end of bitcointools wallet.dat handling code
# wallet.dat reader
def read_wallet(json_db, walletfile):
db = open_wallet(walletfile)
json_db['mkey'] = {}
def item_callback(type, d):
if type == "mkey":
#json_db['mkey']['nID'] = d['nID']
json_db['mkey']['encrypted_key'] = hexstr(d['encrypted_key'])
json_db['mkey']['salt'] = hexstr(d['salt'])
json_db['mkey']['nDerivationMethod'] = d['nDerivationMethod']
json_db['mkey']['nDerivationIterations'] = d['nDerivationIterations']
#json_db['mkey']['otherParams'] = d['otherParams']
parse_wallet(db, item_callback)
db.close()
crypted = 'salt' in json_db['mkey']
if not crypted:
sys.stderr.write("%s: this wallet is not encrypted\n" % walletfile)
return -1
return {'crypted':crypted}
if __name__ == '__main__':
if len(sys.argv) < 2:
sys.stderr.write("Usage: %s [Bitcoin/Litecoin/PRiVCY wallet (.dat) files]\n" % sys.argv[0])
sys.exit(1)
for i in range(1, len(sys.argv)):
filename = sys.argv[i]
if read_wallet(json_db, filename) == -1:
continue
cry_master = binascii.unhexlify(json_db['mkey']['encrypted_key'])
cry_salt = binascii.unhexlify(json_db['mkey']['salt'])
cry_rounds = json_db['mkey']['nDerivationIterations']
cry_method = json_db['mkey']['nDerivationMethod']
crypted = 'salt' in json_db['mkey']
if not crypted:
sys.stderr.write("%s: this wallet is not encrypted\n" % filename)
continue
if cry_method != 0:
sys.stderr.write("%s: this wallet uses unknown key derivation method\n" % filename)
continue
cry_salt = json_db['mkey']['salt']
if len(cry_salt) == 16:
expected_mkey_len = 96 # 32 bytes padded to 3 AES blocks (last block is padding-only)
elif len(cry_salt) == 36: # Nexus legacy wallet
expected_mkey_len = 160 # 72 bytes padded to whole AES blocks
else:
sys.stderr.write("%s: this wallet uses unsupported salt size\n" % filename)
continue
# When cracking we only use the last two AES blocks, and thus we could support
# any encrypted master key size of 32 bytes (64 hex) or more. However, there's
# no reliable way for us to infer what the unencrypted key size was before it
# got padded to whole AES blocks, and thus no way for us to confidently detect
# correct guesses by checking the last block's padding. We rely on that check
# for expected encrypted master key sizes (assuming that 48 was 32, and 80 was
# 72, like specific known wallets use), but we don't dare to do that for
# unexpected sizes where we'd very likely end up with 100% (false) negatives.
if len(json_db['mkey']['encrypted_key']) != expected_mkey_len:
sys.stderr.write("%s: this wallet uses unsupported master key size\n" % filename)
continue
cry_master = json_db['mkey']['encrypted_key'][-64:] # last two AES blocks are enough
sys.stdout.write("$bitcoin$%s$%s$%s$%s$%s$2$00$2$00\n" %
(len(cry_master), cry_master, len(cry_salt), cry_salt, cry_rounds))
from https://github.com/openwall/john/blob/bleeding-jumbo/run/bitcoin2john.py
it has the good parts from pywallet and bitcointools (i used bitcointools scripts too) managed to extract the data without issues.. it has the most refferences to the data my wallet has...
https://github.com/openwall/john/blob/75667e266c2b9356504a86c6013b91a9a3e48c4e/run/bitcoin2john.py#L173 over here i added print(json_db) because the script was useless otherwise. from a 350mb wallet.dat file, it outputs about 1.5gb
each transaction has a tx_v and tx_k like with pywallet.
however in other wallet files i dump that contain similiar keys to this oldeest backup i found, each tx_v is usually the same size as the transaction in the blockchain but with "from account " metadata appended to it in hex.
but with this wallet each transaction value is probably a few kb in size. they do contain data for previous outs in the same block though maybe thats why'?
i mean if my wallet sent 10 transactions in one block it has the same transaction in 1 tx_v i think. but the data is just large.
i do remember loads of cves showing up and bug reports for old wallets, i think also in the debug log there are messages of orphans and stuff. but my question is, i dunno. is there any tricks that i can do in a low level way to find the private key. im coming up with tons of new techniques to extract new key values but none of them are correspnding to this pubkey.
tx_v however is unlike the version 60000+ wallets, my ones 60400, i do have compressed keys.