彩虹表(还原函数)
还原函数并没有一个全球统一的标准,它的设计取决于创建彩虹表的人想要破解什么类型的密码。它的核心任务很简单:把一个哈希值(看起来像乱码)转换成一个符合常见密码规则的字符串。
核心思想:“捏造”一个可能的密码
想象一下,你有一个哈希值 1a79c4f60...
(很长一串十六进制数字)。还原函数就是一个“加工厂”,它的任务是把这串数字映射到一个像 banana
、password123
、@x!8pL
这样的字符串上。
这个映射需要满足两个条件:
- 确定性: 同一个输入
1a79c4f60...
,必须永远产生同一个输出banana
。 - 输出像密码: 输出的字符串应该在常见的密码字典里,或者符合常见的密码模式。
还原函数是如何“捏造”密码的?—— 常见方法
设计者会定义一个“字符集”和一套“映射规则”。
1. 定义字符集
首先,要确定你的彩虹表打算破解哪些密码。这决定了还原函数可以使用的“字母表”。
- 纯数字密码: 字符集 =
0123456789
- 小写字母密码: 字符集 =
abcdefghijklmnopqrstuvwxyz
- 常见密码集(大小写字母+数字): 字符集 =
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
- 包含符号的复杂密码: 字符集 =
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=
2. 设计映射规则
然后,需要一套规则,把输入的哈希值,转换成字符集里的索引,从而挑出字符,拼成一个字符串。
方法一:取模法(最常用)
假设我们定义的密码长度是6,字符集是62个字符(26大写+26小写+10数字)。
- 步骤:
- 把哈希值当作一个非常巨大的数字(比如叫
H
)。 - 我们想要一个6位的密码,所以就进行6次操作。
- 第一次:
索引1 = H % 62
。用这个索引从字符集里取出一个字符,作为密码的第一个字符。 - 第二次:
H = H / 62
(取整数部分)。然后索引2 = H % 62
。取出第二个字符。 - 重复这个过程,直到凑齐6个字符。
- 把哈希值当作一个非常巨大的数字(比如叫
举个极简的例子:
- 哈希值(当作数字):
123456
- 字符集:
abc...zABC...Z01...9
(共62个) - 计算:
123456 % 62 = 休息,我们假装算出来是 10
-> 取第10个字符(假设是'k'
)123456 / 62 = 1991
(取整)1991 % 62 = 休息,我们假装算出来是 5
-> 取第5个字符(假设是'f'
)- … 如此反复,最终可能得到
"kf9m2n"
。
这个 "kf9m2n"
就是一个由还原函数“捏造”出来的、符合规则的密码。它本身没有意义,但它是一个可能的密码候选。
方法二:截取与重组
- 从哈希值的十六进制字符串中间截取一段。
- 把这段十六进制数转换成十进制。
- 再用这个十进制数通过取模等方法映射到字符集。
为什么这样设计是有效的?
你可能会想,这样“瞎编”出来的密码 kf9m2n
根本没人用啊,有什么用?
关键在于链!
- 覆盖性: 彩虹表不关心单次还原出来的密码是否真实存在。它关心的是,通过 “哈希 -> 还原 -> 哈希 -> 还原” 这条长链,最终能覆盖到一些真实的、常用的密码。
- 概率游戏: 我的链起点是真实的常用密码(如
apple
)。经过几次还原后,虽然中间步骤会生成很多无意义的字符串(如kf9m2n
),但只要这条长长的链上任何一个节点的哈希值,碰巧是我想破解的目标,我就成功了。 - 空间换时间: 通过使用不同的还原函数家族(这是彩虹表相比早期同类技术的高级之处,它使用一系列不同的还原函数R1, R2, R3…来避免“链合并”),可以生成数以亿计的不同链条,这些链条交织成一张巨大的“网”(彩虹),网住了海量的可能密码。
总结
- 还原函数是人为设定的,它是一套简单的、确定性的算法。
- 它的输入是哈希值,输出是一个“像”密码的字符串。
- 它的设计目标是让生成的字符串落在你想要破解的密码类型空间里(比如纯数字、或字母数字组合等)。
- 它不需要、也不可能从哈希值反推出原始的、有意义的密码。它的作用是在建链过程中,从一个哈希值“跳”到下一个可能的密码候选,从而将海量的密码信息压缩存储。
简单说,还原函数就是一个“密码猜想的流水线”,它让彩虹表能够用有限的存储空间,去关联和代表近乎无限的密码可能性。