【文章标题】: PDF Password Remover v3.0注册算法简单分析
【文章作者】: DarkBoxer暗夜拳师
【软件名称】: PDF Password Remover v3.0
【下载地址】: http://www.verypdf.com/pwdremover/pwdremover.exe
【使用工具】: OD
【操作平台】: WindowsXPsp2
【软件介绍】: 本软件可以破解Adobe Acrobat PDF文件高达128-bit加密的“所有者密码”。未破解“所有者密码”的PDF文件是不能被编辑和打印的。该程序也可以破解用FileOpen插件加密的文件。破解可以立即完成。破解后的文件可以用各种PDF浏览器阅读而无任何限制。
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
-----------------------------------------------------------------------------------------

【详细过程】
<背景>
今天要拷贝一个PDF文件里面的代码,不过有限制,所以就去某网站下载了一个PDF Password Remover v3.0,轻松搞定PDF。提供下载的网站给出了2个注册码,都可用,嘻嘻~反正今天还有点闲,索性分析下此软件的注册算法,结果发现是个软柿子~~^_^

<脱壳>
软件是UPX加壳,秒之~
测试试炼码:12345678901darkboxer

<分析>
0040539D            68 10B54B00     push    004BB510                         ; ASCII "12345678901darkboxer"
004053A2            E8 59F8FFFF     call    00404C00                         ; //这里就是验证试炼码是否正确
004053A7            83C4 04         add     esp, 4
004053AA            85C0            test    eax, eax
004053AC            74 44           je      short 004053F2                   ; //呵呵,爆破点
004053AE            6A 40           push    40
004053B0            68 946A4900     push    00496A94                         ; ASCII "Thank you."
004053B5            68 5C6A4900     push    00496A5C                         ; ASCII "Thanks for purchasing the PDF Password Remover v3.0."
004053BA            56              push    esi
004053BB            FF15 08844800   call    dword ptr [<&user32.MessageBoxA>>; USER32.MessageBoxA
004053C1            51              push    ecx
004053C2            8BCC            mov     ecx, esp
004053C4            896424 10       mov     dword ptr [esp+10], esp
004053C8            68 10B54B00     push    004BB510                         ; ASCII "12345678901darkboxer"
004053CD            E8 686B0700     call    0047BF3A
004053D2            E8 F9FBFFFF     call    00404FD0
004053D7            83C4 04         add     esp, 4
004053DA            C705 D8B54B00 0>mov     dword ptr [4BB5D8], 1
004053E4            6A 01           push    1
004053E6            56              push    esi
004053E7            FF15 04844800   call    dword ptr [<&user32.EndDialog>]  ; USER32.EndDialog
004053ED            E9 86020000     jmp     00405678
004053F2            6A 10           push    10
004053F4            6A 00           push    0
004053F6            68 146A4900     push    00496A14                         ; ASCII "Your registration key is wrong, please double check it and try again."
004053FB            56              push    esi
004053FC            FF15 08844800   call    dword ptr [<&user32.MessageBoxA>>; USER32.MessageBoxA

;跟进404c00
00404C00            83EC 18         sub     esp, 18
00404C03            83C9 FF         or      ecx, FFFFFFFF
00404C06            33C0            xor     eax, eax
00404C08            53              push    ebx
00404C09            56              push    esi
00404C0A            8B7424 24       mov     esi, dword ptr [esp+24]
00404C0E            57              push    edi
00404C0F            8BFE            mov     edi, esi
00404C11            F2:AE           repne   scas byte ptr es:[edi]
00404C13            F7D1            not     ecx
00404C15            49              dec     ecx
00404C16            83F9 14         cmp     ecx, 14                          ; 注册码长度必须为十20(10进制)位
00404C19            74 07           je      short 00404C22                   ; 不满足就拜拜
00404C1B            5F              pop     edi
00404C1C            5E              pop     esi
00404C1D            5B              pop     ebx
00404C1E            83C4 18         add     esp, 18
00404C21            C3              retn
00404C22            8A06            mov     al, byte ptr [esi]               ; 注册码第一位,这里是‘1’
00404C24            8A4E 01         mov     cl, byte ptr [esi+1]             ; 注册码第二位,这里是‘2’
00404C27            8D5424 0C       lea     edx, dword ptr [esp+C]           ; [edx]就是注册码12A45678903darkboxer
00404C2B            32DB            xor     bl, bl                           ; bl清零
00404C2D            52              push    edx
00404C2E            884424 1C       mov     byte ptr [esp+1C], al
00404C32            885C24 1D       mov     byte ptr [esp+1D], bl
00404C36            884C24 10       mov     byte ptr [esp+10], cl
00404C3A            885C24 11       mov     byte ptr [esp+11], bl
00404C3E            E8 AD090600     call    004655F0                         ; //关键点,多次被调用
00404C43            8BF8            mov     edi, eax                         ; //由第2位得出的结果->edi
00404C45            8D4424 1C       lea     eax, dword ptr [esp+1C]
00404C49            50              push    eax
00404C4A            E8 A1090600     call    004655F0
00404C4F            03F8            add     edi, eax                         ; //第2位得出的结果和第2位得出的结果相加
00404C51            83C4 08         add     esp, 8
00404C54            83FF 0A         cmp     edi, 0A                          ; //是否等于0Ah
00404C57            74 09           je      short 00404C62
00404C59            5F              pop     edi
00404C5A            5E              pop     esi
00404C5B            33C0            xor     eax, eax
00404C5D            5B              pop     ebx
00404C5E            83C4 18         add     esp, 18
00404C61            C3              retn
00404C62            8A4E 12         mov     cl, byte ptr [esi+12]            ; //注册码第19位
00404C65            8A56 13         mov     dl, byte ptr [esi+13]            ; //注册码第20位
00404C68            8D4424 0C       lea     eax, dword ptr [esp+C]
00404C6C            884C24 18       mov     byte ptr [esp+18], cl
00404C70            50              push    eax
00404C71            885C24 1D       mov     byte ptr [esp+1D], bl
00404C75            885424 10       mov     byte ptr [esp+10], dl
00404C79            885C24 11       mov     byte ptr [esp+11], bl
00404C7D            E8 6E090600     call    004655F0
00404C82            8D4C24 1C       lea     ecx, dword ptr [esp+1C]
00404C86            8BF8            mov     edi, eax                         ; //第20位得出的结果->edi
00404C88            51              push    ecx
00404C89            E8 62090600     call    004655F0
00404C8E            03F8            add     edi, eax                         ; //第19位得出的结果和第20位得出的结果相加
00404C90            83C4 08         add     esp, 8
00404C93            83FF 0C         cmp     edi, 0C                          ; //是否等于0Ch
00404C96            74 09           je      short 00404CA1
00404C98            5F              pop     edi
00404C99            5E              pop     esi
00404C9A            33C0            xor     eax, eax
00404C9C            5B              pop     ebx
00404C9D            83C4 18         add     esp, 18
00404CA0            C3              retn
00404CA1            8A56 05         mov     dl, byte ptr [esi+5]             ; //注册码第6位
00404CA4            8A46 0D         mov     al, byte ptr [esi+D]             ; //注册码第14位
00404CA7            8D4C24 0C       lea     ecx, dword ptr [esp+C]
00404CAB            885424 18       mov     byte ptr [esp+18], dl
00404CAF            51              push    ecx
00404CB0            885C24 1D       mov     byte ptr [esp+1D], bl
00404CB4            884424 10       mov     byte ptr [esp+10], al
00404CB8            885C24 11       mov     byte ptr [esp+11], bl
00404CBC            E8 2F090600     call    004655F0
00404CC1            8D5424 1C       lea     edx, dword ptr [esp+1C]
00404CC5            8BF8            mov     edi, eax                         ; //第14位得出的结果->edi
00404CC7            52              push    edx
00404CC8            E8 23090600     call    004655F0
00404CCD            03F8            add     edi, eax                         ; //第6位得出的结果和第14位得出的结果相加
00404CCF            83C4 08         add     esp, 8
00404CD2            83FF 0B         cmp     edi, 0B                          ; //是否等于0Bh
00404CD5            74 09           je      short 00404CE0
00404CD7            5F              pop     edi
00404CD8            5E              pop     esi
00404CD9            33C0            xor     eax, eax
00404CDB            5B              pop     ebx
00404CDC            83C4 18         add     esp, 18
00404CDF            C3              retn
00404CE0            807E 0C 52      cmp     byte ptr [esi+C], 52             ; //第13位是否等于52h,即‘R’
00404CE4            74 09           je      short 00404CEF
00404CE6            5F              pop     edi
00404CE7            5E              pop     esi
00404CE8            33C0            xor     eax, eax
00404CEA            5B              pop     ebx
00404CEB            83C4 18         add     esp, 18
00404CEE            C3              retn
00404CEF            807E 0E 33      cmp     byte ptr [esi+E], 33             ; //第15位是否等于33h,即‘3’
00404CF3            74 09           je      short 00404CFE
00404CF5            5F              pop     edi
00404CF6            5E              pop     esi
00404CF7            33C0            xor     eax, eax
00404CF9            5B              pop     ebx
00404CFA            83C4 18         add     esp, 18
00404CFD            C3              retn
00404CFE            8A4E 0F         mov     cl, byte ptr [esi+F]             ; //第16位->cl
00404D01            33C0            xor     eax, eax
00404D03            80F9 30         cmp     cl, 30                           ; //第16位是否等于30h,即0
00404D06            5F              pop     edi
00404D07            5E              pop     esi
00404D08            5B              pop     ebx
00404D09            0F94C0          sete    al                               ; //等于则置al为1
00404D0C            83C4 18         add     esp, 18
00404D0F            C3              retn

在分析的过程中,发现被程序检测的第19,20,14位填的字母,在调用4655f0后得出的结果都是0,反正在吃粉丝,嘻嘻,不好意思,分析的时候肚子饿了,干脆就悠哉悠哉的多测试了几组数据,发现注册码0~9之间,函数返回的就是0~9,而输入字母,则统统为0,嘻嘻,颇有点选择明文攻击的味道。加上看到原先下载的网站给出的注册码是通用的时候,我就已经怀疑机器ID是唬人的,再加上,注册算法中确实没看到机器ID的出现,莫非我眼花了。于是乎我就猜测它的注册算法:
1.长度必须等于20(十进制)位
2.第1位和第2位的和必须等于A
3.第19位和第20位的和必须等于C
4.第6位和第14位必须等于B
5.第13位必须等于52h,即‘R’
6.第15位必须等于33h,即‘3’
7.第16位必须等于30h,即‘0’
OK!胡乱测试一组试炼码:55@@@5@@@@@@R630@@66 
my god;差点喷面,居然成功了....GS运~~~嘻嘻~

<再分析>
现在的方面粉丝的份量真的太少了,没几下就被我解决了-_-!自己其实也想看看4655f0函数是咋样的,所以索性接着看了下
;4655f0
004655F0            FF7424 04       push    dword ptr [esp+4]                ; //需要被计算的那位注册码,这里是第2位‘2’
004655F4            E8 6CFFFFFF     call    00465565                         ; //继续跟进看了
004655F9            59              pop     ecx
004655FA            C3              retn

;465565
00465565            53              push    ebx
00465566            55              push    ebp
00465567            56              push    esi
00465568            57              push    edi
00465569            8B7C24 14       mov     edi, dword ptr [esp+14]                   ; //edi存放被压入的那位注册码'2'
0046556D            833D DCA84B00 0>cmp     dword ptr [4BA8DC], 1
00465574            7E 0F           jle     short 00465585
00465576            0FB607          movzx   eax, byte ptr [edi]
00465579            6A 08           push    8
0046557B            50              push    eax
0046557C            E8 A35E0000     call    0046B424
00465581            59              pop     ecx
00465582            59              pop     ecx
00465583            EB 0F           jmp     short 00465594
00465585            0FB607          movzx   eax, byte ptr [edi]
00465588            8B0D D0A64B00   mov     ecx, dword ptr [4BA6D0]                   ; //ds:[004BA6D0]=004BA6DA
0046558E            8A0441          mov     al, byte ptr [ecx+eax*2]                  ; //al<-84
00465591            83E0 08         and     eax, 8                                    ; //和8与
00465594            85C0            test    eax, eax                                  ; //是否为0
00465596            74 03           je      short 0046559B
00465598            47              inc     edi
00465599          ^ EB D2           jmp     short 0046556D
0046559B            0FB637          movzx   esi, byte ptr [edi]                       ; //esi<-'2'
0046559E            47              inc     edi                                       ; //[edi]为0,参考前面地址404c32
0046559F            83FE 2D         cmp     esi, 2D                                   ; //是否等于‘-’
004655A2            8BEE            mov     ebp, esi
004655A4            74 05           je      short 004655AB
004655A6            83FE 2B         cmp     esi, 2B                                   ; //是否等于‘+’
004655A9            75 04           jnz     short 004655AF
004655AB            0FB637          movzx   esi, byte ptr [edi]
004655AE            47              inc     edi
004655AF            33DB            xor     ebx, ebx
004655B1            833D DCA84B00 0>cmp     dword ptr [4BA8DC], 1
004655B8            7E 0C           jle     short 004655C6
004655BA            6A 04           push    4
004655BC            56              push    esi
004655BD            E8 625E0000     call    0046B424
004655C2            59              pop     ecx
004655C3            59              pop     ecx
004655C4            EB 0B           jmp     short 004655D1                            ; //下面才是关键
004655C6            A1 D0A64B00     mov     eax, dword ptr [4BA6D0]                   ; //ds:[004BA6D0]=004BA6DA
004655CB            8A0470          mov     al, byte ptr [eax+esi*2]                  ; //al<-84,esi是注册码32h
004655CE            83E0 04         and     eax, 4                                    ; //和4相与
004655D1            85C0            test    eax, eax                                  ; //是否为0
004655D3            74 0D           je      short 004655E2
004655D5            8D049B          lea     eax, dword ptr [ebx+ebx*4]
004655D8            8D5C46 D0       lea     ebx, dword ptr [esi+eax*2-30]             ; //ebx指向esi+eax*2-30的地方,而这个地方的值是:该位注册码+eax*2-30,而eax为0,所以为:注册码-30
004655DC            0FB637          movzx   esi, byte ptr [edi]
004655DF            47              inc     edi
004655E0          ^ EB CF           jmp     short 004655B1
004655E2            83FD 2D         cmp     ebp, 2D
004655E5            8BC3            mov     eax, ebx
004655E7            75 02           jnz     short 004655EB
004655E9            F7D8            neg     eax
004655EB            5F              pop     edi
004655EC            5E              pop     esi
004655ED            5D              pop     ebp
004655EE            5B              pop     ebx
004655EF            C3              retn

OK!关键的地方其实就在004655C6~004655D8之间,必须保证和4相与后的eax不为0,按这条流程下去,才会保证返回着是(注册码-30),否则返回值为0
而eax的保证来源于内存4ba6da,即如下数组:
char eax_key[] = 
{
0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x28, 0x00, 
0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x20, 0x00, 
0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 
0x20, 0x00, 0x20, 0x00, 0x48, 0x00, 0x10, 0x00, 0x10, 0x00, 
0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 
0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 
0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x84, 0x00, 0x84, 0x00, 
0x84, 0x00, 0x84, 0x00, 0x84, 0x00, 0x84, 0x00, 0x84, 0x00, 
0x84, 0x00, 0x84, 0x00, 0x84, 0x00, 0x10, 0x00, 0x10, 0x00, 
0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 
0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 
0x81, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 
0x01, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 
0x10, 0x00, 0x10, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 
0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x02, 0x00, 0x02, 0x00, 
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, 0x10, 0x00, 
0x10, 0x00, 0x10, 0x00, 0x20
};
如004655CB            8A0470          mov     al, byte ptr [eax+esi*2]                  ; //al<-84,esi是注册码32h
al就是eax_key[eax+esi*2],eax是004BA6DA=数组基址0,esi为该位注册码‘2’,即32h*2=64h=100,所以为eax_key[100] = 0x84; 0x84^4 = 4;
在来一个‘A’,16进制41,41h*2=82H=130,所以eax_key[130] = 0x81; 0x81^4=0
只有为0x84,和4与才不会为0,而0x84正好是10个,正好是0~9十个即数组96开始,因为乘2,正好相隔一个,自己算算吧~~
程序就是这样来判断的。。
<注册机>
呵呵,不用了吧。手动计算貌似还更快,呵呵~今天捡到个软柿子~~运气不错^_^
----------------------------------------------------------------------------------------------

【版权声明】: 
本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!


                                                       2007年07月22日 20:17:02