本帖最后由 LwithWforver 于 2014-11-26 18:16 编辑
先在原创破解区分享了注册机,又厚脸皮来发个帖,希望不算违规。
下面是正题:
010注册算法分析1. 关键函数定位进入register窗口,随便填入name,然后check license,弹框信息”Invalid name or password. Please enter your name and password exactly as given when you purchased 010 Editor (make sure no quotes are included).”,通过该信息在 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
| void __usercall chekc(char a1<zf>, int a2<ecx>)
{
//获取用户名
//是否为空
//获取注册码
//是否为空
//注册码格式检查xxxx-xxxx-xxxx-xxxx-xxxx
v42 = (void *)myCheck(mygbName, 3u, '9A'); //注册码验证,返回值v42对于是否注册成功,有很大关系,返回值为231,为失败
v26 = mySecondCheck(mygbName, 3u, '9A'); // 第二次检查,如果返回中为219,就会进入后面的注册成功提示
//myCheck中返回不等于231,mygbName + 44提示进入网络验证,没有弄清楚,哪里会等于1
if ( v42 != (void *)231 && *(_DWORD *)(mygbName + 44) )
{
v27 = myWebCheck((void *)mygbName, 0);
if(v27 < 0 )
{
v43 = myMsg("Could not contact the webserver. Please check your internet connection. If your internet connection is currently working, the server may be down. If this problem persists, please visit 'http://www.sweetscape.com/support/' (", 0xFFFFFFFFu);
}
if ( !v27 )
{
v43 = myMsg( "010 Editor has detected that you have entered an invalid license. Please check to see if your license is entered correctly and try again. If this problem persists, please visit 'http://www.sweetscape.com/support/'.", 0xFFFFFFFFu);
}
v33 = mySecondCheck(mygbName, 3u, '9A');//网络验证是否成功,成功,返回219
}
if ( v43 == (void *)219 ) // v43 == 219,注册成功
{
v43 = myMsg("Password accepted. Thank you for purchasing 010 Editor!", 0xFFFFFFFFu);
//写入注册表
}
}
| 2. 算法分析下面看看主要的验证函数myCheck和mySecondCheck,代码如下: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| signed int __thiscall mySecondCheck(int this, unsigned int a2, unsigned int a3)
{
int v3; // esi@1
signed int result; // eax@2
int v5; // eax@3
int v6; // eax@6
int v7; // eax@9
v3 = this;
if ( *(_DWORD *)(this + 44) ) // 要让这个值等于0,否则进入网络验证,初始化就是0
return 275;
v5 = myCheck(this, a2, a3); //可以看到,只有返回值是45时,才能返回219,注册成功
if ( v5 == 45 )
{
result = 219; // 返回219, 注册成功
}
}
|
可以看到,只有返回值是45时,才能返回219,注册成功。
那么返回myCheck看看,怎么才能得到45的返回值,整个返回值查看一下,只有两处位置,可能返回45,如下: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
| if ( v26 == 0x9Cu )
{
v20 = *(_DWORD *)(regdlg + 28) < a2;
return (-v20 & 0x21) + 45; // 成功?
}
if ( v26 != 0xFCu )
{
if ( v26 == 0xACu && v33 )
{
v20 = v33 < a3;
return (-v20 & 0x21) + 45; // 成功?
}
return 231;
}
|
那么就需要回溯回去,看看v26,是如何得到的,只有在v26等于0x9c或者0xAc时,才有可能注册成功。下面看看完整代码: 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
| int __thiscall myCheck(int this, unsigned int a2, unsigned int a3)
{
//name和key长度是否为0
//myPassCheck(this, (int)&v23);//将key字符串转换成数值,每两个字符转化成2为十六进制数,
//xxxx-xxxx-xxxx-xxxx-xxxx分别对应k1k2-k3k4-k5k6-k7k8-k9k10
//v23其实就是一个数组,存的就是k1-k10
//检测name是否等于'999',是,则失败
if ( v26 == 0x9Cu )
{
LOBYTE(v32) = v23 ^ v28; // k1^k7
LOWORD(v6) = (unsigned __int8)(v24 ^ v29); // k2^k8
LOWORD(v7) = (unsigned __int8)(v25 ^ HIBYTE(v27));// k3^k6
v11 = v7 + ((_DWORD)v6 << 8); // v11 = k3^k6 + ((k2^k8)<<8)
*(_DWORD *)(regdlg + 28) = (unsigned __int8)myCal1(v23 ^ v28);// k1^k7 => 不能等于0
v9 = myCal2(v11); // 不能等于0
v10 = *(_DWORD *)(regdlg + 28);
*(_DWORD *)(regdlg + 32) = (unsigned __int16)v9;
// v10==0,v9==0或者v9>0x3e8,返回231
if ( !v10 || !v9 || (unsigned __int16)v9 > 0x3E8u )
return 231;
v12 = v10 < 2 ? v10 : 0; // v12 = 0或者1
}else
{
if(v26 == 0xFC)
{//不可能成功
}esle
{
//v26不等于0xAC,退出,返回231,失败
//myCal2(k3^k6 + ((k2^k8)<<8)) > 0x3E8, 失败
if ( v26 != 0xACu
|| (v15 = v24 ^ v29,//k2^k8
v16 = v25 ^ HIBYTE(v27),//k3^k6
*(_DWORD *)(regdlg + 28) = 2,
v14 = (unsigned __int16)myCal2(v16 + (v15 << 8)),v11 = k3^k6 + ((k2^k8)<<8)
*(_DWORD *)(regdlg + 32) = (unsigned __int16)v14,
!(_WORD)v14)
|| v14 > 0x3E8 )
return 231;
//sub_4FD0B9( (k1^k7 + (k9^k5 + (k6^k10)<<8)<<8), xxx);
//其实就是凑成十六进制数(k6^k10)(k9^k5)(k1^k7)
v17 = sub_4FD0B9(
(v23 ^ v28) + (((v30 ^ (unsigned __int8)v27) + ((HIBYTE(v27) ^ v31) << 8)) << 8),
(char *)loc_5B8C25 + 2);
v33 = v17;
*(_DWORD *)(regdlg + 52) = v17;
v12 = v17;
}
}
//编码name,返回给v18,
v18 = myEncStr(*(const char **)(*(_DWORD *)qstrname + 12), v26 != -4, v12, *(_DWORD *)(regdlg + 32));
//如果v18,如0xABCDEF10分解成0xAB,0xCD, 0xEF10,不等于v29, v28,v27就失败,其实就是
//(k6k5) = 0xEF10, k7 = 0xCD, k8 = 0xAB
if ( v27 != (_WORD)v18
|| v28 != (unsigned __int8)((unsigned int)v18 >> 0x10u)
|| v29 != (unsigned __int8)((unsigned int)v18 >> 0x18u) )
return 231; // 这三个条件很重要啊
//下面就接近成功了,就是上面提到的返回45的结果,成功
if ( v26 == 0x9Cu )
{
//这里就需要regdlg + 28 = myCal1(k1^k7) >= a2,也就是3,传入的a2是3
//然后v20就是0,那么(-v20 & 0x21)=0,最后返回45
v20 = *(_DWORD *)(regdlg + 28) < a2;
return (-v20 & 0x21) + 45; // 成功?
}
if ( v26 != 0xFCu )
{
if ( v26 == 0xACu && v33 )
{
v20 = v33 < a3;
return (-v20 & 0x21) + 45; // 成功?
}
return 231;
}
}
|
最后总结一下算法,基本可以列出一个方程类似的东西: 1
2
3
4
5
6
| k4 = 0x9C或者0xAC
myCal1(k1^k7) >= 3;//可以任取大于等于3的值,算出k1^k7=?
myCal2(k3^k6 + ((k2^k8)<<8)) > 0;//可以任去大于0
myCal2(k3^k6 + ((k2^k8)<<8)) < 0x3E8;//小于0x3E8的某一个值,算出k3^k6 + ((k2^k8)<<8) = ?
k8k7k6k5 = v18;//0xABCDEF10,可以得到k5=?,k6=?,k7=?,k8=?,由此可以算出上面的k1,k2
sub_4FD0B9((k6^k10)(k9^k5)(k1^k7), xx) = ?//可以算出k9,k10
|
后面的就不贴了,注册机源码大家可以去我的博客(http://t.cn/Rz73ngL)看看
|