[表格算法]Easy PDF to Word Converter 2.0 的算法分析 + delphi(kol/mck)注册机源代码
附件:http://bbs.pediy.com/attachment.php?attachmentid=5960&d=1180185651
在 http://www.unpack.cn/thread-12944-1-1.html 上,看到 xss517 兄弟在讨论这个软件,我也下来看了看,发现又是个用了表格算法的,所以把它写了出来。

又因为下载的是个汉化版,我怕版本有点老,不适用,所以我在它的官方主页:http://www.pdf-to-html-word.com/download.htm 下载了个原装版本来分析。

这是我分析的第三个与这种算法相关的程序,估计也是写这种类型的最后一个了。下次再遇上这种[表格算法]的话,估计可以'小时杀'了,呵呵('秒杀'还办不到)。

好的,我们开始:

在注册框中输入注册名和假码后,会有提示:‘the registration code is wrong. please check it and try again.’,顺着这个提示,将程序拖入OD中,先不运行,使用字符串参考,找到了提示所在的位置:


超级字符串参考         , 条目 851
 地址=00428E6B
 反汇编地址=push    pdf2rtf.0049A1B8
 字符内容=the registration code is wrong. please check it and try again.


而在它的上面便是注册成功的提示:‘thanks for your registration! easy pdf to word converter.’

超级字符串参考         , 条目 848
 地址=00428DB0
 反汇编地址=push    pdf2rtf.0049A1F8
 字符内容=thanks for your registration! easy pdf to word converter.



双击错误提示、成功提示的字符串后,分别来到了如下的地方(当然,你也可以使用GetDlgItemTextA下断,来到这里):

》》》》》》》》
00428E69   > \6A 00         push    0                                ; 跳转来自 00428DA5
00428E6B   .  68 B8A14900   push    pdf2rtf.0049A1B8                 ; |the registration code is wrong. please check it and try again.
00428E70   .  56            push    esi                              ; |hOwner
00428E71   .  FF15 F4344700 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
00428E77   .  68 FB030000   push    3FB                              ; /ControlID = 3FB (1019.)
00428E7C   .  56            push    esi                              ; |hWnd
00428E7D   .  FF15 F8344700 call    dword ptr [<&USER32.GetDlgItem>] ; \GetDlgItem
00428E83   .  50            push    eax                              ; /hWnd
00428E84   .  FF15 FC344700 call    dword ptr [<&USER32.SetFocus>]   ; \SetFocus
00428E8A   .  E9 A4000000   jmp     pdf2rtf.00428F33
》》》》》》》》
00428DAB   .  68 34A24900   push    pdf2rtf.0049A234                 ; |thanks for your registration!
00428DB0   .  68 F8A14900   push    pdf2rtf.0049A1F8                 ; |thanks for your registration! easy pdf to word converter.
00428DB5   .  56            push    esi                              ; |hOwner
00428DB6   .  FF15 F4344700 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
00428DBC   .  8B0D F0D64900 mov     ecx, dword ptr [49D6F0]          ;  pdf2rtf.0049D704
00428DC2   .  894C24 18     mov     dword ptr [esp+18], ecx
00428DC6   .  68 1C1A4A00   push    pdf2rtf.004A1A1C
》》》》》》》》



顺着它们它们跳转来的地方,往上查看,来到这里:

》》》》》》》》
00428D6C   > \8B7424 18     mov     esi, dword ptr [esp+18]          ;  Case 1 of switch 00428D3F
00428D70   .  68 C8000000   push    0C8                              ; /Count = C8 (200.)
00428D75   .  B9 32000000   mov     ecx, 32                          ; |
00428D7A   .  33C0          xor     eax, eax                         ; |
00428D7C   .  BF 1C1A4A00   mov     edi, pdf2rtf.004A1A1C            ; |
00428D81   .  68 1C1A4A00   push    pdf2rtf.004A1A1C                 ; |Buffer = pdf2rtf.004A1A1C
00428D86   .  68 FB030000   push    3FB                              ; |ControlID = 3FB (1019.)
00428D8B   .  56            push    esi                              ; |hWnd
00428D8C   .  F3:AB         rep     stos dword ptr es:[edi]          ; |
00428D8E   .  FF15 F0344700 call    dword ptr [<&USER32.GetDlgItemTe>; \GetDlgItemTextA
00428D94   .  68 1C1A4A00   push    pdf2rtf.004A1A1C
00428D99   .  E8 E2FBFFFF   call    pdf2rtf.00428980
00428D9E   .  83C4 04       add     esp, 4
00428DA1   .  85C0          test    eax, eax
00428DA3   .  6A 20         push    20                               ; /Style = MB_OK|MB_ICONQUESTION|MB_APPLMODAL
00428DA5   .  0F84 BE000000 je      pdf2rtf.00428E69                 ; |不跳,便OK了!
00428DAB   .  68 34A24900   push    pdf2rtf.0049A234                 ; |thanks for your registration!
00428DB0   .  68 F8A14900   push    pdf2rtf.0049A1F8                 ; |thanks for your registration! easy pdf to word converter.
00428DB5   .  56            push    esi                              ; |hOwner
00428DB6   .  FF15 F4344700 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
00428DBC   .  8B0D F0D64900 mov     ecx, dword ptr [49D6F0]          ;  pdf2rtf.0049D704
》》》》》》》》



因此,我们在00428D6C这个地方按F2键下断点,然后Ctrl+F2重新载入程序,F9运行,然后打开注册输入窗口,输入一组实验码:

name:aCaFeeL
code:1111-2222-3333-4444(为什么是这种格式呐?看下面进入算法后的反编代码,便明白了)



点击‘OK’按钮,程序便被中断在了如下位置(开始分析):

》》》》》》》》
『以上代码省略』
00428D6C   > \8B7424 18     mov     esi, dword ptr [esp+18]          ;  Case 1 of switch 00428D3F
00428D70   .  68 C8000000   push    0C8                              ; /Count = C8 (200.)
00428D75   .  B9 32000000   mov     ecx, 32                          ; |
00428D7A   .  33C0          xor     eax, eax                         ; |
00428D7C   .  BF 1C1A4A00   mov     edi, pdf2rtf.004A1A1C            ; |
00428D81   .  68 1C1A4A00   push    pdf2rtf.004A1A1C                 ; |Buffer = pdf2rtf.004A1A1C
00428D86   .  68 FB030000   push    3FB                              ; |ControlID = 3FB (1019.)
00428D8B   .  56            push    esi                              ; |hWnd
00428D8C   .  F3:AB         rep     stos dword ptr es:[edi]          ; |
00428D8E   .  FF15 F0344700 call    dword ptr [<&USER32.GetDlgItemTe>; \得到注册码
00428D94   .  68 1C1A4A00   push    pdf2rtf.004A1A1C
00428D99   .  E8 E2FBFFFF   call    pdf2rtf.00428980                 ;  关键算法,决定eax值,F7进入
00428D9E   .  83C4 04       add     esp, 4
00428DA1   .  85C0          test    eax, eax                         ;  eax and eax = 0 ???
00428DA3   .  6A 20         push    20                               ; /;
00428DA5   .  0F84 BE000000 je      pdf2rtf.00428E69                 ; |eax and eax不为0,便OK了!
00428DAB   .  68 34A24900   push    pdf2rtf.0049A234                 ; |;
00428DB0   .  68 F8A14900   push    pdf2rtf.0049A1F8                 ; |thanks for your registration! easy pdf to word converter.
00428DB5   .  56            push    esi                              ; |hOwner
00428DB6   .  FF15 F4344700 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
00428DBC   .  8B0D F0D64900 mov     ecx, dword ptr [49D6F0]          ;  pdf2rtf.0049D704
00428DC2   .  894C24 18     mov     dword ptr [esp+18], ecx
『以下代码省略』
》》》》》》》》



毫无疑问,上面这段代码显示了,当

00428DA1   .  85C0          test    eax, eax

处的 eax and eax = 0时,便注册成功了,而决定eax数值的便是00428D99处的:

00428D99   .  E8 E2FBFFFF   call    pdf2rtf.00428980

因此,我们自然应该F7键进入其中,重新来过后,中断在00428D99,然后进入到call    pdf2rtf.00428980中,走过一些代码后,来到这里:

》》》》》》》》
『以上代码省略』
00428A99  |.  8D4C24 24     lea     ecx, dword ptr [esp+24]
00428A9D  |.  885C24 30     mov     byte ptr [esp+30], bl
00428AA1  |.  E8 C68E0300   call    pdf2rtf.0046196C
00428AA6  |.  8B4424 1C     mov     eax, dword ptr [esp+1C]          ;  注册码中的 1~4位
00428AAA  |.  8D4C24 1C     lea     ecx, dword ptr [esp+1C]
00428AAE  |.  8B40 F8       mov     eax, dword ptr [eax-8]
00428AB1  |.  50            push    eax
00428AB2  |.  E8 E5920300   call    pdf2rtf.00461D9C
00428AB7  |.  50            push    eax
00428AB8  |.  E8 E3480200   call    pdf2rtf.0044D3A0                 ;  eax计算::F7进入 (eax为最终所要的结果)
00428ABD  |.  8B4C24 1C     mov     ecx, dword ptr [esp+1C]          ;  注册码中的 6~9位
00428AC1  |.  83C4 04       add     esp, 4
00428AC4  |.  8BF0          mov     esi, eax                         ;  esi = eax *注意:esi赋值了*
00428AC6  |.  8B41 F8       mov     eax, dword ptr [ecx-8]
00428AC9  |.  8D4C24 18     lea     ecx, dword ptr [esp+18]
00428ACD  |.  50            push    eax
00428ACE  |.  E8 C9920300   call    pdf2rtf.00461D9C
00428AD3  |.  50            push    eax
00428AD4  |.  E8 C7480200   call    pdf2rtf.0044D3A0                 ;  eax计算::F7进入 (eax为最终所要的结果)
00428AD9  |.  8B5424 18     mov     edx, dword ptr [esp+18]          ;  注册码中的 11~14位
00428ADD  |.  83C4 04       add     esp, 4
00428AE0  |.  8BF8          mov     edi, eax                         ;  edi = eax *注意:edi赋值了*
00428AE2  |.  8D4C24 14     lea     ecx, dword ptr [esp+14]
00428AE6  |.  8B42 F8       mov     eax, dword ptr [edx-8]
00428AE9  |.  50            push    eax
00428AEA  |.  E8 AD920300   call    pdf2rtf.00461D9C
00428AEF  |.  50            push    eax
00428AF0  |.  E8 AB480200   call    pdf2rtf.0044D3A0                 ;  eax计算::F7进入 (eax为最终所要的结果)
00428AF5  |.  8BD8          mov     ebx, eax                         ;  ebx = eax *注意:ebx赋值了*
00428AF7  |.  8B4424 3C     mov     eax, dword ptr [esp+3C]          ;  注册码中的 16~19位
00428AFB  |.  83C4 04       add     esp, 4
00428AFE  |.  8D4C24 38     lea     ecx, dword ptr [esp+38]
00428B02  |.  8B40 F8       mov     eax, dword ptr [eax-8]
00428B05  |.  50            push    eax
00428B06  |.  E8 91920300   call    pdf2rtf.00461D9C
00428B0B  |.  50            push    eax
00428B0C  |.  E8 8F480200   call    pdf2rtf.0044D3A0                 ;  eax计算::F7进入 (eax为最终所要的结果)
00428B11  |.  83C4 04       add     esp, 4
00428B14  |.  8D4C24 1C     lea     ecx, dword ptr [esp+1C]
00428B18  |.  8BE8          mov     ebp, eax                         ;  ebp = eax *注意:ebp赋值了*
00428B1A  |.  6A FF         push    -1
00428B1C  |.  E8 CA920300   call    pdf2rtf.00461DEB
00428B21  |.  6A FF         push    -1
00428B23  |.  8D4C24 1C     lea     ecx, dword ptr [esp+1C]
00428B27  |.  E8 BF920300   call    pdf2rtf.00461DEB
00428B2C  |.  6A FF         push    -1
00428B2E  |.  8D4C24 18     lea     ecx, dword ptr [esp+18]
00428B32  |.  E8 B4920300   call    pdf2rtf.00461DEB
00428B37  |.  6A FF         push    -1
00428B39  |.  8D4C24 3C     lea     ecx, dword ptr [esp+3C]
00428B3D  |.  E8 A9920300   call    pdf2rtf.00461DEB                 ;  //下面是第一部份的算法
00428B42  |.  8D8CB6 E4C000>lea     ecx, dword ptr [esi+esi*4+C0E4]  ;  ecx := esi * $5 + $C0E4;
00428B49  |.  8D844E 942600>lea     eax, dword ptr [esi+ecx*2+2694]  ;  eax := esi + ecx * $2 + $2694;
00428B50  |.  B9 10270000   mov     ecx, 2710                        ;  ecx := $2710;
00428B55  |.  D1E0          shl     eax, 1                           ;  eax := eax shl $1;
00428B57  |.  99            cdq
00428B58  |.  F7F9          idiv    ecx                              ;  edx := eax mod ecx;
00428B5A  |.  3BFA          cmp     edi, edx                         ;  edi = edx ?
00428B5C      74 0B         je      short pdf2rtf.00428B69           ;  相等便OK!
00428B5E  |.  C64424 30 04  mov     byte ptr [esp+30], 4
00428B63  |.  8D4C24 38     lea     ecx, dword ptr [esp+38]
00428B67  |.  EB 29         jmp     short pdf2rtf.00428B92           ;  //下面是第二部份的算法
00428B69  |>  8D83 FEE5FFFF lea     eax, dword ptr [ebx-1A02]        ;  eax := ebx - $1A02;
00428B6F  |.  81C3 E8080000 add     ebx, 8E8                         ;  ebx := ebx + $8E8;
00428B75  |.  99            cdq                                      ;  edx 接受 eax 的扩展
00428B76  |.  33C2          xor     eax, edx                         ;  eax := eax xor edx;
00428B78  |.  B9 10270000   mov     ecx, 2710                        ;  ecx := $2710;
00428B7D  |.  2BC2          sub     eax, edx                         ;  eax := eax - edx;
00428B7F  |.  C64424 30 04  mov     byte ptr [esp+30], 4
00428B84  |.  0FAFC3        imul    eax, ebx                         ;  eax := eax * ebx;
00428B87  |.  99            cdq                                      ;  edx 接受 eax 的扩展
00428B88  |.  F7F9          idiv    ecx                              ;  edx := eax mod ecx;
00428B8A  |.  8D4C24 38     lea     ecx, dword ptr [esp+38]
00428B8E  |.  3BEA          cmp     ebp, edx                         ;  ebp = edx ?
00428B90  |.  74 63         je      short pdf2rtf.00428BF5           ;  相等便OK!
00428B92  |>  E8 D58D0300   call    pdf2rtf.0046196C
00428B97  |.  8D4C24 14     lea     ecx, dword ptr [esp+14]
00428B9B  |.  C64424 30 03  mov     byte ptr [esp+30], 3
00428BA0  |.  E8 C78D0300   call    pdf2rtf.0046196C
『以下代码省略』
》》》》》》》》



通过上面的代码,自然发现,决定注册成功与否的esi、edi、ebx和ebp中的数值都是首先由call    pdf2rtf.0044D3A0这个子程序决定,自然,F7进入后,来到了这里:

》》》》》》》》
0044D3A0  /$  FF7424 04     push    dword ptr [esp+4]
0044D3A4  |.  E8 6CFFFFFF   call    pdf2rtf.0044D315                 ;  F7再次进入
0044D3A9  |.  59            pop     ecx
0044D3AA  \.  C3            retn
》》》》》》》》



再次,F7键进入:

0044D3A4  |.  E8 6CFFFFFF   call    pdf2rtf.0044D315 

后,便来到了这个子算法的开始处:

》》》》》》》》
0044D315  /$  53            push    ebx                              ;  其中的一部分算法
0044D316  |.  55            push    ebp
0044D317  |.  56            push    esi
0044D318  |.  57            push    edi
0044D319  |.  8B7C24 14     mov     edi, dword ptr [esp+14]
0044D31D  |>  833D A00E4A00>/cmp     dword ptr [4A0EA0], 1
0044D324  |.  7E 0F         |jle     short pdf2rtf.0044D335
0044D326  |.  0FB607        |movzx   eax, byte ptr [edi]
0044D329  |.  6A 08         |push    8
0044D32B  |.  50            |push    eax
0044D32C  |.  E8 8D790000   |call    pdf2rtf.00454CBE
0044D331  |.  59            |pop     ecx
0044D332  |.  59            |pop     ecx
0044D333  |.  EB 0F         |jmp     short pdf2rtf.0044D344
0044D335  |>  0FB607        |movzx   eax, byte ptr [edi]             ;  如循环,依次取字符 -> eax
0044D338  |.  8B0D 940C4A00 |mov     ecx, dword ptr [4A0C94]         ;  注意内存窗口中 $004A0C9E~$004A0D9D 的一张Key表
0044D33E  |.  8A0441        |mov     al, byte ptr [ecx+eax*2]        ;  取的字符码*2 在key表中的对应值 -> al
0044D341  |.  83E0 08       |and     eax, 8                          ;  eax := eax and $8;
0044D344  |>  85C0          |test    eax, eax                        ;  eax and eax = 0 ???
0044D346      74 03         je      short pdf2rtf.0044D34B           ;  =0,跳
0044D348  |.  47            |inc     edi
0044D349  |.^ EB D2         \jmp     short pdf2rtf.0044D31D
0044D34B  |>  0FB637        movzx   esi, byte ptr [edi]              ;  取每组的第一个字符 -> esi
0044D34E  |.  47            inc     edi                              ;  取下一个字符
0044D34F  |.  83FE 2D       cmp     esi, 2D                          ;  是否为:-
0044D352  |.  8BEE          mov     ebp, esi
0044D354  |.  74 05         je      short pdf2rtf.0044D35B
0044D356  |.  83FE 2B       cmp     esi, 2B                          ;  是否为:+
0044D359  |.  75 04         jnz     short pdf2rtf.0044D35F
0044D35B  |>  0FB637        movzx   esi, byte ptr [edi]
0044D35E  |.  47            inc     edi
0044D35F  |>  33DB          xor     ebx, ebx                         ;  ebx 清零
0044D361  |>  833D A00E4A00>/cmp     dword ptr [4A0EA0], 1
0044D368  |.  7E 0C         |jle     short pdf2rtf.0044D376
0044D36A  |.  6A 04         |push    4
0044D36C  |.  56            |push    esi
0044D36D  |.  E8 4C790000   |call    pdf2rtf.00454CBE
0044D372  |.  59            |pop     ecx
0044D373  |.  59            |pop     ecx
0044D374  |.  EB 0B         |jmp     short pdf2rtf.0044D381
0044D376  |>  A1 940C4A00   |mov     eax, dword ptr [4A0C94]         ;  $004A0C9E -> eax
0044D37B  |.  8A0470        |mov     al, byte ptr [eax+esi*2]        ;  取的字符码*2 在key表中的对应值 -> al
0044D37E  |.  83E0 04       |and     eax, 4                          ;  eax := eax and $4;
0044D381  |>  85C0          |test    eax, eax                        ;  eax and eax = 0 ???
0044D383  |.  74 0D         |je      short pdf2rtf.0044D392          ;  =0,跳
0044D385  |.  8D049B        |lea     eax, dword ptr [ebx+ebx*4]      ;  eax := ebx*5;
0044D388  |.  8D5C46 D0     |lea     ebx, dword ptr [esi+eax*2-30]   ;  ebx := esi + eax * $2 - $30;
0044D38C  |.  0FB637        |movzx   esi, byte ptr [edi]             ;  取得下一个字符 -> esi
0044D38F  |.  47            |inc     edi                             ;  再依次取下一个字符
0044D390  |.^ EB CF         \jmp     short pdf2rtf.0044D361
0044D392  |>  83FD 2D       cmp     ebp, 2D                          ;  是否为符号:-
0044D395  |.  8BC3          mov     eax, ebx                         ;  eax := ebx; //注意
0044D397  |.  75 02         jnz     short pdf2rtf.0044D39B
0044D399  |.  F7D8          neg     eax                              ;  反相
0044D39B  |>  5F            pop     edi
0044D39C  |.  5E            pop     esi
0044D39D  |.  5D            pop     ebp
0044D39E  |.  5B            pop     ebx
0044D39F  \.  C3            retn
》》》》》》》》


注意:0044D338  |.  8B0D 940C4A00 |mov     ecx, dword ptr [4A0C94]  这处; 通过上面代码的分析,我们知道了其算法主要是由一张表的结果累加,再配上不等的逻辑和算术运算的结果决定的,而这张表可从:

0044D338  |.  8B0D 940C4A00 |mov     ecx, dword ptr [4A0C94]
  
这里的dword ptr [4A0C94]获得,在内存窗口中抓出来后,便是如下的格式:


[$004A0C9E~$004A0D9D 的这张表]:

004A0C9E  20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00   . . . . . . . .
004A0CAE  20 00 28 00 28 00 28 00 28 00 28 00 20 00 20 00   .(.(.(.(.(. . .
004A0CBE  20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00   . . . . . . . .
004A0CCE  20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00   . . . . . . . .
004A0CDE  48 00 10 00 10 00 10 00 10 00 10 00 10 00 10 00  H........
004A0CEE  10 00 10 00 10 00 10 00 10 00 10 00 10 00 10 00  ........
004A0CFE  84 00 84 00 84 00 84 00 84 00 84 00 84 00 84 00  ????????
004A0D0E  84 00 84 00 10 00 10 00 10 00 10 00 10 00 10 00  ??......
004A0D1E  10 00 81 00 81 00 81 00 81 00 81 00 81 00 01 00  .??????.
004A0D2E  01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00  ........
004A0D3E  01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00  ........
004A0D4E  01 00 01 00 01 00 10 00 10 00 10 00 10 00 10 00  ........
004A0D5E  10 00 82 00 82 00 82 00 82 00 82 00 82 00 02 00  .??????.
004A0D6E  02 00 02 00 02 00 02 00 02 00 02 00 02 00 02 00  ........
004A0D7E  02 00 02 00 02 00 02 00 02 00 02 00 02 00 02 00  ........
004A0D8E  02 00 02 00 02 00 10 00 10 00 10 00 10 00 20 00  ....... .



将其转换后,便是如下的一组 KEY 数据了,共(4x4)x16=256组:

   $20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,
   $20,$00,$28,$00,$28,$00,$28,$00,$28,$00,$28,$00,$20,$00,$20,$00,
   $20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,
   $20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,
   $48,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
   $10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
   $84,$00,$84,$00,$84,$00,$84,$00,$84,$00,$84,$00,$84,$00,$84,$00,
   $84,$00,$84,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
   $10,$00,$81,$00,$81,$00,$81,$00,$81,$00,$81,$00,$81,$00,$01,$00,
   $01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,
   $01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,
   $01,$00,$01,$00,$01,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
   $10,$00,$82,$00,$82,$00,$82,$00,$82,$00,$82,$00,$82,$00,$02,$00,
   $02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,
   $02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,
   $02,$00,$02,$00,$02,$00,$10,$00,$10,$00,$10,$00,$10,$00,$20,$00 


而上面这个子程序的算法,我用 Delphi + KOL/MCK 写出的大致框架为如下形式:(已写成为一个string函数格式,但我没有将有 + - 符号的情况写进去)

                                                 //EasyPDFtoRTF子call算法
function EasyPDFtoRTF_call(cz: string): integer; //定义integer函数
var
  eax, ebx, edi : integer;
  Seax, Sal : string;
const
  key: array[0 .. 255] of byte =         //密文表全部数据,共256组
  ($20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,
   $20,$00,$28,$00,$28,$00,$28,$00,$28,$00,$28,$00,$20,$00,$20,$00,
   $20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,
   $20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,
   $48,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
   $10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
   $84,$00,$84,$00,$84,$00,$84,$00,$84,$00,$84,$00,$84,$00,$84,$00,
   $84,$00,$84,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
   $10,$00,$81,$00,$81,$00,$81,$00,$81,$00,$81,$00,$81,$00,$01,$00,
   $01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,
   $01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,
   $01,$00,$01,$00,$01,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
   $10,$00,$82,$00,$82,$00,$82,$00,$82,$00,$82,$00,$82,$00,$02,$00,
   $02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,
   $02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,
   $02,$00,$02,$00,$02,$00,$10,$00,$10,$00,$10,$00,$10,$00,$20,$00);  
begin
  //////// 1 ///////循环
  edi := 1;//初始化i变量

  Seax := '000000';
  Sal  := int2hex(key[ord(cz[edi]) * 2], 2);
  Seax := Seax + Sal;
  eax := hex2int('$' + Seax);
  eax := eax and $8;

  while ((eax and eax) <> 0)  do
    begin
      edi := edi + 1;

      Seax := '000000';
      Sal  := int2hex(key[ord(cz[edi]) * 2], 2);
      Seax := Seax + Sal;
      eax := hex2int('$' + Seax);
      eax := eax and $8;
    end;

  //////// 2 ///////循环
  //edi := 1;//edi应继承前面的结果,不能再赋值了!
  ebx := 0;  //初始化ebx为0

  Seax := '004A0C';
  Sal  := int2hex(key[ord(cz[edi]) * 2], 2);
  Seax := Seax + Sal;
  eax := hex2int('$' + Seax);
  eax := eax and $4;

  while ((eax and eax) <> 0)  do
    begin
      eax := ebx * $5;
      ebx := ord(cz[edi]) + eax * $2 - $30;
      edi := edi + 1;

      Seax := '004A0C';
      Sal  := int2hex(key[ord(cz[edi]) * 2], 2);
      Seax := Seax + Sal;
      eax := hex2int('$' + Seax);
      eax := eax and $4;
    end;
  result := ebx;
end;



然后,我们再回到主算法的代码,并来到这里:

》》》》》》》》
『以上代码省略』
00428B2C  |.  6A FF         push    -1
00428B2E  |.  8D4C24 18     lea     ecx, dword ptr [esp+18]
00428B32  |.  E8 B4920300   call    pdf2rtf.00461DEB
00428B37  |.  6A FF         push    -1
00428B39  |.  8D4C24 3C     lea     ecx, dword ptr [esp+3C]
00428B3D  |.  E8 A9920300   call    pdf2rtf.00461DEB                 ;  //下面是第一部份的算法
00428B42  |.  8D8CB6 E4C000>lea     ecx, dword ptr [esi+esi*4+C0E4]  ;  ecx := esi * $5 + $C0E4;
00428B49  |.  8D844E 942600>lea     eax, dword ptr [esi+ecx*2+2694]  ;  eax := esi + ecx * $2 + $2694;
00428B50  |.  B9 10270000   mov     ecx, 2710                        ;  ecx := $2710;
00428B55  |.  D1E0          shl     eax, 1                           ;  eax := eax shl $1;
00428B57  |.  99            cdq
00428B58  |.  F7F9          idiv    ecx                              ;  edx := eax mod ecx;
00428B5A  |.  3BFA          cmp     edi, edx                         ;  edi = edx ?
00428B5C      74 0B         je      short pdf2rtf.00428B69           ;  相等便OK!
00428B5E  |.  C64424 30 04  mov     byte ptr [esp+30], 4
00428B63  |.  8D4C24 38     lea     ecx, dword ptr [esp+38]
00428B67  |.  EB 29         jmp     short pdf2rtf.00428B92           ;  //下面是第二部份的算法
00428B69  |>  8D83 FEE5FFFF lea     eax, dword ptr [ebx-1A02]        ;  eax := ebx - $1A02;
00428B6F  |.  81C3 E8080000 add     ebx, 8E8                         ;  ebx := ebx + $8E8;
00428B75  |.  99            cdq                                      ;  edx 接受 eax 的扩展
00428B76  |.  33C2          xor     eax, edx                         ;  eax := eax xor edx;
00428B78  |.  B9 10270000   mov     ecx, 2710                        ;  ecx := $2710;
00428B7D  |.  2BC2          sub     eax, edx                         ;  eax := eax - edx;
00428B7F  |.  C64424 30 04  mov     byte ptr [esp+30], 4
00428B84  |.  0FAFC3        imul    eax, ebx                         ;  eax := eax * ebx;
00428B87  |.  99            cdq                                      ;  edx 接受 eax 的扩展
00428B88  |.  F7F9          idiv    ecx                              ;  edx := eax mod ecx;
00428B8A  |.  8D4C24 38     lea     ecx, dword ptr [esp+38]
00428B8E  |.  3BEA          cmp     ebp, edx                         ;  ebp = edx ?
00428B90  |.  74 63         je      short pdf2rtf.00428BF5           ;  相等便OK!
00428B92  |>  E8 D58D0300   call    pdf2rtf.0046196C
00428B97  |.  8D4C24 14     lea     ecx, dword ptr [esp+14]
00428B9B  |.  C64424 30 03  mov     byte ptr [esp+30], 3
00428BA0  |.  E8 C78D0300   call    pdf2rtf.0046196C
『以下代码省略』
》》》》》》》》


通过上面的代码,我们清晰的知道了,两部分的算法分别为:

//第一部份外部算法
  edx := ((esi + (esi * $5 + $C0E4) * 2 + $2694) shl $1) mod $2710;  //6_9
//第二部份外部算法
  edx := ((((ebx - $1A02) xor edx) - edx) * (ebx + $8E8)) mod $2710;

然后,将两部分的结果,分别与edi和ebp的结果比较,相等,便注册成功了!




好,得到上面这些有用的信息后,现在我就把它具体算法的思路给理一下吧:

1。注册名在其中没有起到任何的作用, 程序先得到注册码,然后带入到运算中;
2。取注册码的1~4位数,带入子程序call    0044D3A0中运算,结果送到-> esi中;
3。取注册码的6~9位数,带入子程序call    0044D3A0中运算,结果送到-> edi中;
4。取注册码的11~14位数,带入子程序call    0044D3A0中运算,结果送到-> ebx中;
5。取注册码的16~19位数,带入子程序call    0044D3A0中运算,结果送到-> ebp中;
6。然后用上面的‘第一部份外部算法’,将得到的结果edx 与 第‘2’步骤中得到的结果edi比较,两者相等,便OK;
7。用上面的‘第二部份外部算法’,将得到的结果edx 与 第‘5’步骤中得到的结果ebp比较,如若相等,便成功了。




也许我无法表示清楚,还是看相关的源代码吧!转换成用 Delphi + KOL/MCK 写的代码即是如下形式:


{ KOL MCK } // Do not remove this line!
{$DEFINE KOL_MCK}
unit Main1;

interface

{$IFDEF KOL_MCK}
uses Windows, KOL {$IFNDEF KOL_MCK},
     Classes, Controls, mckCtrls, mirror {$ENDIF (place your units here->)};
{$ENDIF}

type
  {$IFDEF KOL_MCK}
  {$I MCKfakeClasses.inc}
  {$IFDEF KOLCLASSES} TForm1 = class; PForm1 = TForm1;
  {$ELSE OBJECTS} PForm1 = ^TForm1;
  {$ENDIF CLASSES/OBJECTS}
  {$IFDEF KOLCLASSES}{$I TForm1.inc}{$ELSE} TForm1 = object(TObj) {$ENDIF}
    Form: PControl;
  {$ELSE not_KOL_MCK}
  TForm1 = class(TForm)
  {$ENDIF KOL_MCK}
    KOLProject1: TKOLProject;
    KOLForm1: TKOLForm;
    GroupBox: TKOLGroupBox;
    EditName: TKOLEditBox;
    LabelName: TKOLLabel;
    LabelInfo: TKOLLabel;
    ButtonReg: TKOLButton;
    ButtonExit: TKOLButton;
    ProgressBar: TKOLProgressBar;
    procedure ButtonExitClick(Sender: PObj);
    procedure ButtonRegClick(Sender: PObj);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1 {$IFDEF KOL_MCK} : PForm1 {$ELSE} : TForm1 {$ENDIF} ;

{$IFDEF KOL_MCK}
procedure NewForm1( var Result: PForm1; AParent: PControl );
{$ENDIF}

implementation

{$IFNDEF KOL_MCK} {$R *.DFM} {$ENDIF}

{$IFDEF KOL_MCK}
{$I main_1.inc}
{$ENDIF}

//****************************************************************************//
//EasyPDFtoRTF子call算法
function EasyPDFtoRTF_call(cz: string): integer; //定义integer函数
var
  eax, ebx, edi : integer;
  Seax, Sal : string;
const
  key: array[0 .. 255] of byte =         //密文表全部数据,共256组
  ($20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,
   $20,$00,$28,$00,$28,$00,$28,$00,$28,$00,$28,$00,$20,$00,$20,$00,
   $20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,
   $20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,
   $48,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
   $10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
   $84,$00,$84,$00,$84,$00,$84,$00,$84,$00,$84,$00,$84,$00,$84,$00,
   $84,$00,$84,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
   $10,$00,$81,$00,$81,$00,$81,$00,$81,$00,$81,$00,$81,$00,$01,$00,
   $01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,
   $01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,$01,$00,
   $01,$00,$01,$00,$01,$00,$10,$00,$10,$00,$10,$00,$10,$00,$10,$00,
   $10,$00,$82,$00,$82,$00,$82,$00,$82,$00,$82,$00,$82,$00,$02,$00,
   $02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,
   $02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,$02,$00,
   $02,$00,$02,$00,$02,$00,$10,$00,$10,$00,$10,$00,$10,$00,$20,$00);  
begin
  //////// 1 ///////循环
  edi := 1;//初始化i变量

  Seax := '000000';
  Sal  := int2hex(key[ord(cz[edi]) * 2], 2);
  Seax := Seax + Sal;
  eax := hex2int('$' + Seax);
  eax := eax and $8;

  while ((eax and eax) <> 0)  do
    begin
      edi := edi + 1;

      Seax := '000000';
      Sal  := int2hex(key[ord(cz[edi]) * 2], 2);
      Seax := Seax + Sal;
      eax := hex2int('$' + Seax);
      eax := eax and $8;
    end;

  //////// 2 ///////循环
  //edi := 1;//edi应继承前面的结果,不能再赋值了!
  ebx := 0;  //初始化ebx为0

  Seax := '004A0C';
  Sal  := int2hex(key[ord(cz[edi]) * 2], 2);
  Seax := Seax + Sal;
  eax := hex2int('$' + Seax);
  eax := eax and $4;

  while ((eax and eax) <> 0)  do
    begin
      eax := ebx * $5;
      ebx := ord(cz[edi]) + eax * $2 - $30;
      edi := edi + 1;

      Seax := '004A0C';
      Sal  := int2hex(key[ord(cz[edi]) * 2], 2);
      Seax := Seax + Sal;
      eax := hex2int('$' + Seax);
      eax := eax and $4;
    end;
  result := ebx;
end;
//****************************************************************************//

//穷举6_9位数           //第一部份外部算法
function QJ6_9(code1_4 : string): string;
var
  i, pos : integer;
  cz, code6_9 : string;
begin
  for i:=1000 to 9999 do
    begin
      code6_9 := int2hex(i,0);
      if EasyPDFtoRTF_call(code6_9) = ((EasyPDFtoRTF_call(code1_4) + (EasyPDFtoRTF_call(code1_4) * $5 + $C0E4) * 2 + $2694) shl $1) mod $2710
      then Result := code6_9    //正确结果回送
      else if Form1.ProgressBar.Progress>9
           then Form1.ProgressBar.Progress:=0
           else Form1.ProgressBar.Progress := Form1.ProgressBar.Progress + 1;
    end;
end;
//****************************************************************************//

//穷举16_19位数             //第二部份外部算法
function QJ16_19(code11_14 : string): string;
var
  i, pos : integer;
  cz, code16_19 : string;
begin
  for i:=1000 to 9999 do
    begin
      code16_19 := int2hex(i,0);
      if EasyPDFtoRTF_call(code16_19) = ((((EasyPDFtoRTF_call(code11_14) - $1A02) xor $0) - $0) * (EasyPDFtoRTF_call(code11_14) + $8E8)) mod $2710
      then Result := code16_19    //正确结果回送
      else if Form1.ProgressBar.Progress>9
           then Form1.ProgressBar.Progress:=0
           else Form1.ProgressBar.Progress := Form1.ProgressBar.Progress + 1;
    end;
end;
//****************************************************************************//

//EasyPDFtoRTF注册算法
function EasyPDFtoRTF_sn(): string;//定义string函数
var
  code1_4, code6_9, code11_14, code16_19, RegCode : string;
  eax, ebx, ebp, ecx, edx, edi, esi : integer;
begin
{
//注册码的格式应为:
  RegCode := '1111-1714-6666-1568';
  esi := EasyPDFtoRTF_call(copy(RegCode, 1, 4));   //1_4
  edi := EasyPDFtoRTF_call(copy(RegCode, 6, 9));
  ebx := EasyPDFtoRTF_call(copy(RegCode, 11, 14)); //11_14
  ebp := EasyPDFtoRTF_call(copy(RegCode, 16, 19));
//第一部份外部算法
  //edx := ((esi + (esi * $5 + $C0E4) * 2 + $2694) shl $1) mod $2710;  //6_9
//第二部份外部算法
  //edx := $0;
  //edx := ((((ebx - $1A02) xor edx) - edx) * (ebx + $8E8)) mod $2710;
showmessage(int2hex(ebp,0));
showmessage(int2hex(edx,0));
}

  code1_4   := '1111';            //任意  4  位数//这里我定义的为:1111,你可自己修改,但必须满足能被穷举出来哟!
  code6_9   := QJ6_9(code1_4);    //穷举 6_9 位数
  code11_14 := '6666';            //任意  4  位数//这里我定义的为:6666,你可自己修改,但必须满足能被穷举出来哟!
  code16_19 := QJ16_19(code11_14);//穷举16_19位数
  Result := code1_4 +'-'+ code6_9 +'-'+ code11_14 +'-'+ code16_19; //最终结果

end;
//****************************************************************************//

procedure TForm1.ButtonRegClick(Sender: PObj);
begin
  EditName.Text := EasyPDFtoRTF_sn();//EasyPDFtoRTF注册算法
end;
//****************************************************************************//

procedure TForm1.ButtonExitClick(Sender: PObj);
begin
  Form.Close;
end;
//****************************************************************************//

end.




好的,就到这里结束吧!分析难免有不当之处,还望大家给我指出来!


放上几组可用的Key:

name: [任意注册名]
code: 1111-1714-6666-1568

name: [任意注册名]
code: 2020-1712-8080-1920

name: [任意注册名]
code: 2000-1272-7033-2375

name: [任意注册名]
code: 1122-1956-6665-2615

name: [任意注册名]
code: 1144-2440-7040-240F

name: [任意注册名]
code: 1111-1714-8086-2648


因为想偷懒,注册机的部分算法使用了穷举的方式,应该不存在bug的问题,附件中为我打包后的注册机源代码。

  • 标 题:答复
  • 作 者:zcg
  • 时 间:2007-05-27 11:00

也把 Easy PDF to Html Converter 的算法跟了下,发现它的子call算法没有变,联那张Key表的数据都没有变,只是变了下面这些地方:


00424D02  |.  8D8CB6 04AB00>lea     ecx, dword ptr [esi+esi*4+AB04]
00424D09  |.  8D844E 342200>lea     eax, dword ptr [esi+ecx*2+2234]
00424D10  |.  B9 10270000   mov     ecx, 2710
00424D15  |.  D1E0          shl     eax, 1
00424D17  |.  99            cdq
00424D18  |.  F7F9          idiv    ecx
00424D1A  |.  3BFA          cmp     edi, edx



00424D29  |> \8D83 CAEAFFFF lea     eax, dword ptr [ebx-1536]
00424D2F  |.  81C3 E2090000 add     ebx, 9E2
00424D35  |.  99            cdq
00424D36  |.  33C2          xor     eax, edx
00424D38  |.  B9 10270000   mov     ecx, 2710
00424D3D  |.  2BC2          sub     eax, edx
00424D3F  |.  C64424 30 04  mov     byte ptr [esp+30], 4
00424D44  |.  0FAFC3        imul    eax, ebx
00424D47  |.  99            cdq
00424D48  |.  F7F9          idiv    ecx
00424D4A  |.  8D4C24 38     lea     ecx, dword ptr [esp+38]
00424D4E  |.  3BEA          cmp     ebp, edx
00424D50  |.  74 63         je      short pdf2html.00424DB5



也既是,两部分的算法分别变为了:

;1
  edx := ((esi + (esi * $5 + $AB04) * 2 + $2234) shl $1) mod $2710;

;2
  edx := ((((ebx - $1536) xor edx) - edx) * (ebx + $9E2)) mod $2710;

也给出一组Key:

name: 随便
code: 1818-2628-8000-2100

呵呵!