安全生成助记词靓号钱包地址

上篇文章之后,我们继续说说助记词靓号钱包的事情(话说Cocoa到底对靓号有多执着啊……

1.一点背景

这篇文章的起因是余弦大大提到了一例钱包被盗案,主要原因是受害者在一个钓鱼网站输入了助记词,导致了该助记词下2个钱包内的余额被盗。

上篇文章提到使用Profanity2工具生成靓号地址,最后得到的是钱包地址和对应的私钥,并不是助记词。

而大家知道,Metamask等工具在生成钱包时,首先会生成助记词,然后默认生成一个其钱包地址(Metamask叫账户)。如果有需要,可以点击“创建账户”按钮,继续创建第二个、第三个等等钱包地址,这些钱包地址都有各自的私钥。

那么助记词和私钥有什么关系呢?为什么助记词丢失,会导致Metamask下的所有钱包都被盗呢?

2.分层确定性钱包(HD Wallet)

这里就涉及到一个概念“分层确定性钱包”(Hierarchical Deterministic Wallet,简称叫HD Wallet或者HD钱包)。

HD钱包的核心来自于三个标准:BIP32、BIP44、BIP39。这里BIP的意思是Bitcoin Improvement Proposals(比特币改进提案),类似于网络领域的RFC标准。

这三个标准具体内容有兴趣的朋友可以搜一下,我这里简单总结一下:

  • BIP32:通过一个随机数(被称为种子seed),按照事先确定的算法,派生出大量的私钥。这样一来,只要保管好种子,就可以管理成千上万的地址,降低的维护的成本。顺便一提,BIP32 的名称直接就叫做“分层确定性钱包”。

  • BIP44:这是对BIP32的改进,在原有只支持比特币的基础上,通过在钱包内部规定路径path的方式,使钱包可以支持多条公链(如狗狗币、莱特币、以太坊等),进一步拓展了HD钱包的应用范围。例如:Metamask上以太坊的path就是m/44’/60’/0’/0/x,其中x就代表是第几个账户。

  • BIP39:继续对HD钱包进行改进:由于种子冗长难记,BIP39提出了助记词的概念,将seed转换为12个或24个单词(也可以是日文、韩文甚至中文,只不过现在英文用的较多),叫做助记词。这样只要人类记住12个单词,就可以通过程序将助记词转化为种子seed,再通过种子seed根据不用路径path生成不同币种的私钥,这样就完成了从助记词到比特币、以太坊、狗狗币等各类钱包地址的逐级转化。

总结一下,HD钱包生成以太坊地址(或者账号)的步骤就是:

1.先按照BIP32要求生成种子,2.根据BIP39将种子转化为人类可读的助记词,3.通过种子产生多个以太坊私钥,从而派生出对应的地址。

其中种子和助记词其实本质是一样的,因此就不难理解上面兄弟遇到的问题:丢失了助记词后,由助记词产生(其实是种子产生)的多个以太坊地址都失窃了。

3.生成助记词靓号地址

知道了原理,我们就可以通过不停枚举助记词(其实是种子),来暴力搜索靓号地址了。

找了一下好像没有现成的工具,没关系,毛主席教导我们:自己动手,丰衣足食。

这里以Cocoa熟悉的Python为例,nodejs和java也有类似库可以参考。

首先要用到一个叫hdwallet的库,它里面已经集成好了HD钱包所需的BIP标准相关代码。如果是Python 3.10以下版本,直接使用如下命令安装即可。如果是最新的Python 3.11,会遇到一些问题报错,后面有机会再聊。

pip install hdwallet

安装完成后,直接在代码里引入即可。

#!/usr/bin/env python3
from multiprocessing import Pool
from hdwallet import BIP44HDWallet
from hdwallet.cryptocurrencies import EthereumMainnet
from hdwallet.derivations import BIP44Derivation
from hdwallet.utils import generate_mnemonic
from typing import Optional

#线程数,一般几核CPU写几
THREAD_NUM = 6
#靓号地址前缀
PREFIX = '0xc0c0a'
#靓号地址后缀
SUFFIX = '8'

def to_work():
    while 1:
        # Generate english mnemonic words
        MNEMONIC: str = generate_mnemonic(language="english", strength=128)
        # Secret passphrase/password for mnemonic
        PASSPHRASE: Optional[str] = None  # "meherett"
        # Initialize Ethereum mainnet BIP44HDWallet
        bip44_hdwallet: BIP44HDWallet = BIP44HDWallet(cryptocurrency=EthereumMainnet)
        # Get Ethereum BIP44HDWallet from mnemonic
        bip44_hdwallet.from_mnemonic(
            mnemonic=MNEMONIC, language="english", passphrase=PASSPHRASE
        )
        # Clean default BIP44 derivation indexes/paths
        bip44_hdwallet.clean_derivation()
        # Derivation from Ethereum BIP44 derivation path
        bip44_derivation: BIP44Derivation = BIP44Derivation(
            cryptocurrency=EthereumMainnet, account=0, change=False, address=0
        )
        # Drive Ethereum BIP44HDWallet
        bip44_hdwallet.from_path(path=bip44_derivation)
        wallet_address=bip44_hdwallet.address()
        # 这里的例子是生成一个c0c0a开头8结尾的靓号地址,注意开头的0x不要忘记
        if (wallet_address.startswith(PREFIX) and wallet_address.endswith(SUFFIX)):
            print("Mnemonic:", bip44_hdwallet.mnemonic())
            print(f"{bip44_hdwallet.path()} {wallet_address} 0x{bip44_hdwallet.private_key()}")
        # Clean derivation indexes/paths
        bip44_hdwallet.clean_derivation()

def main():
    po = Pool(THREAD_NUM)
    for i in range(THREAD_NUM):
        po.apply_async(to_work)
    po.close()
    po.join()

if __name__ == "__main__":
    main()

这是一个简单的demo版本,主体部分是根据hdwallet的官方示例代码修改的,增加了靓号判断逻辑和多线程。

后续也有改进优化的地方,比如增加EIP 55大小写校验机制(可以生成自定义大小写的靓号地址)、找到一个地址后自动停止运行(现在除非手动结束会一直跑)、改造成GPU显卡加速等等。感兴趣的朋友可以自己试试。

小弟的twitter:@featherye ,欢迎各位大佬关注!

Subscribe to Cocoa
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.