- 密码学
- 逆向工程
- CLI
【免费下载链接】winrar-keygen
Principle of WinRAR key generation.
导读:WinRAR 的授权文件rarreg.key并非简单的用户名拼接,而是由一套基于椭圆曲线密码学(ECC)的签名算法动态生成。本文以仓库文档 README.HOW_DOES_IT_WORK.md 为骨架,完整拆解其背后的数学构造——定义在复合域 GF((2^15)^17) 上的 SM2 变体数字签名方案,包括域构造、椭圆曲线参数、特殊 SHA1 哈希处理、签名与私钥派生算法,以及最终组装rarreg.key的 13 步完整流程,并逐一对齐仓库源码中的实现细节。读完本文,你将能理解 WinRAR 授权文件的全部生成链路,并能在源码中找到每一步对应的实现位置。
1. 背景:rarreg.key 与 SM2 变体 ECC 签名
WinRAR 使用基于 ECC 的签名算法来生成rarreg.key。该算法是中国 SM2 数字签名算法的一个变体,与常见的标准 ECDSA 不同,WinRAR 选用的椭圆曲线定义在一个复合域GF((2^15)^17) 之上。
整个算法链条可以概括为五个环节,也是本文随后依次展开的主线:
- 复合域构造:GF(2^15) 的 17 次扩张,得到 GF((2^15)^17);
- 椭圆曲线:定义在复合域上的二元域曲线及其基点 G、阶 n;
- 消息哈希:将输入消息的 SHA1 状态值直接构造成大整数 h;
- 签名算法:SM2 变体的 ECC 签名,输出 (r, s);
- 密钥派生:由用户名等输入数据派生私钥,再组装输出
rarreg.key。
2. 复合域 GF((2^15)^17) 的构造
2.1 基域 GF(2^15)
基域 GF(2^15) 中的元素采用**标准基(多项式基)**表示,其不可约多项式为:
即x^15 + x + 1,其中每个系数都在 GF(2) 中。若以
(即 {1, α, α², …, α^14})作为基域的标准基,则 GF(2^15) 中的任意元素 A 可以表示为:
由于 x^15 + x + 1 是本原多项式,GF(2^15) 的乘法群由 α 生成,阶为 2^15 − 1 = 0x7FFF。这一点在源码中体现为GF2p15LogExpTableType = uint16_t[0x8000]的对数/指数查表(参见 WinRarConfig.hpp):InitializeGF2p15Table用temp *= 2,若溢出则temp ^= 0x8003完成模 x^15+x+1 归约,恰好对应生成元乘法。
2.2 二次扩张构造复合域
复合域 GF((2^15)^17) 的不可约多项式为:
即y^17 + y^3 + 1,其中每个系数都在 GF(2^15) 中。以
(即 {1, β, β², …, β^16})作为复合域标准基,则复合域中任意元素 B 可表示为:
源码中GF2p15p17Traits(WinRarConfig.hpp)正是这一结构的直接映射:ElementType为uint16_t Items[17],即 17 个 15 位系数;BitSizeValue = 15 * 17 = 255;Verify校验每个系数 < 0x8000;乘法先做教科书式全乘(FullMultiplySchoolBook,依赖 GF(2^15) 对数/指数表),再按y^17 + y^3 + 1归约(ModularReduction中A[i-17] ^= A[i]、A[i-14] ^= A[i]对应 y^17 与 y^3 两项)。
2.3 元素与 255 位整数的映射
为表述方便,文档用255 位整数 D表示复合域中的元素 B,其映射关系为:
从 README.VERIFY_Point_G.md 给出的转换 Python 代码可以看出,这个映射按**每 15 位一段(小端顺序)**把 255 位整数切分成 17 个 15 位系数:
def to_field_repr(val, bits=15, count=17): mask = (1 << bits) - 1 result = [] for _ in range(count): result.append(val & mask) val >>= bits return result3. 定义在复合域上的椭圆曲线
WinRAR 使用的椭圆曲线方程为:
即二元域标准曲线形式y² + xy = x³ + ax² + b中取 a = 0、b = 161(0xA1)。这与源码中曲线定义完全一致(WinRarConfig.hpp):
static inline const EllipticCurveGF2m<GaloisField<GF2p15p17Traits>> Curve{ { GaloisFieldInitByZero{} }, // A = 0 { GaloisFieldInitByElement{}, { 161 } } // B = 161 };基点 G 为:
其大整数表示为(README.VERIFY_Point_G.md):
Gx = 0x56fdcbc6a27acee0cc2996e0096ae74feb1acf220a2341b898b549440297b8cc Gy = 0x20da32e8afc90b7cf0e76bde44496b4d0794054e6ea60f388682463132f931a7转换到 17×15 位小端分段表示后,与 WinRarConfig.hpp 中基点 G 的 34 个 16 位系数逐一对齐(Gx 系数以0x38CC, 0x052F, …, 0x56FD开头,Gy 系数以0x31A7, 0x65F2, …, 0x20DA开头)。仓库还提供了完整的落点验证脚本 README.VERIFY_Point_G.md:在纯 Python 中实现 GF(2^15) 乘法(模 x^15+x+1)、GF((2^15)^17) 多项式乘法与归约(模 y^17+y^3+1),然后通过y² + xy == x³ + b校验 G 与公钥 PK 是否在曲线上。
基点 G 的阶 n 为:
即n = 0x1026dd85081b82314691ced9bbec30547840e4bf72d8b5e0d258442bbcd31(WinRarConfig.hpp),共 241 位。这个阶的位长是后续私钥合法性判断的关键(见第 6 节)。
4. 消息哈希:SHA1 状态值直接构造成大整数
设输入消息为:
即长度为 l 的消息 M。其标准 SHA1 值为:
其中 s₀…s₄ 是 SHA1 输出时的 5 个状态值。通常,最终 SHA1 值是这 5 个状态值按大端序各自序列化后拼接而成的 160 字节(实为 20 字节)摘要。
然而 WinRAR 并不对这 5 个状态值做标准序列化。相反,它直接用一个大整数 h 作为输入消息的哈希:
源码实现GenerateHashInteger(WinRarKeygen.hpp)印证了这一点:它把 SHA1 的 5 个 32 位状态字逐个大端字节序转换后写入RawHash[0..4],再填入RawHash[5..9]为固定常量(注释注明是"SHA1("") with all-zeroed initial value",即0x0ffd8d43, 0xb4e33c7c, 0x53461bd1, 0x0f27a546, 0x1050d90d),最终取前240 位构成哈希整数 h。
5. ECC 数字签名算法(SM2 变体)
设私钥为 k、公钥为 P,则必然有:
即P = k·G(源码中GeneratePublicKey直接计算__ConfigType::G * PrivateKey,见 WinRarKeygen.hpp)。
以 h 表示输入数据的哈希,WinRAR 使用如下算法完成签名:
生成随机大整数 Rnd,满足条件:
即 0 < Rnd < n。实现上
GenerateRandomInteger(WinRarKeygen.hpp)以srand(time(nullptr))为种子,生成 15 个 16 位随机值组成 240 位整数——由于 240 位 < 241 位的阶 n,天然满足 Rnd < n。计算 r:
其中 X(Rnd·G) 表示取 Rnd·G 的 X 坐标,并把它从 GF((2^15)^17) 转换回大整数。源码(WinRarKeygen.hpp)为:
Signature.r.Load(false, (__ConfigType::G * Random).GetX().Dump(), true); Signature.r += Hash; Signature.r %= __ConfigType::Order;若 r = 0 或 r + Rnd = n(即 r ≡ −Rnd mod n),则回到步骤 1 重新生成随机数。
计算 s:
即s = (Rnd − k·r) mod n。源码(WinRarKeygen.hpp)为:
Signature.s = Random - __ConfigType::PrivateKey * Signature.r; Signature.s %= __ConfigType::Order;若 s = 0,回到步骤 1。
输出签名对 (r, s)。
与标准方案对比:标准 SM2 中 r 取(e + x1) mod n(e 为经 ZA 处理的哈希),而 s 形如(1 + dA)^−1 · (k − r·dA) mod n;WinRAR 保留了 r 的"哈希加 x 坐标"形态(此处 e 直接取 240 位哈希整数 h),但 s 的计算简化为了Rnd − k·r mod n,省略了(1+d)^−1因子。从公式结构与源码实现(WinRarKeygen.hpp)可以推断,这是 SM2 框架下的一种简化变体,而非标准的 ECDSA(标准 ECDSA 的 s 还包含随机数逆元项)。
6. WinRAR 私钥生成算法(种子 → 240 位私钥)
设输入数据为:
即长度为 l 的数据 T。WinRAR 用它生成私钥 k,过程如下:
用 g₀…g₅ 表示 6 个 32 位整数,满足:
令g₀ = 0。
若 l ≠ 0,计算 T 的 SHA1 值,并把 SHA1 状态值 Sᵢ 赋给 gᵢ₊₁(i = 0…4):
否则(l = 0)令:
即 g₁…g₅ 取固定常量。源码中对应
GeneratePrivateKey(WinRarKeygen.hpp):有种子时取 SHA1 摘要的 5 个 32 位字(大端转换后)填入Generator[1..5];无种子时直接填入0xeb3eb781, 0x50265329, 0xdc5ef4a3, 0x6847b9d5, 0xcde43b4c。把 g₀ 视为计数器,自增 1,计算:
取 S₀ 的最低 16 位记为 K_g0。对应源码循环(WinRarKeygen.hpp):
Generator[0] = i + 1,对 24 字节的 Generator 计算 SHA1,取第一个状态字的低 16 位写入RawPrivateKey[i]。将步骤 4 再重复14 次(共 15 轮,i = 1…15),得到 k₁…k₁₅。
输出私钥:
即k = k₁ + k₂·2¹⁶ + k₃·2³² + … + k₁₅·2²²⁴,是一个 15×16 = 240 位的整数。源码注释(WinRarKeygen.hpp)特别指出:
Order有 241 位,而RawPrivateKey最多只有 240 位,因此派生出的私钥必然小于阶 n,必然是合法私钥,无需再做范围校验。
7. WinRAR 的固定私钥与公钥
WinRAR 自己的私钥 k 为:
即k = 0x59fe6abcca90bdb95f0105271fa85fb9f11f467450c1ae9044b7fd61d65e(240 位,见 WinRarConfig.hpp)。该私钥正是用第 6 节的算法、以长度 l = 0 的空数据为种子生成的(源码注释标注"Generated byWinRarKeygen<WinRarConfig>::GeneratePrivateKey(nullptr, 0);")。
对应的公钥 P 为:
源码中以 17×15 位小端分段给出(WinRarConfig.hpp):PKx 系数以0x3A1A, 0x1109, …, 0x3861开头,PKy 系数以0x6C20, 0x6027, …, 0x12B6开头,与 README.VERIFY_Point_G.md 中的验证脚本输入完全一致。
8. rarreg.key 的完整生成流程
生成授权文件rarreg.key需要两个输入参数:
- 用户名 U:ANSI 编码字符串(不含null 终止符)。
- 授权类型 L:ANSI 编码字符串(不含null 终止符)。
完整流程如下(源码总入口为GenerateRegisterInfo,见 WinRarKeygen.hpp):
8.1 由用户名派生密钥对并得到 Temp
用第 6 节算法以 U 为种子生成私钥 kᵤ 与公钥 pᵤ,然后将公钥以SM2 压缩公钥格式输出为十六进制字符串 Temp。Temp 长度应为 64,不足 64 位时用字符'0'左侧补齐。对应源码GeneratePublicKeySM2CompressedFormat(WinRarKeygen.hpp):公钥先按 SEC1 压缩(0x02/0x03前缀 + X 坐标),再变换为"2·x + 奇偶位"的 256 位整数,十六进制化后补齐到 64 字符。
8.2 由 Data3 派生 Data0
令 Data3 为:
即固定前缀"60"后接 Temp 的前 48 个十六进制字符(共 50 字符),对应源码HelperStringFormat("60%.48s", temp.c_str())。再用第 6 节算法以 Data3 为种子生成私钥 k_data3 与公钥 p_data3,同样以 SM2 压缩公钥格式输出十六进制串Data0,长度 64,不足补'0'。
8.3 计算 UID
令 UID 为:
即UID = Temp 的后 16 个字符 + Data0 的前 4 个字符(共 20 个十六进制字符),对应源码HelperStringFormat("%.16s%.4s", temp.c_str() + 48, RegInfo.Items[0].c_str())。
8.4 对授权类型签名得到 Data1
用第 5 节签名算法,以 L 为消息、第 7 节的固定私钥 k 签名,得到 (rₗ, sₗ)。rₗ 与 sₗ 的位长不得超过 240,否则重复本步。转换为不带"0x"前缀的十六进制串 SZrₗ、SZsₗ,长度不足 60 时补'0'。然后令 Data1 为:
即Data1 = "60" + SZsₗ + SZrₗ(2 + 60 + 60 = 122 字符)。源码循环直至 r、s 十六进制串均恰好为 60 长度,再以"60%s%s"(先 s 后 r)格式化(WinRarKeygen.hpp)。
8.5 对 (U + Data0) 签名得到 Data2
重新令 Temp 为:
即Temp = U + Data0(对应源码temp = RegInfo.UserName + RegInfo.Items[0];)。用第 5 节算法以 Temp 为消息、固定私钥 k 签名,得到 (r_Temp, s_Temp),位长同样不得超过 240。转换为十六进制串 SZr_Temp、SZs_Temp(无"0x"前缀,不足 60 补'0'),令 Data2 为:
即Data2 = "60" + SZs_Temp + SZr_Temp(122 字符),对应源码HelperStringFormat("60%s%s", …)(WinRarKeygen.hpp)。
8.6 计算 CRC32 校验和
对以下消息计算 CRC32:
即L + U + Data0 + Data1 + Data2 + Data3的拼接。最终校验和为 CRC32 值的按位取反(补码),再转换为十进制字符串 SZchecksum,不足 10 位时补'0'。源码CalculateChecksum(WinRarKeygen.hpp)依次对 LicenseType、UserName、Items[0…3] 调用Crc32.Update,最终Info.Checksum = ~Crc32.Evaluate(),其中 CRC32 使用反射多项式0xEDB88320(HasherCrc32Traits.hpp)。
8.7 组装 Data 并输出
令 Data 为:
即Data = 各段长度(十进制)拼接 + Data0 + Data1 + Data2 + Data3 + 校验和。源码格式为"%zu%zu%zu%zu%s%s%s%s%010lu"(WinRarKeygen.hpp):先输出 Data0、Data1、Data2、Data3 的长度 64、122、122、50,即前缀"6412212250",随后接四个数据段,最后是 10 位十进制校验和。整串共 10 + (64+122+122+50) + 10 = 378 字符,恰好 378 ÷ 54 = 7 行整——这也解释了为何最终文件按每行 54 字符输出(源码中若长度非 54 的倍数会抛出InternalError)。
最终文件按如下格式输出:
固定首行
RAR registration data;用户名,占一行;
授权类型,占一行;
UID,占一行,格式为:
即
UID=+ 20 位十六进制串;Data 内容,每行 54 字符。
仓库 README.md 给出了一个真实输出样例,可与上述结构逐行核对:
RAR registration data Github Single PC usage license UID=3a3d02329a32b63da7d8 6412212250a7d8753c5e7037d83011171578c57042fa30c506caae 9954e4853d415ec594e46017cb3db740bc4b32e47aea25db62f350 9f22065a27da4f8216d2938e1050b6e3347501a3767d1fdd7ee130 dd4ab952600ba16a99236d910bfa995d5f60651ec451f462511507 95b3722d059f2d5303a231e396cf21f17098edeec0b6e3347501a3 767d1fdd7ee45388769767642338ee8a63178f3458b71de5609b18 5eede7ed46566b10bf033daa6384062b259194b1acbd0378116064可以看到第 4 行 UID 恰为 20 位十六进制,第 5 行以6412212250(四个数据段长度)开头,后续共 7 行 × 54 字符。
9. 源码级对照:命令行使用与验证
整个算法在仓库中的实现入口为WinRarKeygen<WinRarConfig>::GenerateRegisterInfo(WinRarKeygen.hpp),它返回RegisterInfo(含 UserName、LicenseType、UID、Items[4]、Checksum、HexData),随后由命令行程序 _tmain.cpp 负责编码转换与文件输出。
命令行用法(详见 README.md):
winrar-keygen.exe <Username> <LicenseName> [options] winrar-keygen.exe -v | --version winrar-keygen.exe -h | --help| 参数 | 说明 |
|---|---|
-e, --encoding <enc> | 编码:utf8(默认)、ascii、ansi |
-o, --output <file> | 输出文件路径(默认rarreg.key) |
-a, --activate | 直接写入%APPDATA%\WinRAR\rarreg.key |
-t, --text | 仅打印到控制台,不写文件 |
-u, --update | 检查更新 |
-v, --version | 显示版本 |
-h, --help | 显示帮助 |
一个 ASCII 编码的示例(README.md):
./winrar-keygen.exe "Github" "Single PC usage license" -t -e ascii需要说明的编码前提:本文档描述的生成算法中,用户名与授权类型均以ANSI 编码(不含 null 终止符)参与哈希与签名;而 CLI 默认utf8编码,非 ASCII 字符会按 README 所述自动添加utf8:前缀;ansi编码依赖当前 Windows 区域代码页(如 Windows-1252),跨区域可能产生乱码;ascii编码只接受纯 ASCII 字符。这与文档"ANSI 编码字符串"的前提是配套的——内部算法只关心参与运算的字节串,编码层由 CLI 负责。
若希望独立验证曲线参数的正确性,仓库提供了不依赖 C++ 代码的纯 Python 验证脚本 README.VERIFY_Point_G.md,可独立运行并打印Verify whether the point G is on the curve: True与Verify whether the PK is on the curve: True,是理解复合域运算与曲线方程最直观的入门材料。
10. 总结
rarreg.key的生成可以浓缩为一条完整的密码学链路:
- 域:GF((2^15)^17) = GF(2^15)[y]/(y^17+y^3+1),基域不可约多项式为 x^15+x+1;
- 曲线:y² + xy = x³ + b(a = 0,b = 161),基点 G、241 位阶 n 见 WinRarConfig.hpp;
- 哈希:SHA1 的 5 个状态值不做标准序列化,直接拼成大整数 h(WinRarKeygen.hpp);
- 签名:r = X(Rnd·G) + h mod n,s = (Rnd − k·r) mod n,是 SM2 的一种简化变体(WinRarKeygen.hpp);
- 私钥派生:以输入串为种子经 15 轮 SHA1 截位生成 240 位私钥(WinRarKeygen.hpp);
- 组装:用户名公钥(SM2 压缩格式)→ Data3 → Data0 → UID → 两次签名(Data1、Data2)→ CRC32 补码校验和 → 按 54 字符每行输出。
整个设计最值得注意的两点在于:其一是复合域 GF((2^15)^17)而非主流标准曲线所采用的素数域或单一扩域,配合对数/指数查表实现高速乘法;其二是签名公式对 SM2 的简化(s 省略了 (1+d)^−1 因子),以及哈希采用状态值直构大整数的非标准处理。这三处"非常规"选择共同构成了 WinRAR 授权机制的技术特征,也正因如此,只要掌握了私钥派生算法,任何人理论上都可以复现其rarreg.key生成过程——这正是本仓库的核心价值所在。
- 密码学
- 逆向工程
- CLI
【免费下载链接】winrar-keygen
Principle of WinRAR key generation.
相关推荐
GmSSL高级特性深度解析:SM2盲签名、环签名与可恢复签名的终极指南
GmSSL作为北京大学开发的国产商用密码开源库,在支持国密SM2/SM3/SM4/SM9/SSL的基础上,还提供了一系列高级密码学特性。本文将深入解析SM2盲签
密码学网络安全PyGMTSAR地表形变监测入门指南:8个真实案例看懂InSAR处理全流程
PyGMTSAR地表形变监测入门指南:8个真实案例看懂InSAR处理全流程 地面沉降、地震错动、火山隆起……这些肉眼难察的地表形变,正在世界各地悄悄发生。 Py
HS256签名算法深度解析:从数学原理到PHP-JWT实战
HS256签名算法深度解析:从数学原理到PHP JWT实战 引言:你还在为JWT签名安全发愁? 当你使用JSON Web Token(JWT,JSON网络令牌)
后端安全
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考