• 标 题:NetTerm 4.2.c注册过程分析及注册机制作SBS (6千字)
  • 作 者:yuky
  • 时 间:2001-4-27 16:50:49

作者:Yuky
目标:NetTerm 4.2.c
下载:http://topsoft.163.com/software/download.php?id=3927
工具:trw2000注册版
时间:2001.4.26-2001.4.27
地点:广州


NetTerm是windows下的终端软件, 用起来还不错,只是它经常要你注册,我受不了它的殷切提醒,决定不让它打扰我。

先随便注册一下,它不像其它软件梆的一声弹出对话框上写“注册失败“,而是很温柔的在不宜觉察的状态条上显示“错误的码“。

好了,可以开工了。用户名:yxg,密码:78787878。在trw2000中下一个常用的中断“bpx hmemcpy“,点确定,被拦下,下“bd *",关掉断点,下“pmodule“返回到NetTerm的领空。

:00430B48 8D85E8FEFFFF lea eax, dword ptr [ebp+FFFFFEE8]
:00430B4E 50 push eax ;eax->'78787878'
:00430B4F 8D85E8FDFFFF lea eax, dword ptr [ebp+FFFFFDE8]
:00430B55 50 push eax ;eax->'yxg'

.... .....

:0043096C 83BDFCFEFFFF08 cmp dword ptr [ebp+FFFFFEFC], 00000008 ;比较注册码长度是否为8
:00430973 0F8547000000 jne 004309C0

..... ....

:0043098E E867C60100 Call IsIlogo!DoRegistrator ;调用isilogo.dll的DoRegistrator函数,看来是注册核心

:00430993 8985F8FDFFFF mov dword ptr [ebp+FFFFFDF8], eax
:00430999 83BDF8FDFFFF00 cmp dword ptr [ebp+FFFFFDF8], 00000000
:004309A0 0F841A000000 je 004309C0

这个DoRegistrator很可疑,看一下堆栈,其中有字符串'yxg','78787878','a123456789cfikuq3A9S'的地址,还有一个整数4,可能是标志。那么这个函数一定是注册码的比对函数了,不敢迟疑,F8追进去。

经过几个比对及跳转到:

:4E2220 PUSH EAX ;EAX->'78787878'目标出现
:4E222A CALL ;这个函数将字符串'78787878'转换成HEX串78787878。
这个函数的算法:从注册码中取出一个字符,和字符串'a123456789cfikuq'比较,如果找到,返回在串中的位置,如:字符'a'返回0,字符'1'返回1,字符'c'返回0xa。如果没找到,返回0。

:4E2330 CALL 4E2357
看一下堆栈,有'yxg',0x78787878的地址,看来有问题,追进去。发现这里是注册码算法的关键。
再看一下用户名:'yxg',Hex串为:79 78 67。注册码(已转换成Hex串):78 78 78 78
循环开始
1.79 xor 78=01 78+79=F1
2.F1 xor 01=F0 78+F1=69
3.69 xor F0=99 78+69=E1
到这里后,把99放到78的位置上,现在注册码的位置上为:99 78 78 78
接下来对第二对注册码运算
4.E1 xor 78=99 78+E1=59
5.59 xor 99=C0 78+59=D1
6.D1 xor C0=11 78+D1=49
现在注册码的位置上为:99 11 78 78
再取下一对注册码78:
7.49 xor 78=31 78+49=C1
8.C1 xor 31=F0 78+C1=39
9.39 xor F0=C9 78+39=B1
现在注册码的位置上为:99 11 C9 78
再取下一对注册码78:
10.B1 xor 78=C9 78+B1=29
11.29 xor C9=E0 78+29=A1
12.A1 xor E0=41 78+A1=19
对注册码的处理完成,现在变为:99 11 C9 41

:1C002259 8D8500FFFFFF lea eax, dword ptr [ebp+FFFFFF00]
:1C00225F 50 push eax
:1C002260 8D85FCFCFFFF lea eax, dword ptr [ebp+FFFFFCFC]
:1C002266 50 push eax
* Reference To: KERNEL32.lstrcmpA, Ord:0269h |
:1C002267 FF157061001C Call dword ptr [1C006170]
这个lstrcmpA函数一看就知是字符串比较函数,看它的堆栈,一个是处理过的注册码的地址,一个是"3A9S"的地址,还记得strcmp()函数吗,如果两个字符串相等,返回0。往下看:

:1C00226D 85C0 test eax, eax ;比较返回值是否是0
:1C00226F 0F850F000000 jne 1C002284 ;不是的话跳到1C002284,
:1C002275 B801000000 mov eax, 00000001
:1C00227A E90C000000 jmp 1C00228B
:1C00227F E907000000 jmp 1C00228B
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:1C00226F(C)
|
:1C002284 33C0 xor eax, eax ;不是0执行这里,使eax清零,
:1C002286 E900000000 jmp 1C00228B
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:1C00219C(U), :1C0021C0(U), :1C00227A(U), :1C00227F(U), :1C002286(U)
|
:1C00228B 5F pop edi
:1C00228C 5E pop esi
:1C00228D 5B pop ebx
:1C00228E C9 leave
:1C00228F C21000 ret 0010

然后程序从dll返回到NetTerm,把eax的值带回来。如果不相同,经过xor eax,eax后,eax为0.
:00430993 8985F8FDFFFF mov dword ptr [ebp+FFFFFDF8], eax
:00430999 83BDF8FDFFFF00 cmp dword ptr [ebp+FFFFFDF8], 00000000
:004309A0 0F841A000000 je 004309C0

再往下走,过了几十行,按F4看到状态条上的“错误的码“。
好了,现在我们试试,将:1C00226D的test eax, eax改为dec eax;nop或xor eax,eax然后运行,仍是原来的用户名注册码,不过现在显示“验证成功!“,看来是找对地方了,我们已经分析了它的注册码比较过程,所以不仅仅要爆破,还要算出注册码。由它的注册码计算过程来看,算法很简单:


原注册码 xor <----异或因子1
|
|
中间结果 xor <----异或因子2
|
|
中间结果 xor <----异或因子3
|
|
结果 (一个字节)

上课时老师讲过如果A XOR B=C,那么C XOR A=B,所以我们只要知道最后的结果和每次的异或因子就能倒着推回来。看一下异或因子的产生过程,是用户名前两个字符的迭代过程,那么和第三个以后的无关,一会儿我们会验证它的正确性。从程序看,最后的比对结果为字符串"3A9S"。现在两者都有了,可以反推了。反推的结果拆开,作为索引找 'a123456789cfikuq'中的对应字符,最后输出就是正确的注册码了!编一个程序自动算注册码。

main()
{
char str[]="a123456789cfikuq";
char usrname[]="yxg";
char x[13];
char pass[]="3A9S";
int i,j,k;
x[0]=usrname[0];
x[1]=usrname[1]+usrname[0];
for(i=2;i<12;i++)
x[i]=usrname[1]+x[i-1];
for(i--,j=3;j>=0;j--)
 for(k=0;k<3;k++,i--)
  pass[j]^=x[i];
for(i=0;i<3;i++)
{
j=i*2;
k=(BYTE)pass[i]/0x10;
x[j]=str[k];
k=(BYTE)pass[i]%0x10;
x[j+1]=str[k];
}
x[j+2]=0;
printf("注册码是:%s",x);
}

算出用户:yxg对应的正确的注册码:k228886c。输入,点确定,显示“验证成功!“。
验证一下,把第三个字符从g改为1,现在用户名:yx1,注册码:k228886c,注册,又成功了。看来分析的没错。

现在我们已经可以来做注册机了!
随便输入个用户名,用程序算出的注册码不对。看来程序中还有对用户名的操作,仔仔细细又跟了一遍,发现注册码的循环次数和用户名的长度有关,于是用VC++写出注册机如下:(下载)

void CCnettermDlg::OnOK()
{
char str[]="a123456789cfikuq";
char pass[]="3A9S";
int i,j,k,c;
UpdateData();
k=m_usr.GetLength();
if(k==0)
return;
char *usrname=m_usr.GetBuffer(k+1);
char *x=new char[4*k+1];
x[0]=usrname[0];
x[1]=usrname[1]+usrname[0];
for(i=2;i<4*k;i++)
x[i]=usrname[1]+x[i-1];
for(i--,j=3;j>=0;j--)
for(c=0;c<k,c++,i--)
pass[j]^=x[i];
for(i=0;i<4;i++)
{
j=i*2;
k=(BYTE)pass[i]/0x10;
x[j]=str[k];
k=(BYTE)pass[i]%0x10;
x[j+1]=str[k];
}
x[j+2]=0;
m_str=x;
UpdateData(FALSE);
delete x;
m_usr.ReleaseBuffer();
}


到此对于NetTerm 4.2.c的破解完成!
题目中的SBS就是"Step by Step",希望我讲的清楚,大家听得明白!
这是我第一次做出注册机,以前曾想做VZPR的注册机,可惜太复杂没能写完。这次做出,不敢独享,拿出与大家共赏。