-
-
[分享]010算法分析,附带注册机,网络验证分析
-
发表于: 6天前 1319
-
010分析
本文对010 16.0.2版本进行分析,参考solly大佬的文章,经过验证,其注册算法与之前版本并未改变,新人第一次写贴,如有违规侵权,请联系会尽快处理。
1.查看文件基本信息
用die对010分析查看基本信息

使用 Qt 6.4.3 图形界面库、由 Microsoft Visual Studio 2019(完整IDE)开发的程序,没有壳。
2.010暴力破解
通过报错信息定位判断点

Ida中位置如下

使用x64dbg进行调试
在程序入口使用shift+D字符串搜索-invalid name


双击进入

查看交叉引用,跳转到引用点

到此处,可以看见上面有三个跳转,再jmp下面一行中继续查看应用跟踪

到达此处,这里跟上面情况一样,仍然是只做了判断,下断点之后继续往上跟

此处0xdb是成功关键位,流程图如下

红线是正确的走向,通过流程图可以对edi复制进行跟踪,
继续往上跟踪,发现有两个引用

跟到这里我们会发现两个跳转,首先看887d方法

其中反汇编如下,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | __int64 __fastcall sub_7FF6484DC3B0(__int64 a1, __int64 a2, __int64 a3){ unsigned int v3; // edi int v6; // eax int v7; // eax int v8; // eax unsigned int v9; // ecx int v10; // eax unsigned int v11; // ecx int v12; // eax unsigned int v13; // ecx v3 = a2; if ( *(a1 + 124) ) return 0x113LL; v6 = sub_7FF6480A3D5F(a1, a2, a3); switch ( v6 ) { case 0x2D: return 0xDBLL; case 0x4E: v12 = sub_7FF6480AC879(a1, v3); v13 = 0x20C; if ( v12 != 23 ) return 0xED; return v13; case 0xE7: return 0x177LL; default: v7 = sub_7FF6480AC879(a1, v3); if ( v7 == 23 ) return 0x71LL; if ( v7 != 42 ) { if ( v7 == 0x138 ) { v8 = sub_7FF6480AEC00(a1); v9 = 0x2F; if ( v8 == 0x1A3 ) return 0xF9; return v9; } return 0x177LL; } v10 = sub_7FF6480AEC00(a1); v11 = 0x177; if ( v10 == 419 ) return 0xF9; return v11; }} |
可以看出对db有检测且v6调用3d5f方法,也就是上一个call,其具体为第一个call返回0x2d此处返回0xdb,验证成功。那么暴力破解的方法就很多了
此处更改这边的if跳转,与返回值
if ( *(a1 + 124) )
return 0x113LL;
具体如下
这种更改判断以及返回实现效果实现为不需要网络破解,(因为在010初始页面有剩余天数下,也就是在者许可证验证之前就有这个逻辑的实现,同样也是调用这个方法)

对应x64进行更改打补丁

成功破解

3.算法分析
接下来我们开始跟踪其注册算法,先在这个位置下一个断点,如下图所示:

按 F9 运行该软件,在注册界面,输入注册码假码信息,如下图所示:

在上图中也可以看到目前的注册状态:Evaluation Version - 30 Days Left。运行,马上断了下来,如下图所示


在ida中代码如下

到这里

下面对方法进行讲解
第一个call中代码如下 主要是去除’ “
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | v2 = *(int *)(a2 + 0x10); v3 = a1 + 24; QString::operator=(a1 + 24, a2); if ( (int)v2 > 2 ) { v5 = *(_WORD *)QChar::QChar((QChar *)&v12, '\''); if ( *(_WORD *)QString::operator[](a2, &v13, 0LL) == v5 ) { v6 = v2; v7 = *(_WORD *)QChar::QChar((QChar *)&v12, '\''); if ( *(_WORD *)QString::operator[](a2, &v13, v2 - 1) == v7 ) {LABEL_8: v10 = QString::mid(a2, v11, 1LL, (int)v2 - 2); QString::operator=(v3, v10); QString::~QString(v11); return; } } else { v6 = v2; } v8 = *(_WORD *)QChar::QChar((QChar *)&v12, '"'); if ( *(_WORD *)QString::operator[](a2, &v13, 0LL) == v8 ) { v9 = *(_WORD *)QChar::QChar((QChar *)&v12, '"'); if ( *(_WORD *)QString::operator[](a2, &v13, v6 - 1) == v9 ) goto LABEL_8; } }} |
第二个call如下,对0与l做替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | v4 = 0LL; QString::QString(a2, a3); v17 = 1; v5 = *((_DWORD *)a2 + 4); if ( v5 > 0 ) { v6 = (unsigned int)v5; do { v7 = *(_WORD *)QChar::QChar((QChar *)v14, 'O'); if ( *(_WORD *)QString::operator[](a2, v4) == v7 || (v8 = *(_WORD *)QChar::QChar((QChar *)v15, 'o'), *(_WORD *)QString::operator[](a2, v4) == v8) ) { QChar::QChar((QChar *)&v18, 48); v9 = v18; *(_WORD *)QString::operator[](a2, v4) = v9; } v10 = *(_WORD *)QChar::QChar((QChar *)v16, 'l'); if ( *(_WORD *)QString::operator[](a2, v4) == v10 ) { QChar::QChar((QChar *)&v13, '1'); v11 = v13; *(_WORD *)QString::operator[](a2, v4) = v11; } ++v4; --v6; } while ( v6 ); } return a2;} |
第三个核心算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | __int64 __fastcall hexing_7FF774DCB840(__int64 a1, unsigned int a2, unsigned int a3){ int v4; // ebp _QWORD *v5; // rbx char *v6; // rdi __int16 v7; // bx __int64 v8; // rax __int16 v9; // bx __int64 v10; // rax int v11; // r8d __int64 v12; // rax _BYTE *v13; // rdx char v14; // dl int v15; // r12d unsigned __int8 v16; // r9 unsigned __int8 v17; // al int v18; // ebx char v19; // di __int16 v20; // bx unsigned __int16 v21; // ax unsigned int v22; // ecx char v23; // r14 unsigned __int16 v24; // ax int v25; // ebx char *v26; // rax int v27; // eax unsigned int v28; // ebx int index_7FF6DED5B0C3; // eax unsigned int v31; // [rsp+20h] [rbp-78h] char v32[2]; // [rsp+24h] [rbp-74h] BYREF char v33[2]; // [rsp+26h] [rbp-72h] BYREF char v34[2]; // [rsp+28h] [rbp-70h] BYREF char v35[2]; // [rsp+2Ah] [rbp-6Eh] BYREF unsigned int v36; // [rsp+2Ch] [rbp-6Ch] unsigned int v37; // [rsp+30h] [rbp-68h] _BYTE v38[24]; // [rsp+38h] [rbp-60h] BYREF unsigned __int8 v39; // [rsp+50h] [rbp-48h] BYREF unsigned __int8 v40; // [rsp+51h] [rbp-47h] unsigned __int8 v41; // [rsp+52h] [rbp-46h] char v42; // [rsp+53h] [rbp-45h] unsigned __int8 v43; // [rsp+54h] [rbp-44h] unsigned __int8 v44; // [rsp+55h] [rbp-43h] unsigned __int8 v45; // [rsp+56h] [rbp-42h] char v46; // [rsp+57h] [rbp-41h] unsigned __int8 v47; // [rsp+58h] [rbp-40h] unsigned __int8 v48; // [rsp+59h] [rbp-3Fh] v37 = a3; v36 = a2; v4 = 0; // 初始化局部变量 v31 = 0; // 初始化配对索引 *(a1 + 128) = 0LL; // 清空结果字段1 *(a1 + 104) = 0; // 清空结果字段2 if ( *(a1 + 40) && *(a1 + 64) ) // 检查用户名和注册码是否都存在 { parse_dashed_hex_7FF6DED5E1DD(a1, &v39); // 解析注册码字符串 v5 = &off_7FF6BBF4A5C0; // 指向黑名单数组 do { if ( QString::operator==(a1 + 0x18, *v5) )// 比较用户名是否在黑名单中 return 0xE7LL; // 如果在,返回错误码0xE7 ++v5; } while ( v5 < &unk_7FF6BBF4A5D0 ); // 遍历整个黑名单数组 if ( *(a1 + 40) > 1 ) { v6 = &unk_7FF6BBF4A5D1; do { v7 = *QChar::QChar(v32, *(v6 - 1)); // 遍历所有特征条目 v8 = QString::operator[](a1 + 24, 0LL); // 取用户名第0位(第1个字符) if ( *QChar::toUpper(v8, v33) == v7 ) // 转大写后对比 { v9 = *QChar::QChar(v34, *v6); v10 = QString::operator[](a1 + 24, 1LL);// 取用户名第1位(第2个字符) if ( *QChar::toUpper(v10, v35) == v9 )// 转大写后对比 { v11 = 0; // 步骤3:匹配注册码的10个核心字节 v12 = 0LL; v13 = v6 + 10; // 指向特征条目中“注册码特征字节”的最后1位 do { // &v39:之前parse_dashed_hex解析出的注册码原始字节数组 if ( *(&v39 + v12) != *v13 ) // 逐字节对比注册码和特征值 break; ++v11; ++v12; --v13; // 反向对比(从后往前) } while ( v12 < 10 ); // 一共对比10个字节 if ( v11 == 10 ) // 如果10个字节全匹配,直接判定无效 return 0xE7LL; } } v6 += 12; // 每个特征条目占12字节,跳到下一个条目 } while ( v6 < byte_7FF6BBF4A5E9 ); } v14 = v46; // 字节7 v15 = v44; // 字节5 v16 = v41; // 字节2 v17 = v40; // 字节1 v18 = v39; // 字节0 v19 = v42; // 字节3(类型标识符) // 此处v42就是rsp也就是重新生成的密钥 switch ( v42 ) // 根据类型字节选择验证路径 { case 0x9C: v20 = (v44 ^ v41) + ((v46 ^ v40) << 8); // 普通许可证)这个是没有免费技术支持,没有免费升级的注册类型,注册码中有版本号检查,必须大于当前软件的版本,否则提示注册码过期。注册码只有4段。 // v20是 010 Editor 普通许可证的 16 位 “产品标识符”,由注册码 4 个固定字节异或计算得出; // 解释:步骤1: 计算许可证ID // v20 = ((v46 ^ v40) << 8) | (v44 ^ v41) // 即字节1^字节7作为高8位,字节5^字节2作为低8位 *(a1 + 108) = xor_add_transform_7FF6DED5AD3F(v45 ^ v39);// 步骤2: 计算有效期 // 有效期 = transform(字节6 ^ 字节0) // transform函数: ((x ^ 0x18) + 61) ^ 0xA7 v21 = validate_and_map_id_7FF6DED58C33(v20); *(a1 + 112) = v21; // 步骤3: 验证并映射ID // ID映射函数: // 1. tmp = ((v20 ^ 0x7892) + 19760) ^ 0x3421 // 2. 如果tmp能被11整除,返回tmp/11,否则返回0 v22 = *(a1 + 108); // 获取计算出的有效期 if ( v22 && v21 - 1 <= 0x3E7 ) // 步骤4: 检查有效期和ID是否有效 // 有效期非0且ID在1-1000范围内 { v23 = 0; if ( v22 < 2 ) v23 = *(a1 + 108); // 如果有效期小于2,v23=有效期,否则v23=0LABEL_26: // 进入公共验证部分 QString::toUtf8(a1 + 24, v38); // 步骤1: 获取用户名的UTF-8编码 v25 = *(a1 + 112); // 获取映射后的ID LOBYTE(v4) = v19 != -4; // 设置标志位: 如果类型不是0xFC则为1,否则为0 v26 = QByteArray::data(v38); // 获取用户名数据指针 v27 = compute_custom_hash_7FF6DED5C97D(v26, v4, v23, v25);// 步骤2: 计算自定义哈希 // 参数: 用户名, 标志位, 有效期/索引, ID if ( v43 == v27 && v15 == BYTE1(v27) && v45 == BYTE2(v27) && v46 == HIBYTE(v27) )// 步骤3: 验证哈希匹配 // 哈希值的四个字节必须分别等于v43, v15, v45, v46 // 即: 字节4=哈希最低字节, 字节5=哈希第二字节, 字节6=哈希第三字节, 字节7=哈希最高字节 // { if ( v19 == '\x9C' ) { if ( v36 > *(a1 + 108) ) // 如果当前时间(v36)大于有效期 { v28 = 0x4E; // 返回错误码0x4E(可能表示过期)LABEL_41: // 清理并返回 QByteArray::~QByteArray(v38); return v28; }LABEL_33: v28 = 0x2D; // 否则返回成功码0x2D goto LABEL_41; } if ( v19 == -4 ) // 0xFC { index_7FF6DED5B0C3 = validate_pair_and_get_index_7FF6DED5B0C3(v39 + (v40 << 8) + (v41 << 16), v27);// 验证配对: 计算(字节0 + 字节1<<8 + 字节2<<16)和哈希值的关系 if ( index_7FF6DED5B0C3 ) // 如果配对有效 { *(a1 + 104) = index_7FF6DED5B0C3;// 存储配对索引 v28 = 0x93; // 返回成功码0x93 goto LABEL_41; } } else if ( v31 ) { if ( v37 > v31 ) { v28 = 0x4E; goto LABEL_41; } goto LABEL_33; } } v28 = '\xE7'; goto LABEL_41; } break; case 0xFC: v23 = -1; // 这个不算正式注册版本,是延长试用期版本,最长可延长为5年试用期(从发布日期算起),这个版本会有一些检查(注册表中的数据),保证延期合法。注册码只有4段 *(a1 + 108) = 255; *(a1 + 112) = 1; *(a1 + 128) = 1; goto LABEL_26; case 0xAC: *(a1 + 108) = 2; // 这个是有免费技术支持、有免费升级的完整注册类型,不过会检查注册码中的过期时间(天数,从发布时间算起),这些免费支持是有期限的,会过期。注册码有5段,是最完整的注册码。 // a1是注册码验证结构体指针,a1+108是 “类型 / 有效期标记字段”,固定赋值为 2; v24 = validate_and_map_id_7FF6DED58C33((v15 ^ v16) + ((v14 ^ v17) << 8));// 计算该类型的 16 位 “产品核心 ID”(类似 0x9C 分支的 v20,但验证逻辑更严格); *(a1 + 112) = v24; // 映射后的 ID 存入结构体a1+112字段(和 0x9C 分支存储映射 ID 的位置一致); if ( v24 - 1 <= 0x1387 ) // 验证映射后的 ID 是否在 “完整许可证有效范围” 内(比 0x9C 分支的 1~1000 范围大,对应 “更完整的注册码” 属性); { v31 = validate_pair_and_get_index_7FF6DED5B0C3(// validate_pair_and_get_index是 “配对验证 + 期限索引获取” 函数,输入 2 个参数,验证匹配则返回 “过期天数 / 支持期限索引”(非 0),失败则返回 0 (v45 ^ v18) + ((v15 ^ v48) << 16) + ((v47 ^ v43) << 8), 026706047); *(a1 + 132) = v31; v23 = v31; goto LABEL_26; } break; } return 0xE7LL; } return 0x93LL;}/* Orphan comments:普通许可证)详细流程*/ |
对应汇编如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | 000000014043B84 | mov qword ptr ss:[rsp+0x10],rbx |000000014043B84 | mov qword ptr ss:[rsp+0x18],rbp |000000014043B84 | push rsi |000000014043B84 | push rdi |000000014043B84 | push r12 |000000014043B84 | push r14 |000000014043B85 | push r15 |000000014043B85 | sub rsp,0x70 |000000014043B85 | mov rax,qword ptr ds:[0x140FF1FF8] | rax:EntryPoint000000014043B85 | xor rax,rsp | rax:EntryPoint000000014043B86 | mov qword ptr ss:[rsp+0x60],rax | rax:EntryPoint000000014043B86 | mov dword ptr ss:[rsp+0x30],r8d | a3=0x4f4c000000014043B86 | mov dword ptr ss:[rsp+0x2C],edx | a2 11000000014043B86 | mov rsi,rcx |000000014043B87 | xor ebp,ebp | v4=0 初始化局部变量000000014043B87 | mov dword ptr ss:[rsp+0x20],ebp | 初始化配对索引000000014043B87 | mov qword ptr ds:[rcx+0x80],rbp | a1 + 128000000014043B87 | mov dword ptr ds:[rcx+0x68],ebp | a1 + 104000000014043B88 | cmp qword ptr ds:[rcx+0x28],rbp |000000014043B88 | je 010editor.14043BBA8 |000000014043B88 | cmp qword ptr ds:[rcx+0x40],rbp |000000014043B88 | je 010editor.14043BBA8 | 检查用户名和注册码是否都存在000000014043B89 | lea rdx,qword ptr ss:[rsp+0x50] | rdx:EntryPoint000000014043B89 | call 010editor.14000E1DD | decode_formatted_hex_id 序列号转换000000014043B89 | lea rbx,qword ptr ds:[<sub_140E1A5C0>] | 0000000140E1A5C0:&"999" 黑名单用户名000000014043B8A | lea rdi,qword ptr ds:[0x140E1A5D0] |000000014043B8A | nop dword ptr ds:[rax],eax |000000014043B8B | mov rdx,qword ptr ds:[rbx] | rdx:EntryPoint000000014043B8B | lea rcx,qword ptr ds:[rsi+0x18] | 0x00000133F51AA700000000014043B8B | call qword ptr ds:[<public: bool __cdecl QString: | 用户名比较 999与AnyOne000000014043B8B | test al,al | al==0 用户名!=999000000014043B8B | jne 010editor.14043BBA1 | al=1跳到注册码值正确000000014043B8C | add rbx,0x8 |000000014043B8C | cmp rbx,rdi |000000014043B8C | jl 010editor.14043B8B0 |000000014043B8C | cmp qword ptr ds:[rsi+0x28],0x1 | 1000000014043B8D | jle 010editor.14043B98F |000000014043B8D | lea rdi,qword ptr ds:[0x140E1A5D1] | v6 sint 3此处同样也是比较用户名,比较签名两个字符 43 ('c') 4F('0') 57 D6 30 E3 CA B9 AC AB A1 ('j') C4 ('u') 4A 55 59 2A 35 E2 C4 65 AC D3 A4 CB000000014043B8E | lea r14,qword ptr ds:[0x140E1A5E9] | co 与ju sint 5000000014043B8E | nop word ptr ds:[rax+rax],ax |000000014043B8F | movzx edx,byte ptr ds:[rdi-0x1] | 遍历黑名单数组,每个12000000014043B8F | lea rcx,qword ptr ss:[rsp+0x24] | b7 01 v32000000014043B8F | call qword ptr ds:[<public: __cdecl QChar::QChar( | 取出c000000014043B8F | movzx ebx,word ptr ds:[rax] | c000000014043B90 | xor edx,edx |000000014043B90 | lea rcx,qword ptr ds:[rsi+0x18] | unchar ch000000014043B90 | call qword ptr ds:[<public: class QChar & __cdecl |000000014043B90 | lea rdx,qword ptr ss:[rsp+0x26] | rdx:EntryPoint000000014043B91 | mov rcx,rax | rax:EntryPoint000000014043B91 | call qword ptr ds:[<public: class QChar __cdecl Q | 转换成大写用户名第一个字符000000014043B91 | cmp word ptr ds:[rax],bx | rax:EntryPoint000000014043B91 | jne 010editor.14043B982 |000000014043B92 | movzx edx,byte ptr ds:[rdi] |000000014043B92 | lea rcx,qword ptr ss:[rsp+0x28] |000000014043B92 | call qword ptr ds:[<public: __cdecl QChar::QChar( |000000014043B92 | movzx ebx,word ptr ds:[rax] | rax:EntryPoint000000014043B93 | mov edx,0x1 |000000014043B93 | lea rcx,qword ptr ds:[rsi+0x18] |000000014043B93 | call qword ptr ds:[<public: class QChar & __cdecl |000000014043B94 | lea rdx,qword ptr ss:[rsp+0x2A] | rdx:EntryPoint000000014043B94 | mov rcx,rax | rax:EntryPoint000000014043B94 | call qword ptr ds:[<public: class QChar __cdecl Q |000000014043B94 | cmp word ptr ds:[rax],bx | rax:EntryPoint000000014043B95 | jne 010editor.14043B982 |000000014043B95 | mov r8d,ebp |000000014043B95 | mov rax,rbp | rax:EntryPoint000000014043B95 | lea rdx,qword ptr ds:[rdi+0xA] | rdx:EntryPoint000000014043B95 | nop |000000014043B96 | movzx ecx,byte ptr ds:[rdx] | rdx:EntryPoint000000014043B96 | cmp byte ptr ss:[rsp+rax+0x50],cl |000000014043B96 | jne 010editor.14043B978 |000000014043B96 | inc r8d |000000014043B96 | inc rax | rax:EntryPoint000000014043B96 | dec rdx | rdx:EntryPoint000000014043B97 | cmp rax,0xA | rax:EntryPoint, 0A:'\n'000000014043B97 | jl 010editor.14043B960 |000000014043B97 | cmp r8d,0xA | 0A:'\n'000000014043B97 | je 010editor.14043BBA1 |000000014043B98 | add rdi,0xC |000000014043B98 | cmp rdi,r14 |000000014043B98 | jl 010editor.14043B8F0 |000000014043B98 | movzx edx,byte ptr ss:[rsp+0x57] | v46 字节7,密钥第八个000000014043B99 | movzx r12d,byte ptr ss:[rsp+0x55] | v44 字节5 第6个000000014043B99 | movzx r9d,byte ptr ss:[rsp+0x52] | v41 字节2 第3个000000014043B9A | movzx eax,byte ptr ss:[rsp+0x51] | v40 字节1 第2个000000014043B9A | movzx ebx,byte ptr ss:[rsp+0x50] | v39 字节0 第一个000000014043B9A | movzx edi,byte ptr ss:[rsp+0x53] | dil(EDI 低 8 位) 字节3 第四个000000014043B9A | cmp dil,0x9C |000000014043B9B | jne 010editor.14043BA16 | swith 0xfc跳转000000014043B9B | movzx ecx,bl | bl=v44,零扩展为32位存入ECX(ECX=0x000000[V44])000000014043B9B | xor cl,byte ptr ss:[rsp+0x56] | cl是ECX低8位(即v44),和rsp+0x56(v41=rsp+0x52)异或 → cl = v44 ^ v41000000014043B9B | xor al,dl | al=v46,dl=v40 → al = v46 ^ v40(高8位原始值)000000014043B9B | movzx ebx,al | 把al(v46^v40)零扩展为32位存入EBX(EBX=0x000000[V46^V40])000000014043B9C | mov eax,0x100 | EAX=0x100(十进制256,等价于2^8)000000014043B9C | imul ebx,eax | EBX = EBX * EAX = (v46^v40) * 0x100 → 等价于 (v46^v40) << 8000000014043B9C | movzx eax,r9b | r9b存储的是之前计算的v44^v41(cl的值),零扩展存入EAX000000014043B9C | xor al,r12b | 校准:确保al=v44^v41000000014043B9D | movzx edx,al | 把al(v44^v41)零扩展到EDX(EDX=0x000000[V44^V41])000000014043B9D | add bx,dx | bx=EBX低16位(即(v46^v40)<<8),dx=EDX低16位(即v44^v41)→ bx = 高8位 + 低8位 = v20000000014043B9D | call 010editor.14000AD3F | xor_add_transform000000014043B9D | movzx eax,al |000000014043B9D | mov dword ptr ds:[rsi+0x6C],eax | 计算版本号000000014043B9E | movzx ecx,bx |000000014043B9E | call 010editor.140008C33 | validate_and_map_id000000014043B9E | movzx eax,ax |000000014043B9E | mov dword ptr ds:[rsi+0x70],eax | 验证产品ID000000014043B9E | mov ecx,dword ptr ds:[rsi+0x6C] |000000014043B9F | test ecx,ecx |000000014043B9F | je 010editor.14043BBA1 |000000014043B9F | dec eax |000000014043B9F | cmp eax,0x3E7 |000000014043BA0 | ja 010editor.14043BBA1 | 验证条件检查版本号必须非零:v22 != 0产品ID必须在1-1000范围内:v21 >= 1 && v21 <= 1000(0x3E7 = 999)000000014043BA0 | mov r14d,ebp |000000014043BA0 | cmp ecx,0x2 |000000014043BA0 | cmovb r14d,ecx | 设置哈希参数000000014043BA1 | jmp 010editor.14043BABD |000000014043BA1 | cmp dil,0xFC | 比较0x fc000000014043BA1 | jne 010editor.14043BA3C |000000014043BA1 | mov r14d,0xFF |000000014043BA2 | mov dword ptr ds:[rsi+0x6C],r14d |000000014043BA2 | mov dword ptr ds:[rsi+0x70],0x1 |000000014043BA2 | mov dword ptr ds:[rsi+0x80],0x1 |000000014043BA3 | jmp 010editor.14043BABD |000000014043BA3 | cmp dil,0xAC | 比较0xac000000014043BA4 | jne 010editor.14043BBA1 |000000014043BA4 | mov dword ptr ds:[rsi+0x6C],0x2 | 将2写入结构体的偏移108处(类型标记)000000014043BA4 | xor al,dl | 计算 v14 ^ v17(字节7^字节1)000000014043BA4 | movzx ecx,al |000000014043BA5 | mov eax,0x100 | 左移8位000000014043BA5 | imul ecx,eax |000000014043BA5 | movzx eax,r9b |000000014043BA5 | xor al,r12b | 计算 v15 ^ v16(字节5^字节2)000000014043BA6 | movzx edx,al |000000014043BA6 | add cx,dx | 计算16位产品id 两部分组合成16位数000000014043BA6 | call 010editor.140008C33 | 计算该类型的 16 位 “产品核心 ID”000000014043BA6 | movzx eax,ax |000000014043BA6 | mov dword ptr ds:[rsi+0x70],eax | 映射后的 ID 存入结构体a1+112字段000000014043BA7 | dec eax |000000014043BA7 | cmp eax,0x1387 | 验证映射后的 ID 是否在 “完整许可证有效范围” 内(比 0x9C 分支的 1~1000 范围大,对应 “更完整的注册码” 属性)000000014043BA7 | ja 010editor.14043BBA1 |000000014043BA7 | movzx ecx,byte ptr ss:[rsp+0x54] | 加载v43(字节4000000014043BA8 | movzx eax,byte ptr ss:[rsp+0x58] | 加载v47(字节8000000014043BA8 | xor ecx,eax |000000014043BA8 | shl ecx,0x8 | ecx = (v43 ^ v47) << 8000000014043BA8 | movzx edx,byte ptr ss:[rsp+0x59] | 加载v48(字节9)000000014043BA9 | xor edx,r12d | edx = v48 ^ r12d(r12d存放v15/字节5)000000014043BA9 | shl edx,0x10 | edx = (v48 ^ v15) << 16000000014043BA9 | add ecx,edx | ecx = ((v47 ^ v43) << 8) + ((v15 ^ v48) << 16)000000014043BA9 | mov edx,ebx | ebx存放v18(字节0)000000014043BA9 | movzx eax,byte ptr ss:[rsp+0x56] | 加载v45(字节6)000000014043BAA | xor edx,eax | edx = v18 ^ v45000000014043BAA | add ecx,edx |000000014043BAA | mov edx,0x5B8C27 | 第二个固定参数:026706047(八进制)000000014043BAA | call 010editor.14000B0C3 |000000014043BAB | mov dword ptr ss:[rsp+0x20],eax |000000014043BAB | mov dword ptr ds:[rsi+0x84],eax | 存储到结构体的偏移84h处000000014043BAB | mov r14d,eax | 进入26分支000000014043BAB | lea rdx,qword ptr ss:[rsp+0x38] | 目标地址,用于存储QByteArray000000014043BAC | lea rcx,qword ptr ds:[rsi+0x18] | 用户名000000014043BAC | call qword ptr ds:[<public: class QByteArray __cd | 序列号转UTF-8000000014043BAC | nop |000000014043BAC | mov ebx,dword ptr ds:[rsi+0x70] | 获取产品id000000014043BAD | cmp dil,0xFC | 比较v19(类型)与0xFC000000014043BAD | setne bpl | 如果v19!=0xFC,则bpl=1,否则bpl=0000000014043BAD | lea rcx,qword ptr ss:[rsp+0x38] |000000014043BAD | call qword ptr ds:[<public: char * __cdecl QByteA | rax指向用户名UTF-8字节数据000000014043BAE | mov r9d,ebx | 第4个参数:产品ID000000014043BAE | mov r8d,r14d | 第3个参数:v23(对于0xAC类型是配对索引)000000014043BAE | mov edx,ebp | 第2个参数:标志位(bpl)000000014043BAE | mov rcx,rax | 第1个参数:用户名数据指针000000014043BAE | call 010editor.14000C97D | compute_custom_hash000000014043BAF | mov edx,eax | 保存哈希值到edx000000014043BAF | cmp byte ptr ss:[rsp+0x54],al | 比较v43(字节4)与哈希最低字节000000014043BAF | jne 010editor.14043BB8D | 不匹配则跳转到失败处理(返回0xE7)000000014043BAF | mov ecx,eax |000000014043BB0 | shr ecx,0x8 | 右移8位,获取第二字节000000014043BB0 | cmp r12b,cl | 比较v15(字节5)与哈希第二字节000000014043BB0 | jne 010editor.14043BB8D |000000014043BB0 | mov ecx,eax |000000014043BB0 | shr ecx,0x10 | 右移16位,获取第三字节000000014043BB1 | cmp byte ptr ss:[rsp+0x56],cl | 比较v45(字节6)与哈希第三字节000000014043BB1 | jne 010editor.14043BB8D |000000014043BB1 | mov ecx,eax |000000014043BB1 | shr ecx,0x18 | 右移24位,获取第四字节000000014043BB1 | cmp byte ptr ss:[rsp+0x57],cl | 比较v46(字节7)与哈希最高字节000000014043BB2 | jne 010editor.14043BB8D | 计算hash匹配000000014043BB2 | cmp dil,0x9C |000000014043BB2 | jne 010editor.14043BB40 |000000014043BB2 | mov eax,dword ptr ss:[rsp+0x2C] |000000014043BB2 | cmp eax,dword ptr ds:[rsi+0x6C] |000000014043BB3 | jbe 010editor.14043BB39 |000000014043BB3 | mov ebx,0x4E | 4E:'N'000000014043BB3 | jmp 010editor.14043BB92 |000000014043BB3 | mov ebx,0x2D | 2D:'-'000000014043BB3 | jmp 010editor.14043BB92 |000000014043BB4 | cmp dil,0xFC |000000014043BB4 | jne 010editor.14043BB72 |000000014043BB4 | movzx ecx,byte ptr ss:[rsp+0x52] |000000014043BB4 | shl ecx,0x10 |000000014043BB4 | movzx eax,byte ptr ss:[rsp+0x51] |000000014043BB5 | shl eax,0x8 |000000014043BB5 | add ecx,eax |000000014043BB5 | movzx eax,byte ptr ss:[rsp+0x50] |000000014043BB5 | add ecx,eax |000000014043BB5 | call 010editor.14000B0C3 |000000014043BB6 | test eax,eax |000000014043BB6 | je 010editor.14043BB8D |000000014043BB6 | mov dword ptr ds:[rsi+0x68],eax |000000014043BB6 | mov ebx,0x93 |000000014043BB7 | jmp 010editor.14043BB92 |000000014043BB7 | cmp dil,0xAC |000000014043BB7 | jne 010editor.14043BB8D |000000014043BB7 | mov eax,dword ptr ss:[rsp+0x20] |000000014043BB7 | test eax,eax |000000014043BB7 | je 010editor.14043BB8D |000000014043BB8 | cmp dword ptr ss:[rsp+0x30],eax |000000014043BB8 | jbe 010editor.14043BB39 |000000014043BB8 | mov ebx,0x4E | 4E:'N'000000014043BB8 | jmp 010editor.14043BB92 |000000014043BB8 | mov ebx,0xE7 |000000014043BB9 | lea rcx,qword ptr ss:[rsp+0x38] |000000014043BB9 | call qword ptr ds:[<public: __cdecl QByteArray::~ |000000014043BB9 | mov eax,ebx |000000014043BB9 | jmp 010editor.14043BBAD |000000014043BBA | mov eax,0xE7 | 赋值0xe7000000014043BBA | jmp 010editor.14043BBAD |000000014043BBA | mov eax,0x93 |000000014043BBA | mov rcx,qword ptr ss:[rsp+0x60] |000000014043BBB | xor rcx,rsp |000000014043BBB | call 010editor.1400074A5 | security_check_cookie000000014043BBB | lea r11,qword ptr ss:[rsp+0x70] |000000014043BBB | mov rbx,qword ptr ds:[r11+0x38] |000000014043BBC | mov rbp,qword ptr ds:[r11+0x40] |000000014043BBC | mov rsp,r11 |000000014043BBC | pop r15 |000000014043BBC | pop r14 |000000014043BBC | pop r12 |000000014043BBD | pop rdi |000000014043BBD | pop rsi |000000014043BBD | ret | |
其中在compute_custom_hash_7FF6DED5C97D中对应映射表如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 0x39CB44B8, 0x23754F67, 0x5F017211, 0x3EBB24DA, 0x351707C6, 0x63F9774B, 0x17827288, 0x0FE74821, 0x5B5F670F, 0x48315AE8, 0x785B7769, 0x2B7A1547, 0x38D11292, 0x42A11B32, 0x35332244, 0x77437B60, 0x1EAB3B10, 0x53810000, 0x1D0212AE, 0x6F0377A8, 0x43C03092, 0x2D3C0A8E, 0x62950CBF, 0x30F06FFA, 0x34F710E0, 0x28F417FB, 0x350D2F95, 0x5A361D5A, 0x15CC060B, 0x0AFD13CC, 0x28603BCF, 0x3371066B, 0x30CD14E4, 0x175D3A67, 0x6DD66A13, 0x2D3409F9, 0x581E7B82, 0x76526B99, 0x5C8D5188, 0x2C857971, 0x15F51FC0, 0x68CC0D11, 0x49F55E5C, 0x275E4364, 0x2D1E0DBC, 0x4CEE7CE3, 0x32555840, 0x112E2E08, 0x6978065A, 0x72921406, 0x314578E7, 0x175621B7, 0x40771DBF, 0x3FC238D6, 0x4A31128A, 0x2DAD036E, 0x41A069D6, 0x25400192, 0x00DD4667, 0x6AFC1F4F, 0x571040CE, 0x62FE66DF, 0x41DB4B3E, 0x3582231F, 0x55F6079A, 0x1CA70644, 0x1B1643D2, 0x3F7228C9, 0x5F141070, 0x3E1474AB, 0x444B256E, 0x537050D9, 0x0F42094B, 0x2FD820E6, 0x778B2E5E, 0x71176D02, 0x7FEA7A69, 0x5BB54628, 0x19BA6C71, 0x39763A99, 0x178D54CD, 0x01246E88, 0x3313537E, 0x2B8E2D17, 0x2A3D10BE, 0x59D10582, 0x37A163DB, 0x30D6489A, 0x6A215C46, 0x0E1C7A76, 0x1FC760E7, 0x79B80C65, 0x27F459B4, 0x799A7326, 0x50BA1782, 0x2A116D5C, 0x63866E1B, 0x3F920E3C, 0x55023490, 0x55B56089, 0x2C391FD1, 0x2F8035C2, 0x64FD2B7A, 0x4CE8759A, 0x518504F0, 0x799501A8, 0x3F5B2CAD, 0x38E60160, 0x637641D8, 0x33352A42, 0x51A22C19, 0x085C5851, 0x032917AB, 0x2B770AC7, 0x30AC77B3, 0x2BEC1907, 0x035202D0, 0x0FA933D3, 0x61255DF3, 0x22AD06BF, 0x58B86971, 0x5FCA0DE5, 0x700D6456, 0x56A973DB, 0x5AB759FD, 0x330E0BE2, 0x5B3C0DDD, 0x495D3C60, 0x53BD59A6, 0x4C5E6D91, 0x49D9318D, 0x103D5079, 0x61CE42E3, 0x7ED5121D, 0x14E160ED, 0x212D4EF2, 0x270133F0, 0x62435A96, 0x1FA75E8B, 0x6F092FBE, 0x4A000D49, 0x57AE1C70, 0x004E2477, 0x561E7E72, 0x468C0033, 0x5DCC2402, 0x78507AC6, 0x58AF24C7, 0x0DF62D34, 0x358A4708, 0x3CFB1E11, 0x2B71451C, 0x77A75295, 0x56890721, 0x0FEF75F3, 0x120F24F1, 0x01990AE7, 0x339C4452, 0x27A15B8E, 0x0BA7276D, 0x60DC1B7B, 0x4F4B7F82, 0x67DB7007, 0x4F4A57D9, 0x621252E8, 0x20532CFC, 0x6A390306, 0x18800423, 0x19F3778A, 0x462316F0, 0x56AE0937, 0x43C2675C, 0x65CA45FD, 0x0D604FF2, 0x0BFD22CB, 0x3AFE643B, 0x3BF67FA6, 0x44623579, 0x184031F8, 0x32174F97, 0x4C6A092A, 0x5FB50261, 0x01650174, 0x33634AF1, 0x712D18F4, 0x6E997169, 0x5DAB7AFE, 0x7C2B2EE8, 0x6EDB75B4, 0x5F836FB6, 0x3C2A6DD6, 0x292D05C2, 0x052244DB, 0x149A5F4F, 0x5D486540, 0x331D15EA, 0x4F456920, 0x483A699F, 0x3B450F05, 0x3B207C6C, 0x749D70FE, 0x417461F6, 0x62B031F1, 0x2750577B, 0x29131533, 0x588C3808, 0x1AEF3456, 0x0F3C00EC, 0x7DA74742, 0x4B797A6C, 0x5EBB3287, 0x786558B8, 0x00ED4FF2, 0x6269691E, 0x24A2255F, 0x62C11F7E, 0x2F8A7DCD, 0x643B17FE, 0x778318B8, 0x253B60FE, 0x34BB63A3, 0x5B03214F, 0x5F1571F4, 0x1A316E9F, 0x7ACF2704, 0x28896838, 0x18614677, 0x1BF569EB, 0x0BA85EC9, 0x6ACA6B46, 0x1E43422A, 0x514D5F0E, 0x413E018C, 0x307626E9, 0x01ED1DFA, 0x49F46F5A, 0x461B642B, 0x7D7007F2, 0x13652657, 0x6B160BC5, 0x65E04849, 0x1F526E1C, 0x5A0251B6, 0x2BD73F69, 0x2DBF7ACD, 0x51E63E80, 0x5CF2670F, 0x21CD0A03, 0x5CFF0261, 0x33AE061E, 0x3BB6345F, 0x5D814A75, 0x257B5DF4, 0x0A5C2C5B, 0x16A45527, 0x16F23945 |
3.注册机
注册机最开始试着写了几个,没有成功,后面直接在solly大佬的基础上生成的,小编代码水平有限。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | import structfrom typing import List# 类型定义UCHAR = intUWORD = intULONG = intUINT32 = intUINT64 = int# 常量定义RLSDATE = 0x473C # 2019/12/06,发布日期VERID = 0x0B # 当前版本IDFREE_UPGRADE_DAYS = 10549 # 免费升级天数MAX_VERSION_ID = VERID + (FREE_UPGRADE_DAYS // 365)name_tables = [ 0x39CB44B8, 0x23754F67, 0x5F017211, 0x3EBB24DA, 0x351707C6, 0x63F9774B, 0x17827288, 0x0FE74821, 0x5B5F670F, 0x48315AE8, 0x785B7769, 0x2B7A1547, 0x38D11292, 0x42A11B32, 0x35332244, 0x77437B60, 0x1EAB3B10, 0x53810000, 0x1D0212AE, 0x6F0377A8, 0x43C03092, 0x2D3C0A8E, 0x62950CBF, 0x30F06FFA, 0x34F710E0, 0x28F417FB, 0x350D2F95, 0x5A361D5A, 0x15CC060B, 0x0AFD13CC, 0x28603BCF, 0x3371066B, 0x30CD14E4, 0x175D3A67, 0x6DD66A13, 0x2D3409F9, 0x581E7B82, 0x76526B99, 0x5C8D5188, 0x2C857971, 0x15F51FC0, 0x68CC0D11, 0x49F55E5C, 0x275E4364, 0x2D1E0DBC, 0x4CEE7CE3, 0x32555840, 0x112E2E08, 0x6978065A, 0x72921406, 0x314578E7, 0x175621B7, 0x40771DBF, 0x3FC238D6, 0x4A31128A, 0x2DAD036E, 0x41A069D6, 0x25400192, 0x00DD4667, 0x6AFC1F4F, 0x571040CE, 0x62FE66DF, 0x41DB4B3E, 0x3582231F, 0x55F6079A, 0x1CA70644, 0x1B1643D2, 0x3F7228C9, 0x5F141070, 0x3E1474AB, 0x444B256E, 0x537050D9, 0x0F42094B, 0x2FD820E6, 0x778B2E5E, 0x71176D02, 0x7FEA7A69, 0x5BB54628, 0x19BA6C71, 0x39763A99, 0x178D54CD, 0x01246E88, 0x3313537E, 0x2B8E2D17, 0x2A3D10BE, 0x59D10582, 0x37A163DB, 0x30D6489A, 0x6A215C46, 0x0E1C7A76, 0x1FC760E7, 0x79B80C65, 0x27F459B4, 0x799A7326, 0x50BA1782, 0x2A116D5C, 0x63866E1B, 0x3F920E3C, 0x55023490, 0x55B56089, 0x2C391FD1, 0x2F8035C2, 0x64FD2B7A, 0x4CE8759A, 0x518504F0, 0x799501A8, 0x3F5B2CAD, 0x38E60160, 0x637641D8, 0x33352A42, 0x51A22C19, 0x085C5851, 0x032917AB, 0x2B770AC7, 0x30AC77B3, 0x2BEC1907, 0x035202D0, 0x0FA933D3, 0x61255DF3, 0x22AD06BF, 0x58B86971, 0x5FCA0DE5, 0x700D6456, 0x56A973DB, 0x5AB759FD, 0x330E0BE2, 0x5B3C0DDD, 0x495D3C60, 0x53BD59A6, 0x4C5E6D91, 0x49D9318D, 0x103D5079, 0x61CE42E3, 0x7ED5121D, 0x14E160ED, 0x212D4EF2, 0x270133F0, 0x62435A96, 0x1FA75E8B, 0x6F092FBE, 0x4A000D49, 0x57AE1C70, 0x004E2477, 0x561E7E72, 0x468C0033, 0x5DCC2402, 0x78507AC6, 0x58AF24C7, 0x0DF62D34, 0x358A4708, 0x3CFB1E11, 0x2B71451C, 0x77A75295, 0x56890721, 0x0FEF75F3, 0x120F24F1, 0x01990AE7, 0x339C4452, 0x27A15B8E, 0x0BA7276D, 0x60DC1B7B, 0x4F4B7F82, 0x67DB7007, 0x4F4A57D9, 0x621252E8, 0x20532CFC, 0x6A390306, 0x18800423, 0x19F3778A, 0x462316F0, 0x56AE0937, 0x43C2675C, 0x65CA45FD, 0x0D604FF2, 0x0BFD22CB, 0x3AFE643B, 0x3BF67FA6, 0x44623579, 0x184031F8, 0x32174F97, 0x4C6A092A, 0x5FB50261, 0x01650174, 0x33634AF1, 0x712D18F4, 0x6E997169, 0x5DAB7AFE, 0x7C2B2EE8, 0x6EDB75B4, 0x5F836FB6, 0x3C2A6DD6, 0x292D05C2, 0x052244DB, 0x149A5F4F, 0x5D486540, 0x331D15EA, 0x4F456920, 0x483A699F, 0x3B450F05, 0x3B207C6C, 0x749D70FE, 0x417461F6, 0x62B031F1, 0x2750577B, 0x29131533, 0x588C3808, 0x1AEF3456, 0x0F3C00EC, 0x7DA74742, 0x4B797A6C, 0x5EBB3287, 0x786558B8, 0x00ED4FF2, 0x6269691E, 0x24A2255F, 0x62C11F7E, 0x2F8A7DCD, 0x643B17FE, 0x778318B8, 0x253B60FE, 0x34BB63A3, 0x5B03214F, 0x5F1571F4, 0x1A316E9F, 0x7ACF2704, 0x28896838, 0x18614677, 0x1BF569EB, 0x0BA85EC9, 0x6ACA6B46, 0x1E43422A, 0x514D5F0E, 0x413E018C, 0x307626E9, 0x01ED1DFA, 0x49F46F5A, 0x461B642B, 0x7D7007F2, 0x13652657, 0x6B160BC5, 0x65E04849, 0x1F526E1C, 0x5A0251B6, 0x2BD73F69, 0x2DBF7ACD, 0x51E63E80, 0x5CF2670F, 0x21CD0A03, 0x5CFF0261, 0x33AE061E, 0x3BB6345F, 0x5D814A75, 0x257B5DF4, 0x0A5C2C5B, 0x16A45527, 0x16F23945]def get_uncheck2(ul: ULONG) -> UWORD: """计算用户数的校验""" a = ul * 11 return ((a ^ 0x3421) - 0x4D30) ^ 0x7892def get_uncheck3(base: ULONG, rlsdate: ULONG) -> ULONG: """计算免费升级天数的校验(AC类型)""" end_of_date = rlsdate + FREE_UPGRADE_DAYS return (((end_of_date * 0x11) ^ 0xFFE53167) + 0x0002C175) ^ 0x0022C078 ^ basedef get_name_check(name: str, is_registed: ULONG, free_days: ULONG, user_count: ULONG) -> ULONG: """计算用户名的校验值""" name_check = 0 idx3 = 0 idx2 = 0 n = len(name) idx4 = user_count * 15 idx1 = free_days * 17 for i in range(n): ch = name[i] idx0 = ord(ch.upper()) & 0xFF name_check += name_tables[idx0] if is_registed != 0: # 正式注册版 name_check ^= name_tables[(idx0 + 0x0D) & 0xFF] name_check *= name_tables[(idx0 + 0x2F) & 0xFF] name_check += name_tables[idx1 & 0xFF] name_check += name_tables[idx4 & 0xFF] name_check += name_tables[idx2 & 0xFF] else: # 扩展评估版 name_check ^= name_tables[(idx0 + 0x3F) & 0xFF] name_check *= name_tables[(idx0 + 0x17) & 0xFF] name_check += name_tables[idx1 & 0xFF] name_check += name_tables[idx4 & 0xFF] name_check += name_tables[idx3 & 0xFF] idx2 = (idx2 + 0x13) & 0xFF idx1 = (idx1 + 0x09) & 0xFF idx4 = (idx4 + 0x0D) & 0xFF idx3 = (idx3 + 0x07) & 0xFF return name_check & 0xFFFFFFFF # 确保是32位def generate_ac_license(name: str, user_count: int = 1000) -> str: """ 生成 AC 类型(全功能版,支持免费升级)的注册码 Args: name: 用户名 user_count: 用户数量(1=单用户,2-999=多用户,1000=站点许可证) """ # 初始化 SN 数组 sn_int = [0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55] # 设置许可证类型为 AC(全功能版,支持免费升级) license_type = 0xAC sn_int[3] = license_type # 计算免费升级结束日期 license_free_upgrade_end_of_date = RLSDATE + FREE_UPGRADE_DAYS print(f"免费升级结束日期: 0x{license_free_upgrade_end_of_date:08X}") # 计算用户名校验 license_register_type = 1 # 正式注册版 name_check = get_name_check(name, license_register_type, license_free_upgrade_end_of_date, user_count) print(f"用户名校验值: 0x{name_check:08X}") # 将用户名校验值写入 SN sn_int[4] = (name_check) & 0xFF sn_int[5] = (name_check >> 8) & 0xFF sn_int[6] = (name_check >> 16) & 0xFF sn_int[7] = (name_check >> 24) & 0xFF # 计算用户数校验 user_count_encoded = get_uncheck2(user_count) print(f"用户数校验: 0x{user_count_encoded:04X}") # 将用户数校验写入 SN sn_int[1] = ((user_count_encoded >> 8) & 0xFF) ^ sn_int[7] sn_int[2] = (user_count_encoded & 0xFF) ^ sn_int[5] # 计算免费升级天数校验 sn_date = get_uncheck3(0x005B8C27, RLSDATE) sn_date &= 0x00FFFFFF print(f"免费升级天数编码: 0x{sn_date:08X}") # 将免费升级天数校验写入 SN sn_int[0] = (sn_date & 0xFF) ^ sn_int[6] sn_int[8] = ((sn_date >> 8) & 0xFF) ^ sn_int[4] sn_int[9] = ((sn_date >> 16) & 0xFF) ^ sn_int[5] # 格式化为注册码字符串 license_key = f"{sn_int[0]:02X}{sn_int[1]:02X}-{sn_int[2]:02X}{sn_int[3]:02X}-" \ f"{sn_int[4]:02X}{sn_int[5]:02X}-{sn_int[6]:02X}{sn_int[7]:02X}-" \ f"{sn_int[8]:02X}{sn_int[9]:02X}" return license_keydef main(): """主函数""" print("=== AC 类型(全功能版,支持免费升级)注册机 ===\n") # 获取用户名 name = input("请输入用户名: ").strip() if not name: name = "admin" # 获取用户数量 try: user_input = input("请输入用户数量 (1=单用户, 2-999=多用户, 1000=站点许可证) [默认1000]: ").strip() if not user_input: user_count = 1000 else: user_count = int(user_input) except ValueError: print("输入无效,使用默认值1000") user_count = 1000 # 确保用户数量在有效范围内 if user_count < 1: user_count = 1 elif user_count > 1000: user_count = 1000 print(f"\n生成注册信息...") print(f"用户名: {name}") print(f"用户数量: {user_count}") # 生成注册码 license_key = generate_ac_license(name, user_count) print(f"\n生成的注册码:") print(f"SN: {license_key}") # 显示注册信息 print(f"\n注册信息:") print(f"- 许可证类型: AC (全功能版,支持免费升级)") print(f"- 发布日期: 2019/12/06") print(f"- 免费升级截止: 2048/10/24") print(f"- 用户数量: {user_count}") print(f"- 版本ID: {VERID}")if __name__ == "__main__": main() |


5.网络认证
定位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 00000001402B1BF8 41:B8 4C4F0000 mov r8d,0x4F4C 1823600000001402B1BFE 48:8B0D B371D400 mov rcx,qword ptr ds:[0x140FF8DB8] 第3次引用140ff8db800000001402B1C05 E8 5521D5FF call 010editor.140003D5F 注册码检查00000001402B1C0A 44:8BF0 mov r14d,eax 00000001402B1C0D BA 11000000 mov edx,0x11 00000001402B1C12 41:B8 4C4F0000 mov r8d,0x4F4C 1823600000001402B1C18 48:8B0D 9971D400 mov rcx,qword ptr ds:[0x140FF8DB8] 第4次引用140ff8db800000001402B1C1F E8 596CD5FF call 010editor.14000887D 注册码检查 及 试用期扩充的检查00000001402B1C24 8BD8 mov ebx,eax 00000001402B1C26 48:8B0D 8B71D400 mov rcx,qword ptr ds:[0x140FF8DB8] 第5次引用140ff8db800000001402B1C2D 41:81FE E7000000 cmp r14d,0xE7 00000001402B1C34 0F84 43010000 je 010editor.1402B1D7D 成功标志3, r14d=0xe700000001402B1C3A 8379 7C 00 cmp dword ptr ds:[rcx+0x7C],0x0 是否进行网络验证,0-不验证,1-验证00000001402B1C3E 0F84 39010000 je 010editor.1402B1D7D 成功标志4,与3选一00000001402B1C44 33D2 xor edx,edx 00000001402B1C46 48:8BCE mov rcx,rsi 00000001402B1C49 E8 CDCDD5FF call 010editor.14000EA1B 网络验证00000001402B1C4E 85C0 test eax,eax eax 为返回验证id00000001402B1C50 0F89 E0000000 jns 010editor.1402B1D36 非负跳转,eax非负,需要再次判断00000001402B1C56 41:B8 0A000000 mov r8d,0xA 0A:'\n'00000001402B1C5C 8BD0 mov edx,eax 00000001402B1C5E 48:8D4D E7 lea rcx,qword ptr ss:[rbp-0x19] |
再ida中网络验证代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | // Hidden C++ exception states: #wind=5__int64 __fastcall sub_7FF6BB3E4320(int a1, int a2){ QByteArray *v4; // rax char *v5; // rax _BYTE *v6; // rdx char v7; // cl __int64 v8; // rax __int64 v9; // rax QByteArray *v10; // rax char *v11; // rax _BYTE *v12; // rdx char v13; // cl char *v14; // rcx char *v15; // rcx char *v16; // rcx char *v17; // rcx char *v18; // rdx char *v19; // rcx char *v20; // rcx char *v21; // rcx char *v22; // rcx char *v23; // rcx char *v24; // rcx char *v25; // rcx char *v26; // rcx char *v27; // rcx char *v28; // rcx char *v29; // rcx char *v30; // rcx char *v31; // rcx char *v32; // rcx char *v33; // rcx char *v34; // rcx char *v35; // rcx char *v36; // rcx char *v37; // rcx char *v38; // rcx char *v39; // rcx char *v40; // rcx char *v41; // rcx char *v42; // rcx char *v43; // rcx char *v44; // rcx char *v45; // rcx int v46; // ebx unsigned int v47; // ebx unsigned int v49; // [rsp+30h] [rbp-D0h] BYREF void *Block; // [rsp+38h] [rbp-C8h] BYREF _BYTE v51[24]; // [rsp+40h] [rbp-C0h] BYREF _BYTE v52[24]; // [rsp+58h] [rbp-A8h] BYREF _BYTE v53[31]; // [rsp+70h] [rbp-90h] BYREF char v54; // [rsp+8Fh] [rbp-71h] BYREF char v55[2048]; // [rsp+90h] [rbp-70h] BYREF char Buffer[1024]; // [rsp+890h] [rbp+790h] BYREF _BYTE v57[512]; // [rsp+C90h] [rbp+B90h] BYREF _BYTE v58[512]; // [rsp+E90h] [rbp+D90h] BYREF Block = 0LL; v49 = 1024; if ( qword_7FF6BC128DB8 ) { QString::QString(v51, (qword_7FF6BC128DB8 + 24)); v4 = QString::toUtf8(v51, v52); v5 = QByteArray::data(v4); v6 = (v57 - v5); do { v7 = *v5; v5[v6] = *v5; ++v5; } while ( v7 ); QByteArray::~QByteArray(v52); QString::~QString(v51); v8 = sub_7FF6BB138C0B(qword_7FF6BC128DB8, v53); v9 = replaceConfusingCharacters_7FF6DED588EB(qword_7FF6BC128DB8, v51, v8); v10 = QString::toLocal8Bit(v9, v52); v11 = QByteArray::data(v10); v12 = (v58 - v11); do { v13 = *v11; v11[v12] = *v11; ++v11; } while ( v13 ); QByteArray::~QByteArray(v52); QString::~QString(v51); QString::~QString(v53); sub_7FF6BB1396BF(qword_7FF6BC128DB8, v57); strcpy(v55, "317K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2K6N6$3g2W2N6s2y4U0j5i4m8W2i4K6u0W2j5$3!0E0i4K6u0r3"); v14 = &v54; do ++v14; while ( *v14 ); strcpy(v14, "cgibin"); v15 = &v54; do ++v15; while ( *v15 ); *v15 = 47; v16 = &v54; do ++v16; while ( *v16 ); strcpy(v16, "010editor"); v17 = &v54; do ++v17; while ( *v17 ); *v17 = 95; v18 = &v54; do ++v18; while ( *v18 ); strcpy(v18, "check"); v19 = &v54; do ++v19; while ( *v19 ); *v19 = 95; v20 = &v54; do ++v20; while ( *v20 ); *v20 = 0x65736E6563696CLL; v21 = &v54; do ++v21; while ( *v21 ); *v21 = 95; v22 = &v54; do ++v22; while ( *v22 ); *v22 = 57; v23 = &v54; do ++v23; while ( *v23 ); *v23 = 98; v24 = &v54; do ++v24; while ( *v24 ); *v24 = 46; v25 = &v54; do ++v25; while ( *v25 ); *v25 = 7366768; v26 = &v54; do ++v26; while ( *v26 ); strcpy(v26, "?"); v27 = &v54; do ++v27; while ( *v27 ); *v27 = 116; v28 = &v54; do ++v28; while ( *v28 ); *v28 = 61; sub_7FF6BB13C82E(v57, Buffer); v29 = &v54; do ++v29; while ( *v29 ); strcpy(v29, Buffer); v30 = &v54; do ++v30; while ( *v30 ); strcpy(v30, "&"); v31 = &v54; do ++v31; while ( *v31 ); *v31 = 7173491; v32 = &v54; do ++v32; while ( *v32 ); *v32 = 61; sub_7FF6BB13C82E(v58, Buffer); v33 = &v54; do ++v33; while ( *v33 ); strcpy(v33, Buffer); v34 = &v54; do ++v34; while ( *v34 ); strcpy(v34, "&"); v35 = &v54; do ++v35; while ( *v35 ); strcpy(v35, "id"); v36 = &v54; do ++v36; while ( *v36 ); *v36 = 61; sub_7FF6BB13D319(Buffer, "%d"); v37 = &v54; do ++v37; while ( *v37 ); strcpy(v37, Buffer); v38 = &v54; do ++v38; while ( *v38 ); strcpy(v38, "&"); v39 = &v54; do ++v39; while ( *v39 ); *v39 = 7039075; v40 = &v54; do ++v40; while ( *v40 ); *v40 = 61; sub_7FF6BB13D319(Buffer, "%d"); v41 = &v54; do ++v41; while ( *v41 ); strcpy(v41, Buffer); v42 = &v54; do ++v42; while ( *v42 ); strcpy(v42, "&"); v43 = &v54; do ++v43; while ( *v43 ); *v43 = 7371124; v44 = &v54; do ++v44; while ( *v44 ); *v44 = 61; sub_7FF6BB13D319(Buffer, "%d"); v45 = &v54; do ++v45; while ( *v45 ); strcpy(v45, Buffer); QString::QString(v51, v55); if ( a2 ) { sub_7FF6BB13F8B7(qword_7FF6BC128DD8, v51, 0, a1, "1On_HttpCheckLicenseFinished(char*,int)"); QString::~QString(v51); return 0LL; } v46 = sub_7FF6BB1399F3(qword_7FF6BC128DD8, v51, &Block, &v49, 1, 0LL); QString::~QString(v51); if ( !v46 ) { v47 = sub_7FF6BB132B44(qword_7FF6BC128DB8, Block, v49); j_j_j_j_free_0(Block); return v47; } } return 0xFFFFFFFFLL;} |
错误提示如下

下面对里面函数解析
其中在sub_7FF6BB13C82E(v58, Buffer);这个里面主要是对密钥与字符串做的替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | // Hidden C++ exception states: #wind=5void __fastcall sub_7FF6BB6014C0(char *a1, _BYTE *a2){ char v2; // r10 char v3; // r11 char *v4; // r9 char v5; // al unsigned __int8 v6; // r8 char v7; // cl unsigned __int8 v8; // r8 char v9; // al v2 = *a1; v3 = 0; v4 = a1; if ( *a1 ) { do { ++v4; a2 += 2; v5 = 48; v6 = v2 ^ (93 - 83 * v3); v7 = v6 >> 4; if ( (v6 >> 4) >= 0xAu ) v5 = 55; v8 = v6 & 0xF; *(a2 - 2) = v7 + v5; v9 = 48; if ( v8 >= 0xAu ) v9 = 55; ++v3; *(a2 - 1) = v8 + v9; v2 = *v4; } while ( *v4 ); *a2 = 0; } else { *a2 = 0; }} |
函数在栈上构造一个URL字符串,格式如下:
e6dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2K6N6$3g2W2N6s2y4U0j5i4m8W2i4K6u0W2j5$3!0E0i4K6u0r3j5$3N6A6j5X3W2F1i4K6u0r3x3o6p5H3k6h3c8A6N6r3!0J5i4K6g2X3j5$3S2W2j5$3E0Q4y4h3k6D9K9h3y4W2L8Y4y4W2i4K6g2X3z5h3u0Q4x3X3g2H3K9s2m8Q4x3@1k6@1i4K6y4p5i4K6g2n7N6X3q4D9N6h3g2Q4y4f1c8Q4x3U0k6S2L8i4m8Q4x3@1u0K6i4K6y4p5i4K6g2n7N6X3q4D9N6h3g2Q4y4f1c8Q4x3U0k6S2L8i4m8Q4x3@1u0A6k6q4)9K6c8q4)9#2b7Y4k6S2L8s2g2W2i4K6g2p5i4K6t1$3j5h3#2H3i4K6y4n7N6X3g2J5i4K6y4p5i4K6g2n7N6X3q4D9N6h3g2Q4y4f1c8Q4x3U0k6S2L8i4m8Q4x3@1u0T1K9h3c8Q4x3@1c8Q4y4f1u0$3j5h3I4#2k6g2)9#2c8l9`.`.
测试
00000045D10F48A8 0000020D5EB625B0 L"23dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2K6N6$3g2W2N6s2y4U0j5i4m8W2i4K6u0W2j5$3!0E0i4K6u0r3j5$3N6A6j5X3W2F1i4K6u0r3x3o6p5H3k6h3c8A6N6r3!0J5i4K6g2X3j5$3S2W2j5$3E0Q4y4h3k6D9K9h3y4W2L8Y4y4W2i4K6g2X3z5h3u0Q4x3X3g2H3K9s2m8Q4x3@1k6@1i4K6y4p5x3K6x3$3x3@1x3J5x3o6j5%4z5o6R3^5y4f1b7J5c8g2)9J5y4X3q4E0M7q4)9K6b7Y4y4#2L8g2)9K6c8o6k6o6x3@1t1^5x3e0t1J5x3@1x3^5b7K6f1^5y4e0V1^5y4U0g2r3y4f1b7^5c8e0c8p5y4U0c8r3c8f1t1I4x3e0R3&6b7@1t1$3x3e0W2m8y4@1x3^5y4@1k6p5c8W2)9J5y4X3q4E0M7q4)9K6b7X3W2V1i4K6y4p5x3q4)9J5y4X3q4E0M7q4)9K6b7X3y4Z5K9#2)9K6c8o6f1K6x3K6j5%4i4K6t1$3j5h3#2H3i4K6y4n7N6s2W2H3i4K6y4p5x3l9`.`."

下面我们进行破解就很简单,直接改跳转

0x00000001402B1D36将这个地址改成上面两个跳转的地址0x00000001402B1D7D
对响应进行分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | __int64 __fastcall sub_7FF67E9FD990(__int64 a1, const char *a2, int a3){ size_t v3; // rbx char *v6; // rsi __int64 v8; // r8 __int64 v9; // rcx char v10; // al __int64 v11; // rax __int64 v12; // rcx bool v13; // zf int v14; // eax int v15; // eax __int64 v16; // rdx __int64 v17; // rcx __int64 v18; // r8 __int64 v19; // r9 int v20; // eax char String[1040]; // [rsp+30h] [rbp-438h] BYREF v3 = a3; v6 = j_j_j___2_YAPEAX_K_Z(a3 + 1LL); strncpy(v6, a2, v3); v6[v3] = 0; if ( sub_7FF67E5C5A6F(a1, v6, "ss", String, 1024) >= 0 ) { v8 = 0LL; v9 = 0LL; while ( 1 ) { v10 = String[v9++]; if ( v10 != aError_1[v9 - 1] ) // 判断是不是error break; if ( v9 == 6 ) { j_j_j_j_free_0(v6); return 0xFFFFFFFALL; } } v11 = 0LL; while ( 1 ) { v12 = String[v11++]; v13 = v12 == aInvalid[v11 - 1]; // 判断invalid 无效 if ( v12 != aInvalid[v11 - 1] ) break; if ( v11 == 8 ) { v13 = v12 == aInvalid[7]; break; } } LOBYTE(v8) = v13; *(a1 + 124) = v8; v14 = sub_7FF67E5C3FA3(v12, String, v8, "invalid"); sub_7FF67E5C2860(v14); if ( *(a1 + 124) == 1 ) { j_j_j_j_free_0(v6); return 0LL; } else if ( sub_7FF67E5C5A6F(a1, v6, &word_7FF67ED820A0, String, 1024) >= 0 )// 读取并验证ID字段 { v15 = atoi(String); *(a1 + 120) = v15; if ( v15 ) { v20 = sub_7FF67E5C3FA3(v17, v16, v18, v19); sub_7FF67E5C2EE6(v20); // 注册表写入id与1690216811 j_j_j_j_free_0(v6); return *(a1 + 120); // 返回ID } else { j_j_j_j_free_0(v6); return 0xFFFFFFFDLL; // ID为0,无效 } } else { j_j_j_j_free_0(v6); return 4294967292LL; } } else { j_j_j_j_free_0(v6); return 4294967289LL; }} |
本地做一个验证服务器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | import socketimport sysimport threadingdef handle_client(client_socket, client_addr): """处理客户端连接""" try: # 接收数据 request_data = client_socket.recv(999).decode('utf-8') if not request_data: return print(f"接收来自 {client_addr} 的数据:") print(request_data) print("-" * 50) # 检查是否包含退出指令 if "?quit=1" in request_data: print("收到退出指令,服务器将关闭") return "quit" # 准备响应数据 response_body = "<ss><id>33333</id></ss>" response_headers = [ "HTTP/1.0 200 OK", "Content-type: text/html", f"Content-length: {len(response_body)}", "", response_body ] response = "\r\n".join(response_headers) print("发送响应数据:") print(response) print("-" * 50) # 发送响应 client_socket.send(response.encode('utf-8')) except Exception as e: print(f"处理客户端 {client_addr} 时出错: {e}") finally: # 关闭连接 try: client_socket.shutdown(socket.SHUT_RDWR) except: pass client_socket.close()def start_http_server(host='127.0.0.1', port=80): """启动HTTP服务器""" # 创建socket try: server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) except socket.error as e: print(f"创建socket失败: {e}") return False # 绑定地址和端口 try: server_socket.bind((host, port)) server_socket.listen(10) except socket.error as e: print(f"绑定端口 {port} 失败: {e}") print("请确保有权限使用80端口,或尝试使用其他端口") server_socket.close() return False print(f"WebServer 已启动...") print(f"访问 http://{host}:{port}") print(f"退出服务器,请访问 http://{host}:{port}/?quit=1") print("-" * 60) try: while True: try: # 接受客户端连接 client_socket, client_addr = server_socket.accept() print(f"新连接来自: {client_addr}") # 处理客户端请求 result = handle_client(client_socket, client_addr) if result == "quit": break except socket.error as e: print(f"接受连接时出错: {e}") continue except KeyboardInterrupt: print("\n收到中断信号,服务器关闭") except Exception as e: print(f"服务器运行出错: {e}") finally: server_socket.close() print("服务器已关闭") return Truedef start_server_with_fallback(): """尝试启动服务器,如果80端口被占用则使用备用端口""" # 尝试的端口列表 ports_to_try = [80, 8080, 8000, 8888] for port in ports_to_try: print(f"尝试启动在端口 {port}...") if start_http_server('127.0.0.1', port): break else: print(f"端口 {port} 启动失败,尝试下一个端口...") if port == ports_to_try[-1]: print("所有端口尝试失败,请检查系统权限或端口占用情况")if __name__ == "__main__": print("Python HTTP 服务器") print("=" * 60) # 检查管理员权限(在Windows上需要管理员权限才能绑定80端口) if sys.platform == "win32": try: # 尝试绑定80端口来检查权限 test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) test_socket.bind(('127.0.0.1', 80)) test_socket.close() print("检测到有权限使用80端口") start_http_server() except PermissionError: print("没有管理员权限,无法使用80端口") print("将尝试使用备用端口...") start_server_with_fallback() except Exception as e: print(f"检测权限时出错: {e}") start_server_with_fallback() else: # 在Linux/macOS上,可能需要sudo权限 start_server_with_fallback() |
然后hosts文件中加上如下数据:
127.0.0.1 752K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4y4%4k6h3g2@1M7$3y4S2M7r3g2Q4x3X3g2U0L8$3@1`.
经过调试

此处发送数据包
对应v46 = sub_7FF67E5C99F3(qword_7FF67F5B8DD8, v51, &Block, &v49, 1, 0LL);// HTTP请求发送

然后再此处写入id可对应注册表查看

如果是 1690216811,表示成功,这个数字解码后是 0,如果是 1690236428,则表示失败,这个数字解码后是 1(这块解码没有跟进,直接照搬)


最后成功,也就是注册表中有这两个注册信息就可以绕过网络验证了
Solly大佬文章链接:b36K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4x3o6V1#2z5o6f1#2i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9