• 标 题:Soundnailsd的破解教程(一) (9千字)
  • 作 者:破解勇[CCG]
  • 时 间:2001-10-17 21:58:46
  • 链 接:http://bbs.pediy.com

Soundnailsd的破解教程(一)

    这个软件是peterchen大哥推荐的,其实早就搞定了,由于近来事比较多,还有一个
重要的原因,那就是俺比较的懒嘛,所以教程现在这才写好....嘿嘿  *^_^*
    下载地址:http://www.esoftware.com.cn/file/photo/animator/2001092101.shtml     

简介 :
    SWF 是 Macromedia Flash 的专属档案格式,它原是用来制作 Flash 动画时所产生的动画档;
而 MP3 是档案小、音极佳的多媒体音乐格式的代表。不过 MP3 并无法像 Flash 动画或串流多媒体格式,
直接在网络上播放,所以常常都只是透过 Internet 来传输档案,无法达到实时播放的功能,这是比较可惜的地方。
现在 Soundnails 能够以简单的步骤,将众多 MP3 档案轻易的批次转换为 SWF 档案,而且还能选择不同样板,输
出制式化的 HTML 网页,让网友直接在网页上播放音乐,这结合了 MP3 及 SWF 两者的优点,表现优异当然是可想而
知了。使用 Soundnails 的好处,是它并不需要先行安装 Macromedia Flash,而且要播放 SWF 档案也不需要安装其
它的音乐播放程序,只要选择想要转换的 MP3 档案,然后 step by step 的跟着做,就可以输出网页 HTML 组件,
然后再上传到 Web Server 中,网友就可以直接在网络上欣赏好听的音乐了。Soundnails 在制作 SWF 时,会自动抓出
MP3 的 ID3 Tag 信息,而呈现在所制作的 HTML 网页中 (或者也能在 Soundnails 中编辑 MP3 档案的 ID3 Tag);网
页上的播放按钮,内建有五组 Flash 动画可选择套用,而输出网页也有五套风格相异的网页样版可挑选。试用版有
30 天及一次处理只能 5 个 MP3 档案等功能限制。


  先用wdasm32静态分析一下看看,略去了很多无关的代码
有兴趣的可以自己看一看.
  代码如下:

:                                :
:                                :
:00457572 C605D8F0470000          mov byte ptr [0047F0D8], 00
:00457579 B890084800              mov eax, 00480890
:0045757E E801040000              call 00457984    --------->
:00457583 8BF0                    mov esi, eax
:00457585 8D45C4                  lea eax, dword ptr [ebp-3C]
:00457588 E8F7030000              call 00457984  ---------->与上面的是同一个CALL.是个关键CALL
:0045758D 8BF8                    mov edi, eax
:0045758F 3BF7                    cmp esi, edi
:00457591 0F8DA1000000            jnl 00457638
:00457597 8BC7                    mov eax, edi
:00457599 48                      dec eax
:0045759A 7405                    je 004575A1------>注意这个跳转和下面那个跳转,
                                                  这个跳转跳向 EASY Version                                               



:0045759C 48                      dec eax
:0045759D 741C                    je 004575BB--->这个跳转跳向 PRO Version
:0045759F EB34                    jmp 004575D5

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045759A(C)
|
:004575A1 6A00                    push 00000000

* Possible StringData Ref from Code Obj ->"Registration"
                                  |
:004575A3 B9A8764500              mov ecx, 004576A8

* Possible StringData Ref from Code Obj ->"EASY Version registered. Restart " --->* *一个名字对应两个注册码,
                                        ->"Program to activate Changes."            分别来对应 EASY Version 和下面
                                  |                                                的 PRO Version
                                  |
:004575A8 BAB8764500              mov edx, 004576B8
:004575AD A1E8F54700              mov eax, dword ptr [0047F5E8]
:004575B2 8B00                    mov eax, dword ptr [eax]
:004575B4 E8476FFFFF              call 0044E500
:004575B9 EB32                    jmp 004575ED

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045759D(C)
|
:004575BB 6A00                    push 00000000

* Possible StringData Ref from Code Obj ->"Registration"
                                  |
:004575BD B9A8764500              mov ecx, 004576A8

* Possible StringData Ref from Code Obj ->"PRO Version registered. Restart "--> * *
                                        ->"Program to activate Changes."
                                  |
:004575C2 BAF8764500              mov edx, 004576F8
:004575C7 A1E8F54700              mov eax, dword ptr [0047F5E8]
:004575CC 8B00                    mov eax, dword ptr [eax]
:004575CE E82D6FFFFF              call 0044E500
:004575D3 EB18                    jmp 004575ED
      :                            :
      :                            :(略去不少代码)

    大体分析一下上面的代码,可断定:0045757E  call 00457984 里面有注册码的算法及比较过程,那我们
就来动态跟踪分析一下这个call.用TRW2000.
  (无关的代码没有作说明)

  代码如下:
0167:00457984  PUSH    EBP
0167:00457985  MOV      EBP,ESP
0167:00457987  ADD      ESP,BYTE -1C
0167:0045798A  PUSH    EBX
0167:0045798B  PUSH    ESI
0167:0045798C  PUSH    EDI
0167:0045798D  XOR      EDX,EDX
0167:0045798F  MOV      [EBP-14],EDX
0167:00457992  MOV      [EBP-10],EDX
0167:00457995  MOV      [EBP-0C],EDX
0167:00457998  MOV      EBX,EAX
0167:0045799A  XOR      EAX,EAX
0167:0045799C  PUSH    EBP
0167:0045799D  PUSH    DWORD 00457B88
0167:004579A2  PUSH    DWORD [FS:EAX]
0167:004579A5  MOV      [FS:EAX],ESP
0167:004579A8  LEA      EAX,[EBX+24]
0167:004579AB  MOV      EDX,00457BA0
0167:004579B0  CALL    00403BE0
0167:004579B5  XOR      ESI,ESI
0167:004579B7  CMP      DWORD [EBX],BYTE +00  ----->拿指向用户名的指针与00比较
0167:004579BA  JZ      00457A1F              ----->为0则跳转
0167:004579BC  CMP      DWORD [EBX+04],BYTE +00---->拿指向填的注册码指针与00比较
0167:004579C0  JZ      00457A1F              ---->为0则跳转
0167:004579C2  MOV      ECX,[EBX+08]
0167:004579C5  MOV      EDX,[EBX]
0167:004579C7  LEA      EAX,[EBP-0C]
0167:004579CA  CALL    00403E58
0167:004579CF  MOV      EAX,[EBP-0C]
0167:004579D2  MOV      EDX,[EBX+04]
0167:004579D5  CALL    00457320  ------------>关键call
0167:004579DA  TEST    AL,AL      ------------->以AL为棋标
0167:004579DC  JZ      00457A1F
0167:004579DE  PUSH    DWORD 00457BB0  ------->00457BB0指向"EASY Version Registered for  "
0167:004579E3  PUSH    DWORD [EBX]    ------->指向用户名的指针.下命令 D *EBX就可看到
0167:004579E5  PUSH    DWORD 00457BD8
0167:004579EA  PUSH    DWORD 00457BE4
0167:004579EF  PUSH    DWORD 00457BF0  ------->00457BF0指向" Serial#: "
0167:004579F4  PUSH    DWORD [EBX+08]
0167:004579F7  LEA      EAX,[EBX+24]
0167:004579FA  MOV      EDX,06
0167:004579FF  CALL    00403ECC
0167:00457A04  MOV      ESI,01        ------>01送ESI. 这个值比较重要.
0167:00457A09  MOV      EAX,[0047F0E4]
0167:00457A0E  MOV      [0047F0DC],EAX
0167:00457A13  MOV      BYTE [0047F0D8],01
0167:00457A1A  JMP      00457B6D
0167:00457A1F  CMP      DWORD [EBX],BYTE +00 ------>拿指向用户名的指针与00比较
0167:00457A22  JZ      00457A87              ---->为0则跳转
0167:00457A24  CMP      DWORD [EBX+04],BYTE +00---->拿指向填的注册码指针与00比较
0167:00457A28  JZ      00457A87              ----->为0则跳转
0167:00457A2A  MOV      ECX,[EBX+08]
0167:00457A2D  MOV      EDX,[EBX]
0167:00457A2F  LEA      EAX,[EBP-10]
0167:00457A32  CALL    00403E58
0167:00457A37  MOV      EAX,[EBP-10]  ----------->eax指向填的用户名
0167:00457A3A  MOV      EDX,[EBX+04]  ----------->edx指向所填的注册码
0167:00457A3D  CALL    004573E0      ----------->关键call
0167:00457A42  TEST    AL,AL          ---------->以AL为棋标
0167:00457A44  JZ      00457A87
0167:00457A46  PUSH    DWORD 00457C04------------>00457C04指向"PRO Version Registered for "
0167:00457A4B  PUSH    DWORD [EBX]  ----------->指向用户名的指针.下命令 D *EBX就可看到
0167:00457A4D  PUSH    DWORD 00457BD8
0167:00457A52  PUSH    DWORD 00457BE4
0167:00457A57  PUSH    DWORD 00457BF0  ------->00457BF0指向" Serial#: "
0167:00457A5C  PUSH    DWORD [EBX+08]
0167:00457A5F  LEA      EAX,[EBX+24]
0167:00457A62  MOV      EDX,06
0167:00457A67  CALL    00403ECC
0167:00457A6C  MOV      ESI,02        -------->02送ESI .这个值也比较重要.
0167:00457A71  MOV      EAX,[0047F0E8]
0167:00457A76  MOV      [0047F0DC],EAX
0167:00457A7B  MOV      BYTE [0047F0D8],01
0167:00457A82  JMP      00457B6D
    :                :
    :                :-->>(略去不少代码,主要是计算试用期剩余时间及时间计算的代码)
    :                :
    :                :
0167:00457B6D  XOR      EAX,EAX
0167:00457B6F  POP      EDX
0167:00457B70  POP      ECX
0167:00457B71  POP      ECX
0167:00457B72  MOV      [FS:EAX],EDX
0167:00457B75  PUSH    DWORD 00457B8F
0167:00457B7A  LEA      EAX,[EBP-14]
0167:00457B7D  MOV      EDX,03
0167:00457B82  CALL    00403BB0
0167:00457B87  RET   
0167:00457B88  JMP      004035A0
0167:00457B8D  JMP      SHORT 00457B7A
0167:00457B8F  MOV      EAX,ESI
0167:00457B91  POP      EDI
0167:00457B92  POP      ESI
0167:00457B93  POP      EBX
0167:00457B94  MOV      ESP,EBP
0167:00457B96  POP      EBP
0167:00457B97  RET   

      看到这里,我们已经比较清楚这个子程序的作用了. 这个子程序的大体流程是这样的,
首先,通过 0167:004579D5  CALL  0045732 根据所填的用户名计算出对应 EASY Version 正确的注册码,
并与填的假的注册码比较,再根据比较结果置AL,然后用AL为棋标测试,如果不是正确的EASY Version注册码,
则再通过0167:00457A3D CALL 004573E0  根据所填的用户名计算出对应 PRO Version 正确的注册码,并与填
的假的注册码比较,再根据比较结果置AL,然后用AL为棋标测试,如果不是正确的 PRO Version 注册码,则
为试用版,再计算时间,计算出30天试用期剩余的天数..大体流程就是这样的了.

    由于一个注册名对应两个不同的注册码,所以注册码的算法应该有两个,(实际上EASY Version算法和PRO
Version的算法基本一样,只有一点小不同,在后面我会介绍 ). 看来要想得到注册码的算法,必须要在追进
0167:004579D5  CALL  00457320 和0167:00457A67  CALL  00403ECC 这两个CALL中再分析.

        休息一下先,(懒劲又上来了) *^_^*

  • 标 题:Soundnailsd的破解教程(二) (6千字)
  • 作 者:破解勇[CCG]
  • 时 间:2001-10-17 22:00:21

Soundnailsd的破解教程(二) 

  休息好了,我们接着上一篇继续来 :-)
话说上回讲到要想得到注册码的算法,必须要在追进0167:004579D5  CALL  00457320 和
0167:00457A67  CALL  00403ECC 这两个CALL中.好,我们就先来看看 CALL  00457320。

下面与计算注册码无关的代码我就不进行说明了. :-)

代码如下:

0167:00457320  PUSH    EBP
0167:00457321  MOV      EBP,ESP
0167:00457323  ADD      ESP,BYTE -0C
0167:00457326  PUSH    EBX
0167:00457327  XOR      ECX,ECX
0167:00457329  MOV      [EBP-0C],ECX
0167:0045732C  MOV      [EBP-08],EDX
0167:0045732F  MOV      [EBP-04],EAX
0167:00457332  MOV      EAX,[EBP-04]
0167:00457335  CALL    00403FC0
0167:0045733A  MOV      EAX,[EBP-08]
0167:0045733D  CALL    00403FC0
0167:00457342  XOR      EAX,EAX
0167:00457344  PUSH    EBP
0167:00457345  PUSH    DWORD 004573C6
0167:0045734A  PUSH    DWORD [FS:EAX]
0167:0045734D  MOV      [FS:EAX],ESP
0167:00457350  LEA      EAX,[EBP-04]
0167:00457353  MOV      EDX,004573DC
0167:00457358  CALL    00403E14
0167:0045735D  MOV      EAX,[EBP-04]
0167:00457360  CALL    00403E0C    --->在用户名后加上字符串"SNL".如:"pojieyongSNL"
0167:00457365  PUSH    EAX          --->EAX为用户名+SNL的个数
0167:00457366  LEA      EAX,[EBP-04]
0167:00457369  CALL    00403FDC
0167:0045736E  MOV      ECX,FFFF2012 -->FFFF2012送ECX        ─────┐
0167:00457373  POP      EDX        ---->用户名+SNL的个数弹到EDX       │
0167:00457374  CALL    00456F2C  ------>一个关键的计算的 CALL       │
0167:00457379  MOV      EBX,EAX  ------->EAX送EBX                     │
0167:0045737B  MOV      EDX,07---------->07送EDX                     │
0167:00457380  MOV      EAX,EBX -------->EBX送EAX                     │
0167:00457382  CALL    004565BC ----->将EAX移位的CALL.        │
0167:00457387  XOR      EAX,A4529D37---->异或             │─>这一段过程就是算码的过程.
0167:0045738C  MOV      EBX,EAX    ---->EAX送EBX                      │     (下面将详细介绍)
0167:0045738E  MOV      EAX,EBX    ---->EBX送EAX                     │
0167:00457390  CDQ                ----->扩展EAX的符号位至EDX          │
0167:00457391  PUSH    EDX                                           │
0167:00457392  PUSH    EAX                                           │
0167:00457393  LEA      EAX,[EBP-0C]                                 │
0167:00457396  CALL    00457FC8  ----->最后算出注册码的CALL**────┘
0167:0045739B  MOV      EAX,[EBP-0C]---->将真注册码的首地址送EAX.
0167:0045739E  MOV      EDX,[EBP-08]---->将假注册码的首地址送EDX.
0167:004573A1  CALL    00403F1C-------->判断真假注册码CALL
0167:004573A6  SETZ    AL      -------->就是它 嘿嘿..狐狸尾巴.
0167:004573A9  MOV      EBX,EAX 
0167:004573AB  XOR      EAX,EAX
0167:004573AD  POP      EDX
0167:004573AE  POP      ECX
0167:004573AF  POP      ECX
0167:004573B0  MOV      [FS:EAX],EDX
0167:004573B3  PUSH    DWORD 004573CD
0167:004573B8  LEA      EAX,[EBP-0C]
0167:004573BB  MOV      EDX,03
0167:004573C0  CALL    00403BB0
0167:004573C5  RET   
0167:004573C6  JMP      004035A0
0167:004573CB  JMP      SHORT 004573B8
0167:004573CD  MOV      EAX,EBX
0167:004573CF  POP      EBX
0167:004573D0  MOV      ESP,EBP
0167:004573D2  POP      EBP
0167:004573D3  RET   

  这个子程序的大体流程是这样的,(与算码无关的代码这里就不介绍了),将ffff2012送ECX,
然后经过0167:00457374  CALL 00456F2C 根据填的(用户名+SNL)计算得到一个值,

    然后再经过0167:00457382  CALL  004565BC 将这个值 (在EAX中)进行移位,移位的个数为7,
这个CALL的上面有指令 MOV  EDX,07 ;这就是移位的个数. 

  这个值经过移位以后,再与A4529D37异或,结果在 EAX中..然后再经过 0167:00457396  CALL 00457FC8
计算出正确的注册码.

  最后再通过0167:004573A1  CALL  00403F1C来判断真假注册码.

  好了,让我们来仔细分析一下计算注册码的流程:

①  首先,在用户名后面加上"SNL",例如,用户名为"pojieyong",加上"SNL"形成"pojieyongSNL".
FFFF2012送ECX,用户名+SNL的个数送EDX.然后经过0167:00457374  CALL 00456F2C 根据填的(用户名+SNL)
计算得到一个值.让我们来看看 0167:00457374  CALL 00456F2C.

代码如下 :

  其中EAX指向 "用户名+SNL"的首地址, ECX=FFFF2012, EDX=用户名+SNL的个数.

0167:00456F2C  PUSH    EBX
0167:00456F2D  PUSH    ESI
0167:00456F2E  TEST    EAX,EAX
0167:00456F30  JNZ      00456F36
0167:00456F32  XOR      ECX,ECX
0167:00456F34  JMP      SHORT 00456F66
0167:00456F36  NOT      ECX          ------>ECX取反.即FFFF2012取反
0167:00456F38  TEST    EDX,EDX      ------>测试EDX
0167:00456F3A  JNG      00456F64
0167:00456F3C  XOR      EBX,EBX      ----->EBX清零<<───────────┐
0167:00456F3E  MOV      BL,[EAX]     ---->取用户名                    │
0167:00456F40  MOV      ESI,ECX      ------>ECX送ESI           │
0167:00456F42  XOR      EBX,ESI      ------>异或             │
0167:00456F44  AND      EBX,FF      ------>EBX和ff进行与运算.      │循
0167:00456F4A  MOV      EBX,[EBX*4+0047ECC8]               │环
0167:00456F51  SHR      ESI,08      ------>ESI逻辑右移8位         │
0167:00456F54  AND      ESI,00FFFFFF    -->ESI和00FFFFFF进行与运算    │
0167:00456F5A  XOR      EBX,ESI        ---->异或             │
0167:00456F5C  MOV      ECX,EBX        ---->EBX送ECX           │
0167:00456F5E  DEC      EDX            ---->EDX-1(个数减1)        │
0167:00456F5F  INC      EAX          ---->EAX+1             │   
0167:00456F60  TEST    EDX,EDX      ----->测试EDX            │
0167:00456F62  JG      00456F3C      ----->循环,直到EDX=0  ──────┘
0167:00456F64  NOT      ECX          ----->ECX取反
0167:00456F66  MOV      EAX,ECX      ----->ECX送EAX
0167:00456F68  POP      ESI
0167:00456F69  POP      EBX
0167:00456F6A  RET   
  经过上面的运算得到一个值,送到EAX中。


  ② 将这个值再经过0167:00457382  CALL 004565BC 进行移位.
代码如下:

其中EDX=7 .
0167:004565BC  MOV      CL,DL ----->7送CL
0167:004565BE  ROL      EAX,CL----->将EAX循环左移
0167:004565C0  RET   
  EAX循环左移以后,再与 A4529D37 进行异或,结果在EAX中. 然后再用CDQ指令扩展EAX的符号位至EDX. 


  ③ 再往下来就是最后算出注册码的CALL了,这个CALL就是0167:00457396  CALL  00457FC8
代码如下:

0167:00457FC8  PUSH    EBP
0167:00457FC9  MOV      EBP,ESP
0167:00457FCB  PUSH    EBX
0167:00457FCC  MOV      EBX,EAX
0167:00457FCE  PUSH    DWORD [EBP+0C]
0167:00457FD1  PUSH    DWORD [EBP+08]
0167:00457FD4  MOV      EDX,EBX
0167:00457FD6  MOV      EAX,[0047F0F0]--->0047F0F0处存放的是一个指针,送到eax.
                                          该指针指向"M65DCBEL2NZPT8RW34HXK71Y9AF"
0167:00457FDB  CALL    00457004  ------>真正计算码的CALL.
0167:00457FE0  POP      EBX
0167:00457FE1  POP      EBP
0167:00457FE2  RET      08
  其实EAX所指向的字符串"M65DCBEL2NZPT8RW34HXK71Y9AF" 就是注册码取值范围,即注册码是从这个字符串
中取值. 那么,注册码是按什么规律来取值的呢?  算法就在0167:00457FDB  CALL  00457004里,看来我们还要
继续往里追 :-)

    再偷懒一下, 闪先  *^_^*

  • 标 题:Soundnailsd的破解教程(三) (7千字)
  • 作 者:破解勇[CCG]
  • 时 间:2001-10-17 22:02:42

Soundnailsd的破解教程(三)

  ④ 现在是我们要追近0167:00457FDB  CALL  00457004时候了。
代码如下:
  (略过与算码无关的代码).
 
    :                    :
    :                    :
    :                    :
0167:00457047  MOV      EBX,EAX
0167:00457049  FILD    QWORD [EBP+08]    ----> 这是一个装入整数指令.将EBP+08处存放的值
                                              (这个值就是上一篇所说的那个EAX循环左移以后,
                                              再与 A4529D37 进行异或,结果在EAX中的值),
                                              装入协处理器堆栈寄存器st(0).

0167:0045704C  FSTP    TWORD [EBP-20]    ----->这是存储和弹出实数的指令.将st(0)中的浮点数
                                              复制到EBP-20所指的地方,再进行一次出栈操作. 
                                                   
0167:0045704F  WAIT   
0167:00457050  CMP      DWORD [EBP+0C],BYTE +00
0167:00457054  JNZ      00457062
0167:00457056  CMP      DWORD [EBP+08],BYTE +00
0167:0045705A  JNC      NEAR 004570EE
0167:00457060  JMP      SHORT 00457068
0167:00457062  JNL      NEAR 004570EE
0167:00457068  FLD      TWORD [0047F0C8]  ---->装入0047F0C8处所指的实数
0167:0045706E  FADD    DWORD [0045713C]  ---->加法
0167:00457074  FMUL    DWORD [00457140]  ---->乘法
0167:0045707A  FLD      TWORD [EBP-20]    ---->装入EBP-20所指的实数
0167:0045707D  FADDP    ST1              ---->加法并弹出
0167:0045707F  FSTP    TWORD [EBP-20]
0167:00457082  WAIT   
0167:00457083  JMP      SHORT 004570EE
0167:00457085  MOV      AX,[EBP-18]  ----->将浮点数的阶码部分和符号位送AX 
0167:00457089  PUSH    EAX
0167:0045708A  PUSH    DWORD [EBP-1C]----->将浮点数的32-63位入栈
0167:0045708D  PUSH    DWORD [EBP-20]----->将浮点数的0-31位入栈
0167:00457090  MOV      [EBP-28],EBX  ----->字符串长度1B送ebp-28所指处 
0167:00457093  FILD    DWORD [EBP-28]
0167:00457096  ADD      ESP,BYTE -0C
0167:00457099  FSTP    TWORD [ESP]  ---->0000001B的浮点形式.
0167:0045709C  WAIT   
0167:0045709D  CALL    00456FA4      ---->运算,计算出下面EAX的浮点形式
0167:004570A2  FSTP    TWORD [EBP-10]
0167:004570A5  WAIT   
0167:004570A6  FLD      TWORD [EBP-10]
0167:004570A9  CALL    00402A08        ----->决定eax的值.后面将介绍到
0167:004570AE  ADD      EAX,BYTE +01
0167:004570B1  ADC      EDX,BYTE +00
0167:004570B4  MOV      EDX,[EBP-04]    ----->EBP-04出存放的是指向"M65DCBEL2NZPT8RW34HXK71Y9AF"
                                              的指针.
0167:004570B7  MOV      DL,[EDX+EAX-01]  ---->将EDX+EAX-01指向的字符送DL,这就是注册码的其中一位,
                                              可以看出是由EAX来定位注册码的.是从后到前来定位的,
                                              即先定位最后一位注册码,再定位到数第二位注册码.......
0167:004570BB  LEA      EAX,[EBP-2C]
0167:004570BE  CALL    00403D34
0167:004570C3  MOV      EDX,[EBP-2C]
0167:004570C6  LEA      EAX,[EBP-24]
0167:004570C9  MOV      ECX,[EBP-24]
0167:004570CC  CALL    00403E58
0167:004570D1  MOV      [EBP-28],EBX  ----->字符串长度1B送ebp-28所指处
0167:004570D4  FILD    DWORD [EBP-28]------> 装入整数0000001B
0167:004570D7  FLD      TWORD [EBP-20]------>装入EBP-20所指的实数.
0167:004570DA  FDIVRP  ST1            ----->反向除法,并弹出
0167:004570DC  CALL    00402A14
0167:004570E1  MOV      [EBP-34],EAX
0167:004570E4  MOV      [EBP-30],EDX
0167:004570E7  FILD    QWORD [EBP-34] ---->装入整数,除法的商.
0167:004570EA  FSTP    TWORD [EBP-20] ---->存储除法的商的浮点形式到EBP-20所指处,再进行一次出栈操作.
0167:004570ED  WAIT   
0167:004570EE  FLD      TWORD [EBP-20] ---->装入EBP-20所指处的实数
0167:004570F1  FCOMP    DWORD [00457144]-->比较并弹出
0167:004570F7  FNSTSW  AX        ------>将状态寄存器的值复制到AX
0167:004570F9  SAHF                ------>将AH寄存器的第7,6,4,2,0位分别送入到
                                              SF,ZF,AF,PF,CF
0167:004570FA  JNZ      00457085    ---->ZF=0则循环       
0167:004570FC  MOV      EAX,ESI
    :                :
    :                :
    :                :(略过一部分)
    :                :
 
 
  上面这一段的大体流程是这样的(只说重点的),

这一步0167:00457085  MOV  AX,[EBP-18] ,将浮点数的阶码部分和符号位送AX,第一次的浮点数就是那个
上一篇所说的那个EAX循环左移以后,再与 A4529D37 进行异或,结果在EAX中的值的浮点形式.

  接下来的0167:0045708A  PUSH  DWORD [EBP-1C] 和 0167:0045708D  PUSH  DWORD [EBP-20]是分别将
浮点数的32-63位入栈 和 将浮点数的0-31位入栈.

  接下来是经过0167:0045709D  CALL  00456FA4这个call,在这个CALL里,浮点数与字符串长度0000001B的
浮点形式进行一系列运算,最终得到一个值,这个值就是后面的EAX的浮点形式.(代码见后)

  再下来经过0167:004570A9  CALL  00402A08,这个CALL将上面得到的那个值,以整数存放到当前堆栈指针
所指处,然后将此整数弹到EAX中. (代码见后)

  继续往下,EAX+1, 再往下 ,0167:004570B7  MOV  DL,[EDX+EAX-01] 这一步通过EAX的值来定位注册码的
取值.

  再往下,有一个反向除法,商就是另一次循环的初始的浮点数. 对了,要注意操作数的宽度.

  循环 ,直到ZF=1

流程就是这样的了.下面是0167:0045709D  CALL  00456FA4和0167:004570A9  CALL 00402A08代码.


    0167:0045709D  CALL    00456FA4
代码如下:

0167:00456FA4  PUSH    EBP
0167:00456FA5  MOV      EBP,ESP
0167:00456FA7  ADD      ESP,BYTE -20
0167:00456FAA  FLD      TWORD [EBP+14]------->装入EBP-14所指的实数.
0167:00456FAD  FLD      TWORD [EBP+08]------->装入EBP+08所指的实数.0000001B的浮点形式.
0167:00456FB0  FDIVP    ST1,ST0    --------->除法并弹出
0167:00456FB2  CALL    004029F0
0167:00456FB7  FLD      TWORD [EBP+08]------->装入EBP-08所指的实数
0167:00456FBA  FMULP    ST1        --------->乘法并弹出
0167:00456FBC  FLD      TWORD [EBP+14]
0167:00456FBF  FSUBRP  ST1        --------->反向减法并弹出
0167:00456FC1  FSTP    TWORD [EBP-20] ------>存储实数到ebp-20所指处,再进行一次出栈操作
0167:00456FC4  WAIT   
0167:00456FC5  FLD      TWORD [EBP-20]
0167:00456FC8  FCOMP    DWORD [00456FFC] ---->比较并弹出
0167:00456FCE  FNSTSW  AX          ----->将状态寄存器的值复制到AX
0167:00456FD0  SAHF                ------>将AH寄存器的第7,6,4,2,0位分别送入到
                                          SF,ZF,AF,PF,CF
0167:00456FD1  JNC      00456FDF
0167:00456FD3  FLD      TWORD [EBP-20]
0167:00456FD6  FLD      TWORD [EBP+08]
0167:00456FD9  FADDP    ST1      ------->加法并弹出.   
0167:00456FDB  FSTP    TWORD [EBP-20] -->存储实数到EBP-20所指处.再进行一次出栈操作
0167:00456FDE  WAIT   
0167:00456FDF  MOV      EAX,[EBP-20]  ──┐
0167:00456FE2  MOV      [EBP-10],EAX   │
0167:00456FE5  MOV      EAX,[EBP-1C]   │
0167:00456FE8  MOV      [EBP-0C],EAX      │-->这一段将ebp-20所指的实数复制到ebp-10所指处.     
0167:00456FEB  MOV     AX,[EBP-18]      │ 
0167:00456FEF  MOV      [EBP-08],AX  ──┘   
0167:00456FF3  FLD      TWORD [EBP-10] ---->装入EBP-10所指的实数.
0167:00456FF6  MOV      ESP,EBP
0167:00456FF8  POP      EBP
0167:00456FF9  RET      18


    0167:004570A9  CALL    00402A08
代码如下:

0167:00402A08  SUB      ESP,BYTE +08
0167:00402A0B  FISTP    QWORD [ESP] ---->存储整数.再进行一次出栈操作.
0167:00402A0E  WAIT   
0167:00402A0F  POP      EAX      ------>将整数弹到EAX.
0167:00402A10  POP      EDX
0167:00402A11  RET   


    好了,就写到这里吧 .


                                                     
                                                      破解勇[CCG]

  • 标 题:关于浮点运算的一点补充 (1千字)
  • 作 者:破解勇[CCG]
  • 时 间:2001-10-18 0:02:02

关于浮点运算的一点补充


㈠  关于协处理器指令
 
    数值运算协处理器和处理器(现在的CPU都内置协处理器)共同监视着指令流,
如果是数值运算协处理器指令则由协处理器执行,如果不是协处理器指令,则由处
理器执行.


㈡  关于ST(0)、ST(1)、...ST(n)
   
  ST(n)为堆栈寄存器,也叫做浮点寄存器. 运算结果及出栈,入栈都是对ST(n)的
操作.这也正是用TRW2000等调试工具代过浮点运算指令时看不到动静的原因.因为
用TRW2000代过浮点运算指令时,浮点运算的结果在堆栈寄存器,出栈入栈也是对堆
栈寄存器操作,所以在TRW2000的寄存器窗口中看不出变化. 




㈢  关于指令中的P、R、I。

 ①    命令后面加上字母P,则表示该指令执行完后再进行一次堆栈寄存器弹出操作,
      如:FADD和FADDP .

  ②  命令后面加上字母R,表示该命令是反向模式。如:FDIV 和 FDIVR

  ③  命令中第二个字母是I,表示以整数操作。如:FLD 和 FILD

㈣  浮点数的阶码部分为偏移阶码.

  ① 32位长浮点数用 7FH  为偏移
 
  ② 64位长浮点数用 3FFH  为偏移

  ③ 80位长浮点数用 3FFFH 为偏移

   
㈤   关于协处理器与处理器之间的数据交换

    指令 FSTSW AX 可以直接将状态寄存器的内容复制到AX.
  另外 FSTSW指令可以将状态寄存器的内容复制到内存


㈥  关于标记寄存器
  每个协处理器堆栈寄存器对应一个标记寄存器,来标明堆栈寄存器中的内容.它标
明了堆栈中的各数是合法还是非法,是无穷还是为0或空.

    00=合法
    01=0
    10=非法或无穷
    11=空



㈦  关于指令系统

  在《加密与解密--软件保护技术及完全解决方案》一书的附录部分,有浮点指令小结,
在352页,指令很全, 而且附带了一个例子。

    好了,先写这么些吧

                                          破解勇[CCG]