• 标 题:Netscan pro 3.3 注册算法分析全过程
  • 作 者:风雨无阻
  • 时 间:2003年11月10日 03:59
  • 链 接:http://bbs.pediy.com

Netscan pro 3.3 注册算法分析全过程
                               文 / 风雨无阻【CCG】
Netscan pro 简介:
    一旦连结上网络,其实你就是与成千上万的电脑为邻,当你在浏览网页、ICQ、聊天时,你知道有哪些主机正在对你的电脑做些什么事情吗?这是一套在TCP/IP通讯协定下的监视工具,帮助你监控你的电脑所有的网络连结活动,确保你在Internet或是TCP/IP架构下的网络正常的安全运作,免于被外界侵犯。透过Netscan Pro可以见到所有对你的电脑的连结清单,所有由外而入的活动都一览无遗。

一、破解专用工具及基础知识
1.  使用工具:
fi3.1,Ollydbg 1.09c汉化版,W32DSM10(这几个软件可以到http://www.pediy.com下载), 
Netscan pro 3.3下载地址:http://www.skycn.com/soft/7990.html

2.  共享软件常见的几种注册过程:
(1)用户名+注册码:这种类型最常用,一般软件会要求你输入用户名和注册码,然后读取你输入的内容,调用程序中的某个函数根据用户名计算出注册码,或者根据注册码计算出用户名。这种方式现在使用的较少,适合破解初学者练习使用,因为一旦在网上公布用户名和注册码,每个人都可以使用了,本文涉及的Netscan Pro就是这种。
(2)机器码+注册码:这种方式也叫作一机一码,其实和上面差不多,只不过把用户名换成了硬盘序列号。每个硬盘的序列号不一样,这样的序列号和注册码不能通用,在一定程度上起到了软件保护的作用。
(3)使用注册文件:这种方式相对于上面两种方式保护强度就更进一层了,当你注册软件的时候,就会让你选择你的注册文件,然后读出注册文件的内容,在进行注册认证。比如国内使用很广泛的《流光》就是这种注册认证方式。
(4)网络在线认证:这种方式目前来讲保护方式最强,比如现在用的各种游戏的外挂注册就采用的是这种方式,一般这种情况在网上有一台服务器的数据库里面有已经注册的用户列表,当你使用软件的时候,软件就会把你输入的用户名和密码和服务器数据库里面的用户名密码进行比较,如果数据库中没有,就会注册失败。
以上只是共享软件众多注册方法里面几种常见的方法,通用的方法就是暴力破解(暴破),这种方法是初学者使用的方法,没有多少技术含量,当你写出某个软件的注册机的时候,标志者你的破解水平达到了中等。
当软件读入用户名以后就会调用一个函数进行注册码的计算,我们把这个CALL叫做关键CALL,如果你的目的是写出注册机就要认真读这个关键CALL里面的汇编语句,弄明白软件作者的注册算法,然后把算法用你自己熟悉的编程语言写出来,就一切OK了!软件计算出正确的注册码以后就会和输入的注册码进行比较,如果是一样的,就会注册成功,一般是将真,假两个注册码地址放在寄存器里面,紧接着对这两个地址的数据进行比较,紧跟一个跳转指令。这个跳转就是控制程序下一步显示注册成功或者注册失败。下文我会就本文的软件进行详细的解释。

二、本软件的破解步骤
1.使用侦测软件fi3.1 检测软件是否加壳,这是破解每个软件的第一步。当然如果你使用TRW2000或者是softice动态跟踪的话就不必了,如果使用ollydbg进行调试就必须保证软件无壳。方法很简单,我们将可执行文件图标拖到fi3.1的图标上放开即可,经过检测Netscan Pro 3.3没有加壳,下面就可以继续我们的注册机之路了。

2.运行程序,进行注册,随便输入用户名和注册码,这时程序会弹出注册失败的对话框。我们记下对话框的内容“注册名或注册码错误!”,然后打开W32DSM10对可执行文件Netscanpro.exe进行反汇编,再点击“参考->串式数据参考”在弹出的窗口中找到刚刚记下的注册失败对话诓的内容。我们用鼠标双击就会转到程序中,代码如下:

:00407160 E803080000              call 00407968
:00407165 83C408                  add esp, 00000008
:00407168 85C0                    test eaxeax
:0040716A 7454                    je 004071C0
:0040716C 6A00                    push 00000000
* Possible StringData Ref from Data Obj ->"你已经注册 !"
:0040716E BABE174C00              mov edx, 004C17BE
:00407173 66C745DC2000            mov [ebp-24], 0020
。。。。。。。。。。。。
:004071BE EB4D                    jmp 0040720D
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040716A(C)
:004071C0 6A00                    push 00000000
* Possible StringData Ref from Data Obj ->"注册名或注册码错误 !"
:004071C2 BADC174C00              mov edx, 004C17DC
:004071C7 66C745DC2C00            mov [ebp-24], 002C
。。。。。。。。。。。。

代码分析:
程序运行到00407160时调用了一个函数,这个函数很有可能就是我们要找的关键CALL,当让也可能是还上面的某个CALl,一般关键CALL就在0040716C的跳转指令的前面不远处,00407160的CALL结果影响EAX寄存器的值,如果注册码正确0040716A处就不会跳转从而出现注册成功的对话框,如果不正确就会跳转到004071C0 出现注册失败的对话框。

下面就要进行动态跟踪具体分析了。
3.用ollydbg打开可执行文件,使用快捷键“Ctrl+G”出现转到对话框输入407160,在按F2 在407160处设置断点,再按F9运行程序,点击注册按钮,输入用户名和注册码,点击“注册”程序会被ollydbg拦截,我们按F8单步跟踪(如果遇到CALL按F8就不会跟进,一般第一遍跟踪的时候不用跟进,只是弄明白每个CALL的具体作用是什么,找到我们所需要的关键CALL)执行完407160处的语句以后我们就会在右上的寄存器窗口发现正确的注册码保存在EDX中,这就说明此处的CALL是关键CALL,下面就要对这个CALL进行详细研究了,按 F9 就会回到程序,再次点击注册,程序被中断,我们这次按F7跟进这CALL
然后在按F8 单步跟踪,注意观察右上的寄存器窗口,程序运行到下面:

004079C3  |>PUSH netscanp.00510548           ;  ASCII "abcdefgh"
004079C8  |>CALL netscanp.00499DA4
004079CD  |>POP ECX
004079CE  |>CMP EAX,8
004079D1  |>JNB SHORT netscanp.004079D7
004079D3  |>XOR EAX,EAX
004079D5  |>JMP SHORT netscanp.00407A2A
004079D7  |>PUSH netscanp.00510548           ; /Arg1 = 00510548 ASCII "abcdefgh"
004079DC  |>CALL netscanp.0040789C          ; 
etscanp.0040789C
004079E1  |>POP ECX
004079E2  |>MOV EDX,EAX
004079E4  |>MOV EAX,netscanp.005105AC      ;  ASCII "987654321"
004079E9  |>/MOV CL,BYTE PTR DS:[EAX]
004079EB  |>|CMP CL,BYTE PTR DS:[EDX]
。。。。。。。。。。。。
00407A03  |>JNZ SHORT netscanp.004079E9
00407A05  |>JE SHORT netscanp.00407A0B
00407A07  |>XOR EAX,EAX
00407A09  |>JMP SHORT netscanp.00407A2A
。。。。。。。。。。。。
00407A25  |>MOV EAX,1
。。。。。。。。。。。。

代码分析:
004079C3 处送入用户名,接下来的一个CALL是计算用户名的长度,这一点一定要注意,一般的软件对用户名的长度都有要求,从004079CE处的语句可以看出,这个软件对用户名长度的要求是大于等于8位,接着执行,执行完4079DC的语句以后就会在右上窗口发现正确的注册码,那就说明4079DC处的CALL就是就算注册码的函数调用,接下来的一个循环就是真假两个注册码的比较,比较完后,根据比较结果决定是否执行 xor eax,eax 语句,从而设置EAX寄存器的值,下面我们跟进4079DC的CALL:
004078B3  |>MOV ESI,netscanp.004C207C   ;下面的字符串就是原始字串           
; ASCII "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
004078B8  |>LEA EBX,DWORD PTR SS:[EBP-C]
。。。。。。。。。。。。
004078E7  |>JGE SHORT netscanp.0040795C        ;下面的循环开始进行注册码的计算
004078E9  |>/PUSH EDI                         
004078EA  |>|MOV ECX,DWORD PTR SS:[EBP-10]  
004078ED  |>|MOVSX EAX,BYTE PTR DS:[ECX]   ;| EAX = F(i)
004078F0  |>|PUSH EAX                         ;下面的CALL含有浮点操作
004078F1  |>|CALL netscanp.0040760C             ; k = (int)((cos( F[i]*7.5+i*4.5)+1)*52)
。。。。。。。。。。。。。
00407933  |>||POP ESI               
00407934  |>||MOVSX EDX,BYTE PTR SS:[EBP+EAX-50]  ;将字符串表查出来的字符取出
00407939  |>||PUSH EDX                              ;EAX = k%0x3e; EBP是固定值
。。。。。。。。。。。。。
0040794D  |>| MOV EAX,DWORD PTR DS:[EBX]        ;EAX = k / 0x3e
0040794F  |>||TEST EAX,EAX                ;如果EAX = 0 则读取用户名的下一位 
00407951  |>|JNZ SHORT netscanp.004078FD   ;否则跳转到 4078FD 继续
00407953  |>|INC EDI                       ;EDX = EDX +1
00407954  |>|INC DWORD PTR SS:[EBP-10]
00407957  |>|CMP EDI,DWORD PTR SS:[EBP-4]
0040795A  |>JL SHORT netscanp.004078E9    ;跳转到4078E9 读取下一位用户名

代码分析:
跟进以后马上就发现了一个字符串,就会联想到很有可能使用的查表的方法计算出注册码,以后的分析证明了这个想法,分析代码的时候有个技巧:要特别注意代码中的循环,一般软件注册算法就是根据用户名的每一位计算出注册码,这样的话必定会用到循环。这个软件在4078ED处每次循环传人用户名的一位,计算出部分注册码然后组合起来就是整个注册码了,4078FE处函数用到了浮点命令,这对于初学者来说有一定的难度,在此就不具体分析了给出函数的作用,用F(i)表示用户名的第i位,函数返回的结果是: 
k = (int)((cos( F[i]*7.5+i*4.5)+1)*52);
从4078FD处开始根据上面的结果在字符串中的对应位置找出字符作为注册码。在上面的代码中有注释,有些用户名字符可能会计算好几位注册码。在00407934将字符串表查出来的字符取出,保存起来这样就组成了整个注册码。给大家一个测试用的用户名和注册码:
用户名:abcdefgh     注册码:11YA5PrJ1
后记:总的来说,这个软件注册算法不是很复杂,适合想从“暴破”专家转行到注册码方面的朋友练习,这算是单表查询类软件中较简单的了,就是有一个CALL使用了浮点指令,可能有一点点麻烦,不过我想既然大家都是搞这一行的就知道“破解不能怕麻烦”,破解本来就是麻烦的事。还有,搞破解不能只看不练,你练习的多了自然就有自己的技巧,相信你很快就能熟练的读懂大部分软件的注册算法从而写出注册机了。祝大家早日 成功!!! 

附8位用户名的注册机C语言源代码:
#include <math.h>
#include <stdio.h>
main()
{
  double m;
  int i,j=0,k,a,b,flag = 1;;
  char y[9],s[63]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    char sn[30];
  printf("Please Inpout UserName(8 byte):
");
  scanf("%s",y);
  for(i=0;i<8;i++)
  {
    m = y[i]*7.5+i*4.5;
    k = (int)((cos(m)+1)*52);
    while(flag==1 && k!=0)
    {
      a = k/0x3e;
      b = k%0x3e;
      sn[j++] = s[b];
      k = a;
      if(a == 0)    flag = 0;
    }
    flag = 1;
  }
  sn[j] = NULL;
  printf("User Name: %s
Your SN  : %s
",y,sn);
}