TencentPediyKeygenMe — 逆向分析
TencentPediyKeygenMe — 逆向分析

目标:求用户名
KCTF的合法 License。
结论(满足程序魔改后验签的正确答案):用户名 : KCTF License: DQF030H7-7X8P0YMA-WHZNQEGF-CFEVXVTA工具:IDA Pro (MCP) 静态分析 + Python 复现 + C 多线程 Pollard’s rho 解 64 位 ECDLP。
目录
- 整体逻辑与校验入口
- License 格式
- 变换链(签名 ⇄ License)
- 各加解密算法与“魔改”
- ECDSA 验签与曲线参数
- ⭐ ECC 验签魔改点 sub_404AD0(重点)
- 利用:强制 r=1 与 64 位 ECDLP
- 注册机算法(正向/逆向)
- 结果与验证
- 关键常量速查表
1. 整体逻辑与校验入口
成功提示字符串 "Congratulations! \n You will be the keygen machine!"(0x46A3C4)被sub_408270(0x408270)引用 —— 这就是注册校验主函数。
hWnd : 用户名输入框, WM_GETTEXT 上限 0x21(33),长度需 != 0
dword_46C6E8: License 输入框, WM_GETTEXT 上限 0x24(36),长度必须 == 35
成功 : MessageBoxA(... , Text="Congratulations!...", Caption="Success!")
主函数流水线(成功路径需层层 al!=0):
sub_4490B0(&v30, 32, &v15/*var84*/, cfg); // base32 解码 32 字符 -> 20 字节
sub_408060(); // SM4 解密
for(i<16) var94[i] = sub_4486D0(var94[i]); // GF(2^8) 逆元 S盒 (16 字节)
for(j<4) var84[16+j] = sub_4486D0(...); // GF(2^8) 逆元 S盒 (尾 4 字节)
sub_407FD0(&v9/*var94*/); // Blowfish 类CBC 加密 -> v8(16字节)
if ( (v8d0^v8d1^v8d2^v8d3) == var84[16:20] ) // 校验和
sub_403210(username, len, &v8); // ECDSA 验签
2. License 格式
- 35 字符;横杠位置
byte_46A3F8 = {0x08, 0x11, 0x1A}= 8 / 17 / 26,即XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX(8-8-8-8)。 - 其余 32 个字符是 base32,字母表
ABCDEFGHJKMNPQRSTVWXYZ1234567890(去掉 I/L/O/U,Crockford 风格),MSB-first。 - 32 个 base32 符号 × 5 bit = 160 bit = 20 字节(解码缓冲
var84)。
3. 变换链(签名 ⇄ License)
栈缓冲区映射(sub_408270 帧内偏移):
| 缓冲 | 偏移 | 含义 |
|---|---|---|
var84 |
ebp-0x84 |
base32 解码 20 字节([0:16]=SM4区,[16:20]=校验/IV 区) |
var94 |
ebp-0x94 |
SM4 输出 / S盒中间 16 字节 |
var_A4 |
ebp-0xA4 |
v8 = ECDSA 签名 16 字节 = r(8B大端) ‖ s(8B大端) |
正向(校验)数据流
base32 → var84(20B)
var94[0:16] = SM4_dec(var84[0:16]) (key="Security@Tencent")
var94[i] = GFinv(var94[i]) for i in 0..15
var84[16+j] = GFinv(var84[16+j]) for j in 0..3
v8 = Blowfish_CBClike_enc(var94, IV=var84[16:20]) (key="DEADBEEF")
assert XOR(v8 的4个dword) == var84[16:20]
ECDSA_verify("KCTF", v8)
逆向(注册机):从合法签名 v8 出发
cks(4B) = XOR(v8 的4个dword) # == var84[16:20] 的“S盒后”值(也是 IV)
var84[16:20] = GFinv(cks) # base32 原始尾4字节
var94_sb = Blowfish_CBClike_dec(v8, IV=cks)
var94_raw = GFinv(var94_sb)
var84[0:16] = SM4_enc(var94_raw)
20B = var84[0:16] ‖ var84[16:20]
base32 编码 → 32 字符,按 8/17/26 插 '-'
Blowfish 的类 CBC 模式(
sub_448A90):out_i = BF(in_i) XOR prev_in,prev_in(-1)=IV。
上下文(sub_407FD0内)显式置expanded=0 / dir=0,sub_448B50(0,...)不交换 P 数组 ⇒ 正向是 Blowfish 加密,逆向用解密。
4. 各加解密算法与“魔改”
| 原语 | 函数 | 结论 / 魔改 |
|---|---|---|
| SM4 | sub_448EE0(密钥扩展) / sub_448F90(分组) |
系统参数 FK=A3B1BAC6/56AA3350/677D9197/B27022DC、标准 S盒(byte_456ED0)、标准 L/L′。魔改:密钥扩展去掉 CK 轮常量。分组用轮密钥逆序 ⇒ 即“解密”。Key="Security@Tencent"。 |
| Blowfish | sub_448710 + 表 unk_455DF0(P[18]) / unk_455E38(S[4][256]) |
算法/密钥扩展结构标准,魔改:P/S 初始表被替换(P[0]=0xF35AF301,非 π 常数 0x243F6A88)。Key="DEADBEEF",IV 取自校验区 4 字节。 |
| 字节 S盒 | sub_4486D0 → sub_4485D0(GF 乘) |
x^254 mod 0x11B = GF(2⁸) 乘法逆元(无仿射)。自逆:GFinv(GFinv(x))==x,逆向时再作用一次即可。 |
| SHA1 | sub_405C60 |
标准 IV 67452301/EFCDAB89/98BADCFE/10325476/C3D2E1F0,未改。 |
| base32 | sub_4490B0 |
标准 MSB-first,自定义字母表。 |
5. ECDSA 验签与曲线参数
sub_403210 构造 CryptoPP DL_VerifierImpl< ... DL_Keys_ECDSA<ECP>, DL_Algorithm_ECDSA<ECP>, DL_SignatureMessageEncodingMethod_DSA, SHA1 >,即 ECDSA / ECP / SHA1。
曲线(64 位素数阶自定义曲线):
p = 0xC564EEF070E69193
a = -3 (= p-3)
b = 0x22996B9C33AEEFDB
G = (0x2223A1D595845FA2, 0x1C4EEE9222DDDA62)
n = 0xC564EEF19A080B07 (素数, 阶)
Q = (0x297A4A1E5B1FC99B, 0x880198E3724F9FEE) 公钥
校验:G,Q 均在曲线上,n·G=O、n·Q=O。
嵌入的私钥:0x46A54C 处 8 字节 54 65 6E 63 65 6E 74 20 = ASCII "Tencent ",
即 d = 0x54656E63656E7420,并验证 d·G == Q ✓(紧邻其后即公钥 Qx@0x46A554、Qy@0x46A55C)。
消息表示:阶为 64 bit ⇒ e = int(SHA1(username)[0:8], 'big')(sub_4051C0 中 (bitlen+7)>>3 = 8)。
签名编码(sub_4050E0 InputSignature):v8[0:8]=r(大端)、v8[8:16]=s(大端)。
6. ⭐ ECC 验签魔改点 sub_404AD0(重点)
6.1 定位
算法对象由 sub_406F90 返回,其 vtable = off_45789C。验签 sub_4051C0 调用(*(algo->vtable[1]))(...),而 *(off_45789C+4) = 0x4578A0 → sub_404AD0。
故 sub_404AD0 就是 DL_Algorithm_ECDSA<ECP>::VerifyDigest(params, pubkey, e, r, s) —— ECDSA 验签核心,参数 a4 = r、a5 = s。
6.2 魔改内容
sub_419EF0 是大整数幅值比较(返回 −1/0/+1)。VerifyDigest 开头对 r,s 做范围校验1 ≤ r,s < n。其中对 r 下界 的判断被篡改:
原版 CryptoPP : if ( Compare(r, 1) >= 0 ) ... // r >= 1 视为合法
魔改后(本样本): if ( Compare(r, 1) == 0 ) ... // 仅 r == 1 视为合法
汇编证据(0x404B76–0x404B96):
404b76 mov edx, esi ; esi = &Integer(1)
404b78 mov eax, ebx ; ebx = r
404b7a call sub_419EF0 ; Compare(r, 1)
404b94 test eax, eax
404b96 jnz loc_404BD3 ; 结果 != 0 → 置失败标志(拒绝)
; 即:只有 Compare(r,1)==0(r==1)才继续
(同函数 0x404B4A 处 jge 是 r < n 上界检查,未改;a5=s 的 1≤s<n 由sub_401E40 / sub_401E90 完成,亦未改。)
6.3 影响
验签等式 (u1·G + u2·Q).x mod n == r 本身不变,但额外强制 r == 1:
- 普通 ECDSA 签名 r 是随机值 ⇒ 能过原版验签,但被魔改版直接拒绝。
(这正是初版 license4Q6PXSE2-...(r≠1)在真机失败的原因。) - 想通过,必须构造
r == 1的合法签名。
7. 利用:强制 r=1 与 64 位 ECDLP
取 r=1,验签计算 u1=e·s⁻¹, u2=r·s⁻¹=s⁻¹,要求
P = u1·G + u2·Q = s⁻¹·(e·G + Q) = s⁻¹·(e+d)·G 的 x 坐标 ≡ 1 (mod n)
因坐标 x < p < n,x ≡ 1 (mod n) 只能是 x = 1。该 x=1 的曲线点存在:
y² = 1 + a·1 + b = b - 2 (mod p) → 是 QR
R = (1, 0x381C2B17A752591E)
令 W = e·G + Q = (e+d)·G(标量已知)。需要 s⁻¹·W = R,即需 ρ0 = dlog_G(R)。
这是一次 64 位 ECDLP(n 为素数,Pohlig–Hellman 无效)。用 16 线程 Pollard’s rho + 蒙哥马利批量求逆 + 区分点,约 70 秒求得:
ρ0 = dlog_G(R) = 0x6CBF49B72611C49A (已验证 ρ0·G = (1, y0))
取 nonce k = ρ0(则 r = (k·G).x mod n = 1),
e = int(SHA1("KCTF")[0:8],'big') = 0x0C5E14D370BD9A84
s = k⁻¹·(e + r·d) mod n = k⁻¹·(e + d) mod n = 0x120BAA4576344E44
v8 = r(8B) ‖ s(8B) = 0000000000000001 120BAA4576344E44
正确性:P = s⁻¹·(e+d)·G = k·G = R,故 P.x mod n = 1 = r ✓,且 r==1 满足魔改约束。
8. 注册机算法(正向/逆向)
e = SHA1("KCTF")[0:8](大端整数)。- 解 ECDLP 得
ρ0 = dlog_G((1,y0));k=ρ0,r=1,s=k⁻¹(e+d) mod n。 v8 = r.to_bytes(8,'big') + s.to_bytes(8,'big')。cks = XOR(v8 的4个小端dword);var84[16:20] = GFinv(cks_bytes);IV = cks。- Blowfish 逆:
var94_sb = BF_dec(v8[0:8]^IV) ‖ BF_dec(v8[8:16]^v8[0:8])。 var94_raw = GFinv(var94_sb);var84[0:16] = SM4_enc(var94_raw)。- 20 字节 base32 编码(MSB-first)→ 32 字符,按 8/17/26 插
-。
注:以上 SM4 / Blowfish / GF 表均直接从二进制 dump(byte_456ED0 / unk_455DF0 / unk_455E38),与样本一致。
9. 结果与验证
用从二进制 dump 的真实表,在 Python 中完整复现正向校验链并施加魔改后规则:
license = DQF030H7-7X8P0YMA-WHZNQEGF-CFEVXVTA
var84(20) = ... (base32 解码)
v8(还原) = 0000000000000001120BAA4576344E44 ← 与构造一致
r == 1 : True (满足魔改约束)
s in [1,n) : True
checksum : True
ECDSA P.x%n == r : True
==> MODIFIED_VERIFY_PASS = True
最终答案:用户名
KCTF→ LicenseDQF030H7-7X8P0YMA-WHZNQEGF-CFEVXVTA作废版本:
4Q6PXSE2-D3JPFV8A-S1XGZPAA-VDMPR3QF(r≠1,仅过原版 ECDSA,被魔改版拒绝)。
10. 关键常量速查表
| 项 | 值 / 地址 |
|---|---|
| 校验主函数 | sub_408270 |
| ECDSA 验签构造 | sub_403210 |
| VerifyDigest(魔改点) | sub_404AD0(algo vtable off_45789C[1]=0x4578A0),jnz @0x404B96 强制 r==1 |
| 私钥 d | 0x46A54C = "Tencent " = 0x54656E63656E7420 |
| 公钥 Q | Qx@0x46A554, Qy@0x46A55C |
| 曲线 p,n,b | 0xC564EEF070E69193, 0xC564EEF19A080B07, 0x22996B9C33AEEFDB |
| G | (0x2223A1D595845FA2, 0x1C4EEE9222DDDA62) |
| x=1 点 R | (1, 0x381C2B17A752591E) |
| ρ0 = dlogG® | 0x6CBF49B72611C49A |
| SM4 key / S盒 | "Security@Tencent" / byte_456ED0(标准,密钥扩展去 CK) |
| Blowfish key / 表 | "DEADBEEF" / unk_455DF0,unk_455E38(魔改表) |
| 字节 S盒 | sub_4486D0 = GF(2⁸) 逆元(0x11B),自逆 |
| base32 字母表 | ABCDEFGHJKMNPQRSTVWXYZ1234567890,横杠位 8/17/26 |
| 签名 v8 | r=0x1,s=0x120BAA4576344E44 → 0000000000000001120BAA4576344E44 |
更多推荐



所有评论(0)