• 标 题:DataFit V7.0.36注册过程的分析 (9千字)
  • 作 者:robot
  • 时 间:2001-11-9 21:42:28
  • 链 接:http://bbs.pediy.com

软件简介:DataFit是一个科学与工程工具,用来对数据进行绘图、回归分析和统计分析。软件已内置上百个数学模型,并可由用户自己设定数学模型,对于初学者和专家来说,该软件都是一个高效率的易于使用的曲线拟合和分析的工具。(本人在10多年前上大学时就对曲线拟合情有独钟,使用了一下该软件,对它的曲线拟合真是印象很深,简单说就是傻瓜也能把一组复杂的数据拟合出很好的结果——尽管有的曲线拟合得非常好,但又让我觉得拟合的非常没有道理)
下载地址:http://www.oakdaleengr.com/download.htm
分析工具:IDA Pro 4.15,SmartCheck 6.60
前言:要想获得这个软件的注册码相对来说,需要费一番功夫的。去年曾经用SoftICE和SmartCheck追过,追了半天追不出来,最后只好采用暴破的形式使之成为注册版,很丑。今年经过对几个VB程序的注册码的分析,信心大增,于是又开始跟这个软件搏斗,不过还好,终于解决了。这个软件的注册码的生成确实经过该公司软件设计人员的考虑,因此虽然最后仍然是通过STRCMP这个函数进行比较的,但由于是通过对注册码进行变换之后的比较,因此通过中断STRCMP获得字符串来进行注册肯定是不行的(即非明码比较),只有了解了该软件注册码的计算过程,才能够获得注册码,其中的关键就是注册码的变换过程。不过由于有IDA Pro和SmartCheck这两个利器,同时该软件并没有采取不可逆算法,这个程序注册码的计算还是分析出来的。

分析过程:启动SmartCheck,选择打开datafit.exe,运行后会显示评估版(EVALUATION COPY)对话框,按“继续”按钮后进入主程序。选择Help菜单下的Enter license命令,会弹出Enter license对话框,让你输入许可号,经过分析知道,这里最好输入16个字母,大小写无关,如:ABCDEFGHIJKLMNOP(为什么输入这样的字符串,摸索出来的嘛,一般来说,分析注册过程要避免输入这种字符串,但这个软件是个例外,输入这种字符串后,可以很清楚地知道字符串变换后各自的位置,这样可以在SmartCheck中就可以清楚看出字符串的变换过程,减少在IDA中通过观察反汇编结果来分析程序的过程),输入后软件直接退出该对话框,不会显示什么“注册码不正确”之类的容易被人利用的信息,不过没关系。退出DataFit,软件又会显示评估版对话框,单击“Exit Datafit”按钮退出。现在,SmartCheck已经把整个运行Datafit的过程忠实地记录下来了,结合IDA分析吧。

在SmartCheck中找到Parentfrm_Unload,移动鼠标向下不远就是注册码的变换过程了。

流程:Parentfrm_Unload
:007DABF3          call    sub_51E9C0  ;退出是保存设定
:007DABF8          cmp    word_952108, bx
:007DABFF          jz      short loc_7DAC29
:007DAC01          call    sub_56E5C0  ;注册码的变换及比较过程,call见下面
:007DAC06          mov    eax, dword_952104
:007DAC0B          mov    ecx, dword_952100
:007DAC11          push    eax
:007DAC12          push    ecx
:007DAC13          call    ds:__vbaStrCmp  ;字符串比较

注册码的变换及比较过程
:0056E5C0          push    esi
:0056E5C1          mov    esi, ds:__vbaStrCopy
:0056E5C7          mov    edx, offset dword_455228  ;值为1ah
:0056E5CC          mov    ecx, offset dword_952100
:0056E5D1          call    esi ; __vbaStrCopy
:0056E5D3          mov    edx, offset dword_455228
:0056E5D8          mov    ecx, offset dword_952104
:0056E5DD          call    esi ; __vbaStrCopy
:0056E5DF          mov    eax, dword_9520FC
:0056E5E4          push    eax
:0056E5E5          call    sub_94B400  ;注册码的变换过程
:0056E5EA          mov    esi, ds:__vbaStrMove
:0056E5F0          mov    edx, eax
:0056E5F2          mov    ecx, offset dword_952104
:0056E5F7          call    esi ; __vbaStrMove
:0056E5F9          push    offset dword_952104
:0056E5FE          call    sub_5A0E90  ;注册码的比较过程
:0056E603          mov    edx, eax
:0056E605          mov    ecx, offset dword_952100
:0056E60A          call    esi ; __vbaStrMove
:0056E60C          pop    esi
:0056E60D          retn

注册码的变换过程:
:0094B463          call    sub_94A600  ;获得注册码字符串的长度并用Mid函数检测字符
:0094B475          mov    edx, [ebp+var_20]
:0094B478          push    edx
:0094B479          call    sub_94A810  ;变换1
:0094B496          mov    eax, [ebp+var_20]
:0094B499          push    eax
:0094B49A          call    sub_94AD90  ;变换2
:0094B4B2          mov    edx, [ebp+var_20]
:0094B4B5          push    edx
:0094B4B6          call    sub_94AA70  ;变换3
:0094B4C2          mov    eax, [ebp+var_20]
:0094B4C5          push    eax
:0094B4C6          call    sub_94AF30  ;变换4
:0094B4D2          mov    ecx, [ebp+var_20]
:0094B4D5          push    ecx
:0094B4D6          call    sub_94A810  ;变换5

注册码的变换过程:
变换1:通过Mid函数取字符,先取偶数位字符放到前面,然后奇数位字符放到后面
      结果:ABCDEFGHIJKLMNOP ====> BDFHJLNPACEGIKMO

变换2:字符串左边3个放到最后,然后转为小写
      结果:BDFHJLNPACEGIKMO ====> hjlnpacegikmobdf

变换3:用Mid函数每次取1个字符,令该字符的ASCII值-61h,如果该值大于19h,则结果为0h,否则用下表变换:
新字符:abcdefghijklmnopqrstuvwxyz 或 新字符:lesiaxkcpgnmvrzfbhjuqdoywt
原字符:eqhvbpjrdsgalkwiuncztmyfxo    原字符:abcdefghijklmnopqrstuvwxyz
      结果:hjlnpacegikmobdf ====> cgmrflsakpnvzeix

变换4:用Mid函数每次取1个字符,令该字符的ASCII值-0ch,如果结果不小于61h则不变,如果小于则加上1ah
      结果:cgmrflsakpnvzeix ====> quaftzgoydbjnswl

变换5:同变换1
      结果:quaftzgoydbjnswl ====> ufzodjslqatgybnw
经过上面5步变换后的字符串就是要比较的字符串

比较过程:call 5A0E90
:005A0F9E          mov    [ebp+var_64], 0FFFFFFFFh  ;-1
:005A0FB1          call    ds:rtcRandomNext          ;Rnd(-1)
:005A0FC4          mov    word ptr [ebp+var_64], 0Ah ;10
:005A0FD1          call    ds:rtcRandomize            ;Randomize(10)
:005A0FEC          mov    eax, 12Ch                  ;300
:005A0FF1          cmp    word ptr [ebp+var_2C], ax  ;是否进行了300次
:005A0FF5          jg      loc_5A1126                ;是则转
:005A1009          mov    esi, 1
:005A100E          mov    eax, 10h                  ;16
:005A1013          cmp    si, ax                    ;是否进行了16次
:005A1016          jg      loc_5A10F4                ;是则转而比较变换字符串与新“随机”生成的是否相同
:005A102E          call    ds:rtcRandomNext          ;取随机数
:005A1034          fstp    [ebp+var_260]              ;放到[ebp+var_260]中
:005A103A          mov    eax, 7Ah
:005A103F          mov    cx, ax
:005A1042          mov    eax, 61h
:005A1047          sub    cx, ax
:005A104A          jo      loc_5A234B
:005A1050          add    cx, 1                      ;7ah-61h+1=1ah
:005A105A          movsx  edx, cx
:005A105D          mov    [ebp+var_288], edx
:005A1063          fild    [ebp+var_288]
:005A1069          fstp    [ebp+var_28C]
:005A106F          fld    [ebp+var_28C]
:005A1075          fmul    [ebp+var_260]              ;1ah*随机数
:005A107B          movsx  eax, ax                    ;eax=61h
:005A107E          mov    [ebp+var_290], eax
:005A1084          fild    [ebp+var_290]
:005A108A          fstp    [ebp+var_294]
:005A1090          fadd    [ebp+var_294]              ;61h+1ah*随机数
:005A10A0          call    ds:__vbaR8IntI2            ;取整
:005A10B9          call    ds:rtcBstrFromAnsi        ;根据ASCII值取相应的字符,即VB的chr函数
:005A10CF          lea    ecx, [ebp+var_3C]
:005A10D2          call    ebx ; __vbaStrMove        ;放到[ebp+var_3C]
:005A10DD          mov    eax, 1
:005A10E2          add    ax, si                    ;计数器加1
:005A10EB          mov    esi, eax
:005A10ED          xor    edi, edi
:005A10EF          jmp    loc_5A100E                ;取下一个字符
:005A10F4          mov    eax, [ebp+arg_0]
:005A10F7          mov    ecx, [eax]
:005A10F9          push    ecx
:005A10FA          mov    edx, [ebp+var_3C]
:005A10FD          push    edx
:005A10FE          call    ds:__vbaStrCmp            ;字符串比较
:005A1104          test    eax, eax
:005A1106          jz      short loc_5A111F          ;相等则转(注册码正确)
:005A1108          mov    eax, 1
:005A110D          add    ax, word ptr [ebp+var_2C]  ;计数器加1
:005A1117          mov    [ebp+var_2C], eax
:005A111A          jmp    loc_5A0FEC                ;取下一组(共300组)

上面的过程用VB表示就是:
Rnd(-1):Randomize(10)
for i=1 to 300:Rand_Code=""
    for j=1 to 16:Rand_Code=Rand_Code+Chr(int(&H61+Rnd*&H1A)):next j
    if Rand_Code="ufzodjslqatgybnw" then
        注册码正确:exit sub
    endif
next i

应该注意:上面虽然是随机生成的300个注册码,但由于固定了随机种子,因此300个注册码也是固定的,但不知不同机器的随机种子值是否相同,从我测试的来看,各机器间的随机种子值是相同的,因此注册码应是通用的。不过有5个肯定是通用的,原因见下面:
从5A1130-5A1EE4将变换字符串分别与下面5组固定字符串进行比较,如果相同则注册码正确:
(1) acllkakiojgoymjg (2) cpnikjlaepkvccfo (3)qhkxakuwdkzuaaol (4) lvzlpckxgecgeunm (5) mflkssoltlajgjmp
如果与上面的均不相同则生成下列字符串:"areaunder"(语句5A1F10-5A2074)并返回
如果注册码正确则[ebp+var_44]=-1,否则=0

注册码的反推:
设Regcode为最终参与比较的字符串(看上面,共305个),Regcode1为反推出的注册码
说明:注册码的长度为16且均是字母,这里反推出的注册码为小写的,实际输入时大小写无关。
步骤1:for i=1 to 8:Regcode1=Regcode1+mid(Regcode,i+8,1)+mid(Regcode,i,1)
步骤2:for i=1 to 16
if asc(mid(Regcode1,i,1)) <=&H6E then
  mid(Regcode,i,1)=chr(asc(mid(Regcode1,i,1))+&HC)
else
  mid(Regcode,i,1)=chr(asc(mid(Regcode1,i,1))+&HC-&H1A)
end if
步骤3:for i= to 16
  mid(Regcode1,i,1)=mid("eqhvbpjrdsgalkwiuncztmyfxo",(Asc(mid(Regcode,i,1))-&H60),1)
步骤4:Regcode=Right(Regcode1,3)+Left(Regcode1,13)
步骤5:Regcode1="":for i=1 to 8:Regcode1=Regcode1+mid(Regcode,i+8,1)+mid(Regcode,i,1)

注册器已做成,5个通用的注册码中的一个为:OFRETLWUYWWQMQNY,通过随机种子生成的300个注册码中的一个为:CKVVZSTQYVKKJXQB,如果大家下载了这个软件,请测试并报告一下后面的随机生成的注册码是否可以注册成功,如果成功则说明那300个随机生成的注册码也是通用的。

补充说明:我分析的这个版本是V7.0.36,目前该网站提供的版本是V7.1.44,我下载回来分析了一下,注册过程一点没变,上面的注册分析过程同样适用于新的版本。
另外,注册表中H.L.M\Software\Oakdale Engineering\DataFit\Version\desckey值表示的是软件第一次运行的时间,跟序列号无关,注册成功后,会建立一个License的键值,即正确的注册码字符串。