Cocoa

Posted on Nov 06, 2022Read on Mirror.xyz

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

上篇文章之后,我们继续说说助记词靓号钱包的事情(话说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 ,欢迎各位大佬关注!

<>