前段时间偶然发现了几个手机号 MD5 反查网站:
第一反应不是查号码,而是好奇:这种服务背后到底是什么实现?把 100 亿个手机号全部算一遍,需要多大的数据库、多少 CPU ,普 通服务器能不能跑?
于是我自己写了一套,从建表、索引到 Web 查询都跑了一遍。
先算最直接的方案
覆盖 10000000000 ~ 19999999999 ,一共是 100 亿个数字。
如果做完整索引,即使不保存完整 MD5 ,只保存截断哈希和 40 bit 的号码编号,每条记录压到 5 字节,最终仍然需要约 47 GiB 。
查询会很快,但为了这么一个工具准备几十 GiB 数据,感觉有点重。
换成彩虹表
后来采用了完美彩虹表,把号码连成一条条长度为 1000 的计算链:
手机号 → MD5 → 归约成新手机号 → MD5 → …… → 终点
数据库不保存中间经过的号码,只保存每条链的起点和终点,一条记录 13 字节。
查询时,程序猜测目标 MD5 位于链中的哪一站,然后继续计算到终点,通过磁盘索引找到对应起点,再从头走完整条链,用完整的 128 bit MD5 验证结果。
最终推荐配置是 4 张表:
覆盖空间:100 亿 数据库大小:约 660 MB 理论命中率:约 99.19% 典型命中:约 0.1 ~ 0.3 秒 完整未命中:约 0.65 秒
因为最后会重新计算完整 MD5 ,所以可能漏查,但不会返回一个错误号码。
建表成本有点出乎意料
4 张表最终只保留约 4800 万条链,但为了排序、终点去重和补链,实际需要完成约 1200 亿次 MD5 计算。
这个数字看起来很吓人,不过短字符串 MD5 的计算速度很快。按项目测试的单核约 613 万次/秒估算,纯计算量约为:
1200 亿 ÷ 613 万 ≈ 5.4 CPU 核时
再加上排序、去重和写盘,56 核机器构建 4 张表大约十几分钟。换成 8 核机器,通常也就是一小时左右。云服务器性能、共享 CPU 和磁盘速度不同,实际时间会有差异。
也就是说,建表真正需要的是一次短时间的多核计算,不需要长期保留高配置服务器。
运行成本更低
生成后的数据库只有约 660 MB 。查询器使用 pread() 按桶读取少量数据,不需要把整张表载入内存,也不需要 MySQL 、Redis 或 Elasticsearch 。
低访问量情况下,一台普通的 2 核或 4 核小服务器就能运行。假设每天查询 1 万次,而且极端地全部走到完整未命中:
10000 × 0.65 秒 ≈ 1.8 CPU 核时/天
平均 CPU 占用并不高,真正需要注意的是短时间内的并发。批量查询可以多线程处理,多台机器也可以直接复制同一份只读数据库进 行扩展。
算下来,这种服务最主要的成本可能不是 660 MB 数据,也不是一次性的建表,而是服务器、公网 IPv4 、带宽和后续维护。
我也搭了一个版本
目前支持单个和批量查询,一次最多提交 100 个 MD5:
体验地址: https://tools.waitchenx.cn
它不是“破解任意 MD5”,只能查询目标数字范围内的无盐 MD5 。带盐哈希、HMAC 、其他字符内容和范围外数字都不适用。
做这个项目最有意思的地方,是把一个看起来需要海量存储的问题,变成了一个很具体的时间与空间交换:约 1200 亿次一次性计算, 换来一份 660 MB 、可以长期复用的查询数据库。