【原创】Turbo Photo的破解及不完全算法分析
Turbo Photo是一个以数码影像为背景,面向数码相机普通用户和准专业用户而设计的一套集图片管理,浏览,处理,输出为一身的软件系统。她包括两个部分:Turbo Photo 相册和Turbo Photo 编辑器。

【作者】WindayJiang
【破解声明】纯粹学习
【破解工具】WDSM,OLLDBG, eXeScopE,  PEID,
【破解难度】EASY
【软件保护】SN+TIME
【软件下载】http://www.stepok.com/chs/index.htm

运行软件,程序要关闭那一刻才会弹出注册框,错误注册会有提示 

开工!先查壳吧,VC编的,没有加壳。

用OD加载,CTRL+N查看一下,找不到GETWINDOWTEXTA之类的常规函数,那么就试一下内存的吧, 调出注册窗口,输入12345-54321-888888-98765,按解锁,这时提示序列号错误,不要关闭它,回到OD,在DUMP里CTRL+B,查找HEX31 32 33 34 35, 如下:
004BB52C  31 32 33 34 35 00 35 34 33 32 31 00 38 38 38 38  12345.54321.8888
004BB53C  38 38 00 39 38 37 36 35 00 00 00 00 80 22 1D 40  88.98765......" @

在004BB52C下硬件断点,再次解锁就会来到这里:
00453430  |.  8BC8          MOV ECX,EAX
00453432  |.  33C0          XOR EAX,EAX
00453434  |.  83E1 03       AND ECX,3
00453437  |.  F3:A4         REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
.
CTRL+F9返回后来到这里:
00452EA3   .  B9 7CB14B00   MOV ECX,TPhoto.004BB17C
00452EA8   .  E8 53050000   CALL TPhoto.00453400       //注册码的比较CALL
00452EAD   .  85C0          TEST EAX,EAX         //判断EAX
00452EAF   .  75 1B         JNZ SHORT TPhoto.00452ECC   //跳就成功注册了,不跳就提示错误
00452EB1   .  50            PUSH EAX

于是修改452EAF为JMP SHORT TPhoto.00452ECC,但你很快就会发现这是不治本的,程序启动会再判断是否注册,于是点击452EAF右键,选FIND REFERENCES TO CALL DESTINATION,发现有2个地方调用,另一个就是4535CC,在此下断,重运行程序,断下:
004535CC  |.  E8 2FFEFFFF   CALL TPhoto.00453400
004535D1  \.  C3            RETN             //返回4533A5
……………..
004533A5  |.  F7D8          NEG EAX
004533A7  |.  5F            POP EDI         ;  TPhoto.004BBC58
004533A8  |.  5E            POP ESI
004533A9  |.  1BC0          SBB EAX,EAX
004533AB  |.  5D            POP EBP
004533AC  |.  F7D8          NEG EAX
004533AE  |.  5B            POP EBX
004533AF  |.  59            POP ECX           //上面是对EAX处理
004533B0  \.  C3            RETN            //返回453A37
………………..
00453A37  |.  8986 64030000 MOV DWORD PTR DS:[ESI+364],EAX    //将EAX写入内存标志位
00453A3D  |.  C786 68030000>MOV DWORD PTR DS:[ESI+368],1    
00453A47  |.  5E            POP ESI
00453A48  \.  C3            RETN              

由上2处判断可知只要令标志返回1就OK了,下面我们看看注册码的比较CALL:
00453400  /$ >PUSH EBX                                          
00453401  |. >MOV EBX,ECX
00453403  |. >PUSH EBP               //"C:\WINNT\system32\tpflag.rg,注册信息文件
00453404  |. >MOV EBP,DWORD PTR SS:[ESP+C]
00453408  |. >LEA EDX,DWORD PTR DS:[EBX+3B0]    //第一段注册码地址放到EDX  /12345
0045340E  |. >PUSH ESI
0045340F  |. >CMP EDX,EBP
00453411  |. >PUSH EDI
00453412  |. >JE TPhoto.004534BE          //这里会跳
……. 
004534BE  |>>MOV EDI,TPhoto.004B795C       ;  ASCII "DDGTM"  //字串DDGTM到EDI
004534C3  |.>MOV ESI,EDX                    //第一段注册码到ESI  12345
004534C5  |>>/MOV CL,BYTE PTR DS:[ESI]             //第1个注册码到CL    “1”
004534C7  |.>|MOV DL,BYTE PTR DS:[EDI]             //“D”
004534C9  |.>|MOV AL,CL                     //第1个注册码到AL
004534CB  |.>|CMP CL,DL                     //第1个注册是不是“D”
004534CD  |.>|JNZ SHORT TPhoto.004534ED             //不是跳走
004534CF  |.>|TEST AL,AL                     //AL是不是0
004534D1  |.>|JE SHORT TPhoto.004534E9                //0就跳到4534E9
004534D3  |.>|MOV DL,BYTE PTR DS:[ESI+1]            //第2个注册码到CL    “2”
004534D6  |.>|MOV CL,BYTE PTR DS:[EDI+1]            //第二个“D”
004534D9  |.>|MOV AL,DL
004534DB  |.>|CMP DL,CL            
004534DD  |.>|JNZ SHORT TPhoto.004534ED            //不是跳走
004534DF  |.>|ADD ESI,2
004534E2  |.>|ADD EDI,2
004534E5  |.>|TEST AL,AL
004534E7  |.>\JNZ SHORT TPhoto.004534C5            //向上循环
004534E9  |>>XOR EAX,EAX                   //EAX清0
004534EB  |.>JMP SHORT TPhoto.004534F2            //跳到4534F2
004534ED  |>>SBB EAX,EAX                  //借位减
004534EF  |.>SBB EAX,-1
004534F2  |>>TEST EAX,EAX        
004534F4  |.>JNZ SHORT TPhoto.004534FD             //如果不跳标志值就不能设为1
004534F6  |.>POP EDI
004534F7  |.>POP ESI
004534F8  |.>POP EBP
004534F9  |.>POP EBX
004534FA  |.>RETN 10
………………
004534FD  |> \8B>MOV EAX,DWORD PTR SS:[ESP+20]       //第4段注册码
00453501  |.  8B>MOV ECX,DWORD PTR SS:[ESP+1C]       //第3段注册码
00453505  |.  8B>MOV EDX,DWORD PTR SS:[ESP+18]       //第2段注册码
00453509  |.  50 PUSH EAX                   //第4段注册码入栈
0045350A  |.  51 PUSH ECX                   //第3段注册码入栈
0045350B  |.  52 PUSH EDX                  //第2段注册码入栈
0045350C  |.  55 PUSH EBP                  //第1段注册码入栈
0045350D  |.  8D>LEA ECX,DWORD PTR DS:[EBX+204]                     ; |
00453513  |.  E8>CALL TPhoto.0048E210            //看来这个最终的比较CALL了
00453518  |. >TEST EAX,EAX                 //标志是否为0
0045351A  |. >JNZ SHORT TPhoto.00453523           //0的话就是注册不成功了
……………………
00453523  |> >LEA EAX,DWORD PTR SS:[ESP+20]
00453527  |. >PUSH EAX
00453528  |. >CALL <JMP.&MFC42.#3811>
0045352D  |. >PUSH 0
0045352F  |. >MOV ECX,EAX
00453531  |. >CALL <JMP.&MFC42.#3337>
00453536  |. >MOV ECX,DWORD PTR DS:[EAX+14]
00453539  |. >ADD ECX,76C
0045353F  |. >CMP ECX,7D5                 //是否2005年?
00453545  |. >JLE SHORT TPhoto.0045358F
…………..
0045358F  |> >MOV EAX,1               //终于看到这个了,等得不就是这个嘛,呵呵
00453594  |. >POP EDI
00453595  |. >POP ESI
00453596  |. >MOV DWORD PTR DS:[EBX+368],EAX
0045359C  |. >POP EBP
0045359D  |. >POP EBX
0045359E  \. >RETN 10

篇幅有限,哪些JUMP该修改我就不说了,改完后当你再次运行程序会提示:注册号是非法的窗口,需要再次解锁,拿出EXESCOPE,找到该对话框274, 换算HEX是112, 再来WDASM看看:
* Possible Reference to Dialog: DialogID_0112 
:00453B31 6812010000              push 00000112      //到OD设断这里
:00453B36 89742418                mov dword ptr [esp+18], esi
CTRL+K VIEW CALL TREE,看看是从什么地方调用的(这里要看2次,篇幅问题我只能省略了),来到这:
0042E79F   .  E8 3C4E0200   CALL TPhoto.004535E0
0042E7A4   .  3BC5          CMP EAX,EBP
0042E7A6   .  A1 E0B44B00   MOV EAX,DWORD PTR DS:[4BB4E0]/很面熟吧,在注册码比较那见过
0042E7AB   .  7F 4D         JG SHORT TPhoto.0042E7FA  //必须跳否则说非法
……………. 
0042E7FA   > \3BC5          CMP EAX,EBP        
0042E7FC   .  75 68         JNZ SHORT TPhoto.0042E866  //必须跳,否则是试用版
……………

修改一下,一个非常好用的软件就此告破!

总结一下:很长时间没写破文了,自己感觉也很累,比破的时间还要长:P, 破的时候没有用其他一些可能更有效更快捷的断点,而是用几个软件交叉配合,让刚学破解的NEWBIES熟练用用软件,写得不好还望大家多多原谅了。另外我没有时间再跟进注册算法,TPhoto.0048E210这个CALL估计就是算法的关键CALL,有兴趣的朋友可以再去看看。还有就是TurboPhotoAlbum.exe是相册,同样需要破解的,但跟上面思路一样,我就懒得写了:P

////////////////////////////////////////////////////////////////
If you want to crack well, learn ASM well !

WiNDaYJiANg       2005-12-18
////////////////////////////////////////////////////////////////




  • 标 题: 贴一段老版本(3.5)的注册算法给你参考一下
  • 作 者:ForEver
  • 时 间:2005-12-18 18:37

序列号格式:XXXXX-XXXXXX-XXXXX-XXXXX

我们假设输入的四组序列号分别用s1[6],s2[6],s3[7],s4[6]来表示。
其中s1[0]代表第一组序列号的第一个字符。其余类推。


loc46B2EE:  mov  dword ptr [ebx],     offset loc_46A9A0 ;这些地址其实就是验证注册码
    mov  dword ptr [ebx+4],   offset loc_46A910 ;的CALL,软件作者根据计算机
    mov  dword ptr [ebx+8],   offset sub_46A890 ;用户名选择一部分来验证。如
    mov  dword ptr [ebx+0Ch], offset loc_46A980 ;果只跟踪用到的CALL来做注册
    mov  dword ptr [ebx+10h], offset loc_46A810 ;机是不能通用的。这大概也是
    mov  dword ptr [ebx+14h], offset sub_46AC80 ;软件作者对破解的防范。
    mov  dword ptr [ebx+18h], offset sub_46AD40 ;
    mov  dword ptr [ebx+1Ch], offset sub_46AE00 ;下面我们一一分析这些CALL。
    mov  dword ptr [ebx+20h], offset loc_46AEB0 ;
    mov  dword ptr [ebx+24h], offset sub_46AF40 ;
    mov  dword ptr [ebx+28h], offset loc_46AB80 ;
    mov  dword ptr [ebx+2Ch], offset loc_46AB20 ;
    mov  dword ptr [ebx+30h], offset loc_46AAC0 ;
    mov  dword ptr [ebx+34h], offset loc_46ABE0 ;
    mov  dword ptr [ebx+38h], offset loc_46AA60 ;
    mov  dword ptr [ebx+3Ch], offset loc_46AC40 ;
;///////////////////////////////////////////////////////////////

第一个CALL: s1[4] = 4Dh ('M')

;-------------------------------------------------
loc_46A9A0:        ; DATA XREF: sub_46B200+EEo
    mov  eax, [esp+4]      ;第一组序列号
    push  ebx
    push  esi
    push  edi
    mov  bl, [eax+4]       ;指向第五个字符



loc_46AA45:        ; CODE XREF: .text:0046AA2Aj
    mov  edx, dword_490278 ;这个全局变量里是 1 ,追一下可以看到
    xor  ecx, ecx
    movsx  eax, bl
    add  edx, 4Ch
    pop  edi
    cmp  eax, edx          ;不等于 4Dh 失败
    pop  esi
    setz  cl
    mov  eax, ecx
    pop  ebx
    retn
;///////////////////////////////////////////////////////////////

第二个CALL: s1[2]>=43h && s1[2]<=4Ch

;---------------------------------------------------------------
loc_46A910:        ; DATA XREF: sub_46B200+F4o

loc_46A961:        ; CODE XREF: .text:0046A94Aj
    mov  edx, [esp+4]    ;第一组序列号
    mov  al, [edx+2]     ;指向第三个字符
    cmp  al, 43h         ;小于43h失败
    jl  short loc_46A976
    cmp  al, 4Ch         ;大于4Ch失败
    jg  short loc_46A976

unknown_libname_47:
    mov  eax, 1
    retn
; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?

loc_46A976:        ; CODE XREF: .text:0046A93Bj
          ; .text:0046A93Fj .text:0046A955j
          ; .text:0046A959j .text:0046A96Aj
          ; .text:0046A96Ej
    xor  eax, eax
    retn
;///////////////////////////////////////////////////////////////

第三个CALL: s1[1]>=42h && s1[1]<=44

;---------------------------------------------------------------
sub_46A890  proc near    ; DATA XREF: sub_46B200+FBo

loc_46A8CB:        ; CODE XREF: sub_46A890+21j
    push  ecx
    lea  ecx, [esp+0Ch+var_8]
    mov  eax, esp
    mov  dword ptr [eax], 5
    call  sub_46B4B0
    test  eax, eax
    jz  short loc_46A8F2
    mov  ecx, [esp+8+arg_0]     ;第一组序列号
    xor  eax, eax
    cmp  byte ptr [ecx+1], 41h  ;第二个字符等于41h则失败
    setnl  al
    add  esp, 8
    retn
; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?

loc_46A8F2:        ; CODE XREF: sub_46A890+4Fj
    mov  edx, [esp+8+arg_0]
    mov  al, [edx+1]
    cmp  al, 41h               ;小于41h则失败
    jl  short loc_46A90A
    cmp  al, 45h               ;大于等于45h则失败
    jge  short loc_46A90A
    mov  eax, 1
    add  esp, 8
    retn
; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?

loc_46A90A:        ; CODE XREF: sub_46A890+2Cj
          ; sub_46A890+30j sub_46A890+6Bj
          ; sub_46A890+6Fj
    xor  eax, eax
    add  esp, 8
    retn
sub_46A890  endp
;///////////////////////////////////////////////////////////////

第四个CALL:s1[3]>=42 && s1[3]<=79

;---------------------------------------------------------------
loc_46A980:        ; DATA XREF: sub_46B200+102o
    mov  eax, [esp+4]    ;第一组序列号
    mov  al, [eax+3]     ;第四个字符
    cmp  al, 41h         ;小于41h失败
    jl  short loc_46A995
    cmp  al, 7Ah         ;大于等于7ah失败
    jge  short loc_46A995

unknown_libname_48:
    mov  eax, 1
    retn
; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?

loc_46A995:        ; CODE XREF: .text:0046A989j
          ; .text:0046A98Dj
    xor  eax, eax
    retn
;///////////////////////////////////////////////////////////////

第五个CALL:s1[0] = 44h ('D')

;---------------------------------------------------------------
loc_46A810:        ; DATA XREF: sub_46B200+109o
    


loc_46A866:        ; CODE XREF: .text:0046A843j
          ; DATA XREF: .text:0046A880o
    mov  eax, [esp+4]  ;第一组序列号
    mov  al, [eax]       ;第一个字符
    cmp  al, 44h         ;小于44h失败
    jl  short loc_46A874
    cmp  al, 45h         ;大于等于45h失败

loc_46A872:        ; CODE XREF: .text:0046A856j
          ; .text:0046A864j
    jl  short unknown_libname_43 ; default

loc_46A874:        ; CODE XREF: .text:0046A852j
          ; .text:0046A860j .text:0046A86Ej
    xor  eax, eax
    retn
; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?

unknown_libname_43:      ; CODE XREF: .text:0046A841j
          ; .text:0046A843j .text:0046A872j
          ; DATA XREF: .text:0046A880o
    mov  eax, 1    ; default
    retn        
;///////////////////////////////////////////////////////////////

第六个CALL: s4[0] = (s1[4]%7 + s1[2]%5 + s1[1]*2 + s1[0]) % 1Ah + 41h

;---------------------------------------------------------------
sub_46AC80  proc near    ; DATA XREF: sub_46B200+110o

    sub  esp, 8
    lea  eax, [esp+8+var_4]
    push  eax
    call  ?GetTickCount@CTime@@SG?AV1@XZ ; CTime::GetTickCount(void)
    mov  ecx, [eax]
    push  0
    mov  [esp+0Ch+var_8], ecx
    lea  ecx, [esp+0Ch+var_8]
    call  ?GetLocalTm@CTime@@QBEPAUtm@@PAU2@@Z ; CTime::GetLocalTm(tm *)
    mov  eax, [eax+4]
    cmp  eax, 2Dh
    jle  short loc_46ACF9
    mov  ecx, [esp+8+arg_0]    
    push  esi
    mov  esi, 7
    push  edi
    movsx  eax, byte ptr [ecx+4] ;第一组序列号第五个字符
    cdq
    idiv  esi                   
    movsx  eax, byte ptr [ecx+2] ;第一组序列号第三个字符
    mov  edi, 5                
    mov  esi, edx              
    cdq
    idiv  edi
    pop  edi
    add  esi, edx              
    movsx  edx, byte ptr [ecx+1] ;第一组序列号第二个字符
    movsx  ecx, byte ptr [ecx]   ;第一组序列号第一个字符
    lea  eax, [esi+edx*2]      
    pop  esi
    add  eax, ecx             ;eax = s1[4]%7 + s1[2]%5 + s1[1]*2 + s1[0]
    mov  ecx, 1Ah             
    cdq
    idiv  ecx
    mov  eax, [esp+8+arg_C]   
    movsx  ecx, byte ptr [eax]  ;s4[0]
    xor  eax, eax
    add  dl, 41h              ;eax % 1ah +41
    and  edx, 0FFh
    cmp  ecx, edx
    setz  al
    add  esp, 8
    retn

sub_46AC80  endp
;///////////////////////////////////////////////////////////////

第七个CALL: s4[1] = (s2[3]%3 + s2[4]*5 + s2[0]*2 + s2[1] + s2[2]) % 1Ah + 41h

;---------------------------------------------------------------
sub_46AD40  proc near    ; DATA XREF: sub_46B200+117o
    sub  esp, 8
    lea  eax, [esp+8+var_4]
    push  eax
    call  ?GetTickCount@CTime@@SG?AV1@XZ ; CTime::GetTickCount(void)
    mov  ecx, [eax]
    push  0
    mov  [esp+0Ch+var_8], ecx
    lea  ecx, [esp+0Ch+var_8]
    call  ?GetLocalTm@CTime@@QBEPAUtm@@PAU2@@Z ; CTime::GetLocalTm(tm *)
    mov  eax, [eax+18h]
    inc  eax
    cmp  eax, 5
    jle  short loc_46ADB8
    mov  ecx, [esp+8+arg_4]   ;第二组序列号
    push  esi
    mov  esi, 3
    movsx  eax, byte ptr [ecx+3] ;s2[3]
    cdq
    idiv  esi
    movsx  eax, byte ptr [ecx+4] ;s2[4]
    pop  esi
    lea  eax, [eax+eax*4]
    add  edx, eax
    movsx  eax, byte ptr [ecx]   ;s2[0]
    lea  eax, [edx+eax*2]
    movsx  edx, byte ptr [ecx+2] ;s2[2]
    movsx  ecx, byte ptr [ecx+1] ;s2[1]
    add  eax, edx
    add  eax, ecx
    mov  ecx, 1Ah
    cdq
    idiv  ecx
    mov  eax, [esp+8+arg_C]    ;第四组序列号
    movsx  ecx, byte ptr [eax+1] ;s4[1]
    xor  eax, eax
    add  dl, 41h
    and  edx, 0FFh
    cmp  ecx, edx
    setz  al
    add  esp, 8
    retn
sub_46AD40  endp
;///////////////////////////////////////////////////////////////

第八个CALL: s4[2] = (s3[3]*23 + s3[5] + s3[0])% 1Ah + 41h

;---------------------------------------------------------------
sub_46AE00  proc near    ; DATA XREF: sub_46B200+11Eo
    sub  esp, 8
    lea  eax, [esp+8+var_4]
    push  eax
    call  ?GetTickCount@CTime@@SG?AV1@XZ ; CTime::GetTickCount(void)
    mov  ecx, [eax]
    push  0
    mov  [esp+0Ch+var_8], ecx
    lea  ecx, [esp+0Ch+var_8]
    call  ?GetLocalTm@CTime@@QBEPAUtm@@PAU2@@Z ; CTime::GetLocalTm(tm *)
    mov  eax, [eax]
    and  eax, 80000007h
    jns  short loc_46AE2C
    dec  eax
    or  eax, 0FFFFFFF8h
    inc  eax

loc_46AE2C:        ; CODE XREF: sub_46AE00+25j
    cmp  eax, 5
    jle  short loc_46AE70
    mov  edx, [esp+8+arg_8]       ;第三组序列号
    movsx  ecx, byte ptr [edx+3]    ;s[3]
    lea  eax, [ecx+ecx*2]
    shl  eax, 3
    sub  eax, ecx
    movsx  ecx, byte ptr [edx+5]    ;s[5]
    movsx  edx, byte ptr [edx]      ;s[0]
    add  eax, ecx
    mov  ecx, 1Ah
    add  eax, edx
    cdq
    idiv  ecx
    mov  eax, [esp+8+arg_C]    ;第四组序列号
    movsx  ecx, byte ptr [eax+2] ;s4[2]
    xor  eax, eax
    add  dl, 41h
    and  edx, 0FFh
    cmp  ecx, edx
    setz  al
    add  esp, 8
    retn
sub_46AE00  endp
;///////////////////////////////////////////////////////////////

第九个CALL: s4[3] = (s3[1]*7 + s3[2] + s2[2] + s1[3]) % 1Ah + 41h
        
;---------------------------------------------------------------
loc_46AEB0:        ; DATA XREF: sub_46B200+125o
    call  sub_46A690
    cmp  eax, 5
    jle  short loc_46AF06
    mov  edx, [esp+0Ch]        ;第三组序列号
    movsx  ecx, byte ptr [edx+1] ;s3[1]
    movsx  edx, byte ptr [edx+2] ;s3[2]
    lea  eax, ds:0[ecx*8]
    sub  eax, ecx
    mov  ecx, [esp+4]          ;第一组序列号
    movsx  ecx, byte ptr [ecx+3] ;s1[3]
    add  eax, ecx
    mov  ecx, [esp+8]          ;第二组序列号
    movsx  ecx, byte ptr [ecx+2] ;s2[2]
    add  eax, ecx
    mov  ecx, 1Ah
    add  eax, edx
    cdq
    idiv  ecx
    mov  eax, [esp+10h]        ;第四组序列号
    movsx  ecx, byte ptr [eax+3] ;s4[3]
    xor  eax, eax
    add  dl, 41h
    and  edx, 0FFh
    cmp  ecx, edx
    setz  al
    retn
;///////////////////////////////////////////////////////////////

第十个CALL: 返回 1 ,即成功标志
        
;---------------------------------------------------------------
  
;///////////////////////////////////////////////////////////////

第十一个CALL: s3[3]>=49h && s3[3]<=52h
        
;---------------------------------------------------------------
loc_46AB80:        ; DATA XREF: sub_46B200+133o
    call  sub_46A690
    cmp  eax, 8
    jle  short loc_46AB9F
    mov  eax, [esp+0Ch]        ;第三组序列号
    mov  al, [eax+3]           ;s3[3]
    cmp  al, 49h
    jl  short unknown_libname_63
    cmp  al, 52h
    jg  short unknown_libname_63

unknown_libname_60:
    mov  eax, 1
    retn  
;///////////////////////////////////////////////////////////////

第十二个CALL: s3[2]>=42h && s3[2]<=4Bh
        
;---------------------------------------------------------------    
loc_46AB20:        ; DATA XREF: sub_46B200+13Ao
    call  sub_46A690
    cmp  eax, 8
    jle  short loc_46AB3F
    mov  eax, [esp+0Ch]          ;第三组序列号
    mov  al, [eax+2]             ;s3[2]
    cmp  al, 42h
    jl  short unknown_libname_59
    cmp  al, 4Bh
    jg  short unknown_libname_59

unknown_libname_56:
    mov  eax, 1
    retn
;///////////////////////////////////////////////////////////////

第十三个CALL: s3[1]>=48h && s3[1]<=51h
        
;---------------------------------------------------------------  
loc_46AAC0:        ; DATA XREF: sub_46B200+141o
    call  sub_46A690
    cmp  eax, 8
    jle  short loc_46AADF
    mov  eax, [esp+0Ch]
    mov  al, [eax+1]
    cmp  al, 48h
    jl  short unknown_libname_55
    cmp  al, 51h
    jg  short unknown_libname_55

unknown_libname_52:
    mov  eax, 1
    retn  
;///////////////////////////////////////////////////////////////

第十四个CALL: s3[4]>=4bh && s3[4]<=54
        
;---------------------------------------------------------------  
loc_46ABE0:        ; DATA XREF: sub_46B200+148o
    call  sub_46A690
    cmp  eax, 5
    jle  short loc_46ABFF
    mov  eax, [esp+0Ch]           ;第三组序列号
    mov  al, [eax+4]              ;s3[4]
    cmp  al, 4Bh
    jl  short unknown_libname_67
    cmp  al, 54h
    jg  short unknown_libname_67

unknown_libname_64:
    mov  eax, 1
    retn
;///////////////////////////////////////////////////////////////

第十五个CALL: s3[0] = 47h ('G')
        
;---------------------------------------------------------------   
 
 loc_46AA60:        ; DATA XREF: sub_46B200+14Fo
    call  sub_46A690
    cmp  eax, 8
    jle  short loc_46AA77
    mov  ecx, [esp+0Ch]           ;第三组序列号
    xor  eax, eax
    cmp  byte ptr [ecx],  47h      ;s[0] == 47h
    setz  al
    retn
;///////////////////////////////////////////////////////////////

第十六个CALL: s3[5]>=4Ah && s3[5]<=53h
        
;---------------------------------------------------------------   
loc_46AC40:        ; DATA XREF: sub_46B200+156o
    call  sub_46A690
    cmp  eax, 8
    jle  short loc_46AC5F
    mov  eax, [esp+0Ch]           ;第三组序列号
    mov  al, [eax+5]              ;s3[5]
    cmp  al, 4Ah
    jl  short loc_46AC74
    cmp  al, 53h
    jg  short loc_46AC74

unknown_libname_68:
    mov  eax, 1
    retn    
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

总结一下:
   把所有的条件合并起来,就是生成注册码的方法了。为了省空间和容易阅读,我省去
了一部分代码,而取判断的最小集。软件作者在验证注册码时采用随机验证的方法。因此
不正确的注册码可能当时并不提示。而在过一段时间才提示的。
   这个软件的注册方法很值得借鉴,只是验证部分的CALL过于集中,容易给人发现。如
果把验证的CALL分散开,必然会大大增加破解的难度。

   软件在注册后的启动画面上仍然是'Trial version',这可能是软件的Bug.软件靠一个
全局标志判断是否出现‘Trial version’或‘Registered to’。但却没有初始化这个
全局标志。

   下面的这些算法放在一起就可以做注册机了:
   
        s1[0] = 44h ('D')
        s1[1]>=42h && s1[1]<=44
        s1[2]>=43h && s1[2]<=4Ch
        s1[3]>=42 && s1[3]<=79
        s1[4] = 4Dh ('M')
        
        s4[0] = (s1[4]%7 + s1[2]%5 + s1[1]*2 + s1[0]) % 1Ah + 41h
        s4[1] = (s2[3]%3 + s2[4]*5 + s2[0]*2 + s2[1] + s2[2]) % 1Ah + 41h
        s4[2] = (s3[3]*23 + s3[5] + s3[0])% 1Ah + 41h
        s4[3] = (s3[1]*7 + s3[2] + s2[2] + s1[3]) % 1Ah + 41h
        
        s3[0] = 47h ('G')
        s3[1]>=48h && s3[1]<=51h
        s3[2]>=42h && s3[2]<=4Bh
        s3[3]>=49h && s3[3]<=52h
        s3[4]>=4bh && s3[4]<=54
        s3[5]>=4Ah && s3[5]<=53h