解析比特币区块.dat文件,获得签名r以及其相关信息,并找出相同的r及恢复私钥

写在前面

先在前面说一下,我没有完全实现整个过程,因为有一个地方确实实现不了,等以后有条件再使用别的方法尝试一下。前一段时间看了一篇14年的论文,里面主要说的是比特币中随机数相同会造成私钥泄漏的问题,作者分析了当下存在很多因私钥泄漏,比特币频频被盗的现象,然后找到了问题所在,并且使用Blockchainr工具将其实现了,找出了比特币网络中存在的使用相同随机数的用户私钥。

目的

处于自身学习的角度,我要自己完成作者所提到的步骤,最终找到相同的随机数并且恢复私钥。

整体思路

先编写解析区块文件的程序,将后缀为.dat的区块文件解析出来,然后将数据存储到数据库,之后再借助14年那篇PPT的思想,利用作者提到的dabloom过滤器去找到相同的签名r,这时我们不能仅仅导出签名r,还要导出相关的信息,这样我们才可以知道是否来源于同一用户,才能去计算用户私钥。

具体过程

目前我可以实现解析区块并导出到数据库中,把.dat文件以二进制形式读取,编写区块结构并给出区块中各个字段的长度,以及通过已知字段的值,去读取某些未知长度字段的数据,如:解锁脚本可以通过脚本长度来读取,还有我们可以通过下图这样的思想去解析区块:
idear
从block入手,里面包含blockheader、tx因为一个区块有多条交易,我们定义tx为一个集合Txs,Txs中的每条交易又包含input、output当然它们也会存在多条,所以依然定义为集合,这样我们就可以通过遍历的方式将一个区块的所有数据导出。下面给出部分代码:

class Block:
    def __init__(self,blockchain):
        self.blockheight = 1
        self.continueParsing = True
        self.magicNum = 0
        self.blocksize = 0
        self.blockheader = ''
        self.txCount = 0
        self.Txs = []

这样的话从block入手,就可以逐层解码文件,最终得到完整的区块信息。

ScriptSig

因为我们要找到签名 r r ,所以必须要解析签名脚本,我主要是通过解码 16 16 进制的字符串得到的,做之前我们要清楚解锁脚本的结构,这样才能正确处理,下面给出一张解锁脚本的结构图:
ScriptSig
知道这些的话我们就可以通过脚本长度,签名长度等来进一步分割字符串,并且给出判定条件当前置交易索引不为 0 x f f f f f f f f 0xffffffff 时解析scriptsig,因为相等时交易时coinbase交易,是系统给的交易而不是经过人签名得来的,所以不具备 E C D S A ECDSA 签名脚本,详情可以看我的另外一篇博客:https://blog.csdn.net/qq_35324057/article/details/104072715
下面给出实现这部分的代码:

   if 0xffffffff != self.txpreindex:
            scriptsig = ScriptSig(self.inscriptsig)
            self.sigr = scriptsig.sigR
            self.sigs = scriptsig.sigS
            self.inpubkey = scriptsig.pubkey
        else:
            self.sigr = ''
            self.sigs = ''
            self.inpubkey = ''
class ScriptSig:
    def __init__(self,blockchain):
        # if blockchain[8:10] == '20':
    #         #     self.sigR = blockchain[10:74]
    #         #     if blockchain[76:78] == '20':
    #         #         self.sigS = blockchain[78:142]
    #         #     else:
    #         #         self.sigS = blockchain[78:144]
    #         # else :
    #         #     self.sigR = blockchain[10:76]
    #         #     if blockchain[78:80] == '20':
    #         #         self.sigS = blockchain[78:142]
    #         #     else:
    #         #         self.sigS = blockchain[78:144]
        self.scriptLength = int(blockchain[0:2],16)*2
        self.script = blockchain[2:2 + self.scriptLength]
        self.rLength = int(blockchain[8:10],16)*2
        self.sigR = blockchain[10:10+self.rLength]
        self.sLength = int(blockchain[10+self.rLength+2:10+self.rLength+2+2],16)*2
        self.sigS = blockchain[10+self.rLength+2+2:10+self.rLength+2+2+self.sLength]

        if SIGHASH_ALL != int(blockchain[self.scriptLength:self.scriptLength + 2], 16):
            self.pubkey = "Script op_code is not SIGHASH_ALL"

        else:
            self.pubkey = blockchain[2 + self.scriptLength + 2:2 + self.scriptLength + 2 + 66]

这样就得到了计算私钥需要使用的两个值: s i g R , s i g S sigR,sigS ,但是我们还需要得到交易的哈希值 Z Z ,这一点真的太难了,为什么这么说呢,因为交易的哈希值并不是简单的对区块中的某个值做哈希运算,而是需要找到该交易的 p r e h a s h prehash 所指向的交易,为什么要这样做呢,不知道的小伙伴请看我的上面提到的那篇博客,我们要是用前驱交易的交易输出部分,或者称为加密脚本,并且还需要一些删减增加字符串,然后最终得到我们的交易字符串,对其进行两次 s h a 256 sha256 运算得到 Z Z ,这一点是无法实现的。有时间的话我会单独写一篇关于计算交易哈希值的文章。

Redis

对于redis是什么我就不多说了,就是一种key-value的数据库。

安装

  • pycharm安装redis包:
pip install redis
  • 然后再下载安装redis数据库
    redis
  • 安装RedisDesktopManager -------redis数据库的可视化工具

插入区块数据

这也是我实现的最大的难点,主要就是如果key相同的话,因为每个区块都有相同的字段,所以key的值就会被覆盖,如果一个.dat文件中有多个区块的话,到最后插入的数据也只是最后一个区块的数据,而不能将所有的区块数据插入,实现这一点浪费了我极大的时间和经历,希望分享出来能帮到大家!

思想

主要的思想就是用不同的key来标识不同区块不同tx不同input中的数据。
如:我们用 p r e v i o u s H a s h + previousHash+ 字段名,来标识不同区块的数据,用 p r e v i o u s H a s h + T x s e q + previousHash+Txseq+ 字段名,来标识一个区块中不同的tx中的数据,依次类推,可以得到插入所有数据的方法,下面给出一些代码:

#insert into redis DB

        #blockheader
        # re.set(self.previoushash + 'Version',self.version)
        # re.set(self.previoushash + 'PreviousHash', self.previoushash)
        # re.set(self.previoushash + 'MerkleRoot', self.Hash)
        # re.set(self.previoushash + 'Timestamp', self.time)
        # re.set(self.previoushash + 'Difficulty', self.difficulty)
        # re.set(self.previoushash + 'Nonce', self.nonce)
        #
        # # block
        # re.set(self.previoushash + 'MagicNum', self.magicnum)
        # re.set(self.previoushash + 'Blocksize', self.blocksize)
        # re.set(self.previoushash + 'Txcount', self.txcount)
        #
        # # Tx
        # re.set(self.previoushash + self.txseq + 'TxVersion', self.txversion)
        # re.set(self.previoushash + self.txseq + 'InCount', self.incount)
        # re.set(self.previoushash + self.txseq + 'Txseq', self.txseq)
        # re.set(self.previoushash + self.txseq + 'OutCount', self.outcount)
        # re.set(self.previoushash + self.txseq + 'LockTime', self.locktime)
        #
        # # Input
        # re.set(self.previoushash + self.txseq + self.inputseq + 'TxPrevHash', self.txprevhash)
        # re.set(self.previoushash + self.txseq + self.inputseq + 'TxPreIndex', self.txpreindex)
        # re.set(self.previoushash + self.txseq + self.inputseq + 'InScriptLen', self.inscriptlen)
        # re.set(self.previoushash + self.txseq + self.inputseq + 'InScriptSig', self.inscriptsig)
        # re.set(self.previoushash + self.txseq + self.inputseq + 'InSequence', self.sequence)

取数据

如果往里面存数据理解的话,取数据也很好理解了,主要就是利用了区块中本来就存在几个数据, t x c o u n t txcount i n p u t c o u n t inputcount o u t p u t c o u n t outputcount ,利用这几个数据的话,就可以遍历它们得到数据,很简单就可以实现,如: g e t ( p r e v i o u s H a s h + i + t x v e r s i o n ) get(previousHash+i+'txversion') ,其中 i i 就是从0遍历到 t x c o u n t txcount ,前面还有个东西忘了说,你查询数据用到的数据一定要存储起来,比如 p r e v i o u s H a s h previousHash ,就需要按序号存储起来,并且字段名方便提取,这样的话,提取数据的时候,用起来才会很方便。

for i in range(0,19972):
        hash = re.get('Hash' + str(i))
        txcount = re.get(hash + 'Txcount')
        # print(txcount)
        for j in range(0,int(txcount)):
            # print ("Locktime:%s" % re.get(hash + str(j) + 'InCount'))
            incount = re.get(hash + str(j) + 'InCount')

总结

还有一些地方没有说,就不写了,总之这个过程还是很有收获的,就是在做的过程中很难过,每天需要有点进度,我却卡在数据库上面很长时间,希望大家每天也能有所收获,无论希望多渺茫,都要继续加油!

给出我的blog主页:stride Max Zz.欢迎访问

发布了23 篇原创文章 · 获赞 1 · 访问量 1529

猜你喜欢

转载自blog.csdn.net/qq_35324057/article/details/104864308