首页
社区
课程
招聘
[原创][分享]VeryPDF PDF2Word v3.0 算法分析详细过程以及注册机汇编源码关键部分
发表于: 2011-5-20 16:40 2749

[原创][分享]VeryPDF PDF2Word v3.0 算法分析详细过程以及注册机汇编源码关键部分

2011-5-20 16:40
2749
对于本软件注册算法的分析坛友已经发布过,本来没必要再行发帖,不过本人所写的分析更为详细,对于入门级的爱好者或许是一篇不错的引领入门的帖子。

【破文标题】VeryPDF PDF2Word v3.0 算法分析详细过程+注册机汇编源码关键部分
【破文作者】网络浪子(netprodiag)
【作者邮箱】netprodiag@163.com
【破解平台】Win7 SP1
【破解工具】PEiD0.95、OllyICE
【作者邮箱】piaoyun04@163.com
【软件名称】VeryPDF PDF Editor v3.0
【原版下载】http://dl.verypdf.net/pdf2word.exe
一、准备工作
用 PEiD v0.95 査壳,显示 UPX 0.89.6 - 1.02 / 1.05 - 2.90 -> Markus & Laszlo,用 Pexplorer 打开后直接保存就可以完美脱壳。再次用 PEiD v0.95 査壳,显示 Microsoft Visual C++ 6.0,查

看节段以及用 OllyICE 载入,确定再无其它壳。启动脱壳后的程序,提示输入 E-mail 以及 Registration Key,任意输入符合 E-mail 格式的 E-mail 以及注册码,弹出“Your registration key is

wrong……”,既然有错误提示的对话框,可以采用“查找所有参考的字符串法、F12 暂停调用栈法以及对创建对话框和消息框相关的下断点的方法,至于选择哪一种取决于要看哪种能适用并快速定位关键处以

及你能适用的前提下个人的喜好而定。
二、定位关键 call

1、用 OllyICE 载入程序,输入试炼注册信息,我这里采用 F12 暂停调用栈法然后采用回溯定位关键处:

00406676   > \8BB424 E80000>mov     esi, dword ptr [esp+E8]          ;  Case 1 of switch 004065D8
0040667D   .  68 C8000000   push    0C8                              ; /Count = C8 (200.)
00406682   .  B9 32000000   mov     ecx, 32                          ; |
00406687   .  33C0          xor     eax, eax                         ; |
00406689   .  BF F0617F00   mov     edi, 007F61F0                    ; |ASCII "cracker987654321"
0040668E   .  68 F0617F00   push    007F61F0                         ; |Buffer = pdf2word.007F61F0
00406693   .  68 FB030000   push    3FB                              ; |ControlID = 3FB (1019.)
00406698   .  56            push    esi                              ; |hWnd = 00B55F00
00406699   .  F3:AB         rep     stos dword ptr es:[edi]          ; |
0040669B   .  FF15 F8855B00 call    dword ptr [<&USER32.GetDlgItemTe>; \GetDlgItemTextA
004066A1   .  68 F0617F00   push    007F61F0                         ;  ASCII "cracker987654321"
004066A6   .  E8 95F8FFFF   call    00405F40
004066AB   .  83C4 04       add     esp, 4
004066AE   .  85C0          test    eax, eax
004066B0   .  74 44         je      short 004066F6
004066B2   .  6A 40         push    40                               ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
004066B4   .  68 B42A5E00   push    005E2AB4                         ; |Title = "Thank you."
004066B9   .  68 882A5E00   push    005E2A88                         ; |Text = "Thank you registered VeryPDF PDF2Word v3.0."
004066BE   .  56            push    esi                              ; |hOwner = 00B55F00

由上面的代码片段分析可知,在地址 0040669B 通过调用 API 函数 GetDlgItemTextA 获取输入的注册信息,仅此一处获取输入的试炼注册码,说明本程序的注册验证仅与注册码有关,与 E-mail 无关

,即只要注册码正确,E-mail 可以任意。再看地址 004066B9 处有提示注册成功的字符串信息“Thank you registered VeryPDF PDF2Word v3.0.”以及综合其上面地址 004066B0 处得跳转指令 je 可

知,这是一个关键的跳转,其上一句地址 004066AE 处的指令为 test eax,eax,说明 下一句跳转指令 je 是根据 eax 的值来判断,那 eax 的值来自哪里,就是通过地址 004066A6 处的指令 call

00405F40 来返回 eax 的值,最后看地址 00406689 处的指令 push 007F61F0(虚拟内存地址 007F61F0 就是 GetDlgItemTextA 函数获取注册窗口中静态文本框 Registration Key 所对应的注册码编辑

框内容的存放地址,而又在地址 004066A1 处使用 push 007F61F0 紧接着使用指令 call 00405F40,即存放获取的注册码的内容的内存地址 007F61F0 是作为注册验证过程的参数传入的),由此可知,
地址 004066A6 处的指令 call 00405F40 是注册码的验证 call,至此,我们已经确定了 call 00405F40 是关键的 call.
2、注册码验证分析
经过上面 “1”的分析,我们用 F7 单步进入验证 call 分析其算法

00405F40  /$  83EC 18       sub     esp, 18                    ;  验证算法入口,预留栈空间 4*24(0x18=24)=96 字节,因为 Win32 程序的栈结构决定单位大小为 4 字节。
00405F43  |.  83C9 FF       or      ecx, FFFFFFFF              ;  ecx=-1(典型的通过置 ecx=-1 并结合 scas 指令计算字串长度的指令片段法)
00405F46  |.  33C0          xor     eax, eax                   ;  eax=0
00405F48  |.  53            push    ebx
00405F49  |.  56            push    esi
00405F4A  |.  8B7424 24     mov     esi, dword ptr [esp+24]    ;  输入注册码的首址,即通过代码片段“1”中地址 004066A1 处的指令 push 007F61F0 传入的参数
00405F4E  |.  57            push    edi                        ;  pdf2word.007F62B8
00405F4F  |.  8BFE          mov     edi, esi                   ;  输入注册码的首址赋给 edi,结果 edi 也指向了输入的注册码的缓冲区首址
00405F51  |.  F2:AE         repne   scas byte ptr es:[edi]     ;  当 edi 指向的内存中的字符串尚未结束时,因为 scas 的作用是取出 edi 指向的字符串的一个字符并与 eax 比较,,如果与

eax 的值不等,则继续检测下一个字符,由于通过上面的 xor  eax,eax,已经将 eax 清零,所以相当于检测字符串是否结束
00405F53  |.  F7D1          not     ecx
00405F55  |.  49            dec     ecx                        ;  求出输入注册码的长度,上面的指令段就是计算字串长度的一种方法
00405F56  |.  83F9 14       cmp     ecx, 14                    ;  与0x14,即与十进制的 20 比较
00405F59  |.  74 07         je      short 00405F62             ;  不跳则注册失败,说明注册码长度必须为 20 位
00405F5B  |.  5F            pop     edi                        ;  pdf2word.007F62B8
00405F5C  |.  5E            pop     esi
00405F5D  |.  5B            pop     ebx
00405F5E  |.  83C4 18       add     esp, 18
00405F61  |.  C3            retn
00405F62  |>  8A06          mov     al, byte ptr [esi]         ;  取 Key[0] 到 al 中
00405F64  |.  8A4E 01       mov     cl, byte ptr [esi+1]       ;  取 Key[1] 到 cl 中
00405F67  |.  8D5424 0C     lea     edx, dword ptr [esp+C]
00405F6B  |.  32DB          xor     bl, bl
00405F6D  |.  52            push    edx
00405F6E  |.  884424 1C     mov     byte ptr [esp+1C], al
00405F72  |.  885C24 1D     mov     byte ptr [esp+1D], bl
00405F76  |.  884C24 10     mov     byte ptr [esp+10], cl
00405F7A  |.  885C24 11     mov     byte ptr [esp+11], bl
00405F7E  |.  E8 C4A61800   call    00590647
00405F83  |.  8BF8          mov     edi, eax
00405F85  |.  8D4424 1C     lea     eax, dword ptr [esp+1C]
00405F89  |.  50            push    eax
00405F8A  |.  E8 B8A61800   call    00590647                  
00405F8F  |.  03F8          add     edi, eax                           ;  edi=key[0]+Key[1]
00405F91  |.  83C4 08       add     esp, 8
00405F94  |.  83FF 0B       cmp     edi, 0B                    ;  key[0]+Key[1]=11,0xB=11
00405F97  |.  74 09         je      short 00405FA2
00405F99  |.  5F            pop     edi                        ;  pdf2word.007F62B8
00405F9A  |.  5E            pop     esi
00405F9B  |.  33C0          xor     eax, eax
00405F9D  |.  5B            pop     ebx
00405F9E  |.  83C4 18       add     esp, 18
00405FA1  |.  C3            retn
00405FA2  |>  8A4E 12       mov     cl, byte ptr [esi+12]      ;  cl=Key[18],0x12=18
00405FA5  |.  8A56 13       mov     dl, byte ptr [esi+13]      ;  dl=Key[19],0x13=19
00405FA8  |.  8D4424 0C     lea     eax, dword ptr [esp+C]
00405FAC  |.  884C24 18     mov     byte ptr [esp+18], cl
00405FB0  |.  50            push    eax
00405FB1  |.  885C24 1D     mov     byte ptr [esp+1D], bl
00405FB5  |.  885424 10     mov     byte ptr [esp+10], dl
00405FB9  |.  885C24 11     mov     byte ptr [esp+11], bl
00405FBD  |.  E8 85A61800   call    00590647
00405FC2  |.  8D4C24 1C     lea     ecx, dword ptr [esp+1C]
00405FC6  |.  8BF8          mov     edi, eax
00405FC8  |.  51            push    ecx
00405FC9  |.  E8 79A61800   call    00590647                  
00405FCE  |.  03F8          add     edi, eax                           ;  edi=Key[18]+Key[19]
00405FD0  |.  83C4 08       add     esp, 8
00405FD3  |.  83FF 0D       cmp     edi, 0D                    ;  key[18]+Key[19]=13,0xD=13
00405FD6  |.  74 09         je      short 00405FE1
00405FD8  |.  5F            pop     edi                        ;  pdf2word.007F62B8
00405FD9  |.  5E            pop     esi
00405FDA  |.  33C0          xor     eax, eax
00405FDC  |.  5B            pop     ebx
00405FDD  |.  83C4 18       add     esp, 18
00405FE0  |.  C3            retn
00405FE1  |>  8A56 05       mov     dl, byte ptr [esi+5]       ;  dl=Key[5]
00405FE4  |.  8A46 0D       mov     al, byte ptr [esi+D]       ;  al=Key[13]
00405FE7  |.  8D4C24 0C     lea     ecx, dword ptr [esp+C]
00405FEB  |.  885424 18     mov     byte ptr [esp+18], dl
00405FEF  |.  51            push    ecx
00405FF0  |.  885C24 1D     mov     byte ptr [esp+1D], bl
00405FF4  |.  884424 10     mov     byte ptr [esp+10], al
00405FF8  |.  885C24 11     mov     byte ptr [esp+11], bl
00405FFC  |.  E8 46A61800   call    00590647
00406001  |.  8D5424 1C     lea     edx, dword ptr [esp+1C]
00406005  |.  8BF8          mov     edi, eax
00406007  |.  52            push    edx
00406008  |.  E8 3AA61800   call    00590647                  
0040600D  |.  03F8          add     edi, eax                   ;  edi=Key[5]+Key[13]
0040600F  |.  83C4 08       add     esp, 8
00406012  |.  83FF 09       cmp     edi, 9                     ;  Key[5]+Key[13]=9
00406015  |.  74 09         je      short 00406020
00406017  |.  5F            pop     edi                        ;  pdf2word.007F62B8
00406018  |.  5E            pop     esi
00406019  |.  33C0          xor     eax, eax
0040601B  |.  5B            pop     ebx
0040601C  |.  83C4 18       add     esp, 18
0040601F  |.  C3            retn
00406020  |>  807E 0C 56    cmp     byte ptr [esi+C], 56       ;  Key[12]='V',大写的字母 V,因为 56 表示字符时代表大写的字母 V
00406024  |.  74 09         je      short 0040602F
00406026  |.  5F            pop     edi                        ;  pdf2word.007F62B8
00406027  |.  5E            pop     esi
00406028  |.  33C0          xor     eax, eax
0040602A  |.  5B            pop     ebx
0040602B  |.  83C4 18       add     esp, 18
0040602E  |.  C3            retn
0040602F  |>  807E 0E 33    cmp     byte ptr [esi+E], 33       ;  Key[14]='3',因为 33 表示字符时代表数字 3
00406033  |.  74 09         je      short 0040603E
00406035  |.  5F            pop     edi                        ;  pdf2word.007F62B8
00406036  |.  5E            pop     esi
00406037  |.  33C0          xor     eax, eax
00406039  |.  5B            pop     ebx
0040603A  |.  83C4 18       add     esp, 18
0040603D  |.  C3            retn
0040603E  |>  8A4E 0F       mov     cl, byte ptr [esi+F]       ;  cl=Key[15]
00406041  |.  33C0          xor     eax, eax
00406043  |.  80F9 31       cmp     cl, 31                     ;  Key[15]='1'
00406046  |.  5F            pop     edi                        ;  pdf2word.007F62B8
00406047  |.  5E            pop     esi
00406048  |.  5B            pop     ebx
00406049  |.  0F94C0        sete    al                         ;  如果满足以上条件则将 al 的值置为 1
0040604C  |.  83C4 18       add     esp, 18
0040604F  \.  C3            retn                               ;  验证完毕返回 al 的值来确定跳转到注册成功或失败的流程处理模块处

【算法总结】
注册码长度必须为 20 位并满足以下条件:
1、key[0]+Key[1]=11
2、key[18]+Key[19]=13
3、Key[5]+Key[13]=9
4、Key[12]='V'
5、Key[14]='3'
6、Key[15]='1'
6.其他位为任意字符

注册成功会在安装文件夹下的 config.ini 中写入 Regcode=正确的注册码字符串,如果想重新注册可以删除 Regcode 那行或删除=后正确的注册码字符串;如果想以试用模式使用并无限次使用试用模式,

只需在 config.ini 中写入一行,Used=0,=后面的数表示已试用的次数,最大试用次数为 100。

【注册机汇编源码】
.const

MAXSIZE                equ 20

.data

szFormat           db  '%s',0 ;注册码输出格式为字符串格式
szConstTable          db  '0123456789abcdefghijklmnopqrstuvwxyz',0 ;限定注册码中可以使用的字符
RegCodeBuffer   db MAXSIZE dup(0),0 ;存放注册码缓冲区,20 位注册码再加上一个字符串结束符 0

;##########定义结构开始##################
_LARGE_INTEGER    struct
        LowPart                dd ?
        HighPart        dd ?
_LARGE_INTEGER    ends
;##########定义结构结束##################

.code

GenRandProc proc uses ecx edx First:DWORD,Second:DWORD
Local    c1:_LARGE_INTEGER,c2:_LARGE_INTEGER
;rdtsc ;(多核处理器中已经不准确,因此不再使用)
;invoke GetTickCount        ; 取得随机数种子,当然,可用别的方法代替
invoke    QueryPerformanceCounter,addr c1
mov    eax,c2.LowPart
sub    eax,c1.LowPart
mov    edx,c2.HighPart
sbb    edx,c1.HighPart
sub eax,edx ;eax 中返回随机数种子
mov ecx,23
mul ecx
add eax,7
mov ecx,Second
sub ecx,First
inc ecx
xor edx,edx
div ecx
add edx,First
mov eax,edx
ret
GenRandProc endp

;=================================================
;注册算法:

注册码长度必须为 20 位并满足以下条件:
1、key[0]+Key[1]=11
2、key[18]+Key[19]=13
3、Key[5]+Key[13]=9
4、Key[12]='V'
5、Key[14]='3'
6、Key[15]='1'
6.其他位为任意字符

;代码编写:网络浪子(netprodiag@163.com)
;=================================================
KeyGenProc proc

LOCAL @szKey[MAXSIZE+1]:BYTE;多定义一个字节空间,确保在注册码缓冲区中最后插入一个结束符 0,~ 否则出问题
LOCAL @szOut[MAXSIZE+1]:BYTE

invoke RtlZeroMemory,addr @szOut,sizeof @szOut ;使用局部变量前一定要先初始化后再使用,否则出现意想不到的严重后果。通过 RtlZeroMemory API 函数将局部变量指向的内存域清空为零
invoke RtlZeroMemory,addr @szKey,sizeof @szKey ;相当于将局部变量初始化为零

xor  eax,eax
xor  ecx,ecx
xor  edx,edx

;KEY[0]
invoke _Rand,2,9
movzx edx,byte ptr [szConstTable+eax]
mov  byte ptr[@szKey + 0],dl
;计算KEY[1]
mov     edx,11
sub  edx,eax
movzx edx,byte ptr [szConstTable+edx]
mov  byte ptr[@szKey + 1],dl

;KEY[18]
invoke _Rand,4,9
movzx edx,byte ptr [szConstTable+eax]
mov  byte ptr[@szKey + 18],dl
;计算KEY[19]
mov  edx,13
sub  edx,eax
movzx edx,byte ptr [szConstTable+edx]
mov  byte ptr[@szKey + 19],dl

;KEY[5]
invoke _Rand,0,9
movzx edx,byte ptr [szConstTable+eax]
mov  byte ptr[@szKey + 5],dl
;计算KEY[13]
mov  edx,9
sub  edx,eax
movzx edx,byte ptr [szConstTable+edx]
mov  byte ptr[@szKey + 13],dl

;计算 Key[12]、Key[14]、Key[15]
mov  byte ptr[@szKey + 12],50h ;'V'
mov  byte ptr[@szKey + 14],44h ;'3'
mov  byte ptr[@szKey + 15],46h ;'1'

;计算 Key[3]、Key[4]、Key[6]、Key[7]、Key[8]、Key[9]、Key[10]、Key[11]、Key[16]、Key[17]
mov  ecx,2 ;直接通过 ecx=2 排除掉 Key[0]、Key[1]
LConcalculate:
invoke _Rand,0,sizeof szConstTable-2;sizeof szConstTable 得到 szConstTable 的字节大小为 21(包含字符串定义结束符 0),然后-2得到 szConstTable 数组的最大索引号为 19
movzx edx,byte ptr [szConstTable+eax] ;本身应该通过求字符串长度的方法来确定索引号,但由于数组 szConstTable 被定义为 db 类型,因此字符串长度与字节数一致,所以可以替代。
;这里采用排除法来间接计算上面注释中提及的 Key 组,即排除掉 Key 中索引号对应的位置字符为非任意字符的情形,其它位置用 szConstTable 数据中的随机获取的字符填充。
cmp edx,5 ;如果为 key[5],即注册码中的第 6 个位置,则直接跳过继续下一位
je @f
cmp edx,12
je @f
cmp edx,13
je @f
cmp edx,14
je @f
cmp edx,15
je @f
cmp edx,18
je @f
cmp edx,19
je @f
mov  byte ptr[@szKey + ecx],dl
@@:
inc  ecx
cmp  ecx,21
jl  LConcalculate ;如果 Key 位尚未填充完则跳回继续填充

invoke lstrcat,addr @szOut,addr @szKey  ;连接字符串
lea  eax,@szOut
invoke wsprintf,addr RegCodeBuffer,addr szFormat,eax ;格式化注册码字符串
lea eax,RegCodeBuffer
ret

KeyGenProc endp

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 136
活跃值: (105)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
2
b不错适合新手练习啊
2011-5-20 17:18
0
游客
登录 | 注册 方可回帖
返回
//