• 标 题:Advanced PDF Password Recovery Pro 2.12的不完美破解 (12千字)
  • 作 者:heXer
  • 时 间:2003-5-20 21:58:04
  • 链 接:http://bbs.pediy.com

Advanced PDF Password Recovery Pro 2.12的不完美破解

当时破解这个东西完全是出于自己的需要,为了把一篇只读的PDF文档另存为RTF文档进行编辑,
这篇破文感觉很不好写,我怎么安排都觉得乱糟糟,来回修改了几次,我只能做到这个效果了,
有些地方纯属猜测,有兴趣的慢慢看吧,看得头晕了我不负责,我写得自己都头晕了。
*************************************************************************************
target    : Advanced PDF Password Recovery Pro 2.12
homepage  : http://www.elcomsoft.com/apdfpr.html
exefile  : apdfprp.exe 267,264 bytes
Protecter : ASProtect v1.2?

Unregistered version limitations:(引用官方原文)
?1.with a brute-force attack, passwords longer than 4 characters cannot be recovered
?2.some dictionary attack options are disabled
?3.the program decrypts only first 10% pages (but at least one page) of the documents, and replaces all other pages with blank (empty) ones

第一步当然要脱壳了,我用的是AsprStripperXP_v123
脱完后不能执行,原因是OEP不正确,如何恢复OEP,就本例而言,我采用的是比较简单的方法:
先查看一下脱壳后的文件可以发现它是用Watcom32 C/C++编译的,
用16进制编辑器QIVEW打开,搜索'WATCOM',只找到一处在RVA=41B41Dh
然后稍往前看几个字节RVA=41B414h处是:
0041B414:E9 57 0C 00 00 =======> JMP 41C070
0041B419:?? ?? ?? ??
0041B41D:'WATCOM C/C++32'
我断定这里就是OEP=41B414,把脱壳后的文件的OEP恢复后果然可以执行了
不知用Watcom32 C/C++编译的可执行文件是否都是这样的,有待考证

================================================================================
下面是注册验证子过程,程序中多次调用这个子程序进行检验:
00409747                enter  40h, 0
0040974B                lea    eax, [ebp+var_40]
0040974E                call    sub_409687        ;读取注册码
00409753                lea    eax, [ebp+var_40]
00409756                call    sub_4095C6        ;注册码验证算法
0040975B                leave
0040975C                retn
================================================================================
注册码验证算法:
004095C6 sub_4095C6      proc near
004095C6 var_6C          = byte ptr -6Ch
004095C6 var_10          = byte ptr -10h
004095C6                push    ebx
004095C7                push    ecx
004095C8                push    edx
004095C9                push    esi
004095CA                push    edi
004095CB                enter  6Ch, 0
004095CF                mov    esi, eax            ;注册码指针
004095D1                call    sub_41B260        ;取长度LEN
004095D6                mov    ecx, eax
004095D8                cmp    eax, 6            ;LEN>=6?
004095DB                jge    short loc_4095E4
004095DD loc_4095DD:    xor    eax, eax            ;注册码错误
004095DF                jmp    locret_409680
004095E4 loc_4095E4:    mov    ebx, 2
004095E9                cdq
004095EA                idiv    ebx
004095EC                test    edx, edx            ;注册码长度是偶数位吗?
004095EE                jnz    short loc_4095DD
004095F0                mov    eax, ecx
004095F2                cdq
004095F3                idiv    ebx
004095F5                lea    ebx, [eax-2]        ;LEN/2-2
004095F8                lea    eax, [ebp+var_6C]
004095FB                call    sub_40F9BC        ;初始化四个32位链接变量
00409600                lea    edi, [esi+4]        ;从注册码的第5位开始
00409603                mov    edx, edi
00409605                lea    eax, [ebp+var_6C]
00409608                call    sub_40FA0D        ;填充,使消息长度恰好是512位的整数倍
0040960D                lea    edx, [ebp+var_6C]    ;
00409610                lea    eax, [ebp+var_10]    ;
00409613                call    sub_40FE18        ;MD5算法的主循环,从第5位开始,长度为LEN/2-2
00409618                mov    ebx, 10h
0040961D                mov    edx, offset unk_44FD00    ;16字节常数
00409622                lea    eax, [ebp+var_10]    ;
00409625                call    sub_41B4E0        ;比较相等?
0040962A                test    eax, eax
0040962C                jnz    short loc_4095DD
0040962E                lea    eax, [ebp+var_6C]
00409631                call    sub_40F9BC        ;初始化四个32位链接变量
00409636                mov    ebx, ecx            ;LEN
00409638                mov    edx, esi            ;从注册码第1位开始
0040963A                lea    eax, [ebp+var_6C]
0040963D                call    sub_40FA0D        ;填充,使消息长度恰好是512位的整数倍
00409642                lea    edx, [ebp+var_6C]    ;
00409645                lea    eax, [ebp+var_10]    ;
00409648                call    sub_40FE18        ;MD5算法的主循环,对整个注册码运算
0040964D                lea    eax, [ebp+var_10]    ;
00409650                call    sub_409575        ;检查注册会员列表,你是否是其中的一位会员?
00409655                test    eax, eax            ;如果你是已注册会员,返回EAX!=0
00409657                jz      short locret_409680
00409659                cmp    ds:dword_45A8F0, 0    ;是此次运行后第一次通过验证吗
00409660                jnz    short loc_40967B
00409662                push    10h            ;是第一次通过验证
00409664                push    edi
00409665                call    ds:off_44FD18        ;刷新注册表
0040966B                call    ds:off_44FD10        ;注意这行,有后话!!!
00409671                mov    ds:dword_45A8F0, 1    ;置已验证过标志
0040967B loc_40967B:    mov    eax, 1            ;是注册用户
00409680 locret_409680:  leave
00409681                pop    edi
00409682                pop    esi
00409683                pop    edx
00409684                pop    ecx
00409685                pop    ebx
00409686                retn
00409686 sub_4095C6      endp
================================================================================

整个注册算法很明了,合法的注册码只需满足下述3个条件:
1.注册码长度LEN为不小于6的偶数
2.从注册码的第5位开始,取(LEN-4)/2位进行MD5运算,结果为16字节固定数
3.对整个注册码进行MD5运算,结果在已注册名单列表中

对于MD5从算法上我们就别花心思逆运算了,如果你想逆算它没人反对,成功了你可就大名鼎鼎了
我是懒人一个,我也不想出名,因此我选择爆破。
哦,这么简单,随便怎么改吧,保证每次调用验证过程返回EAX=1就行了呗,我为了搞的象模象样的,
先下2个断点,BPX 0040961D,BPX 0040964D,输入一个合法长度的注册码,跟踪进去,记录下
我输入的注册码对应的2个MD5结果,然后把这两个结果PATCH进文件里,我刚输入的注册码不就是合
法注册码了吗?

可惜高兴的太早了,这样改完后限制依然存在。
奇怪了,注册码是整个串取MD5运算才比较的,感觉不应该漏掉哪位在其它地方设有暗桩的,
重新把反汇编结果中所有对注册验证子过程的参考调用处仔细查看一下,发现下面2个可疑位置:

====下面代码与直接解密恢复%10页码限制有关==============================================
0040AA63  > E8 DFECFFFF    CALL _APDFPRP.00409747        ;注册验证子过程,成功则返回EAX=1
0040AA68  . 85C0          TEST EAX,EAX
0040AA6A  . 74 3D          JE SHORT _APDFPRP.0040AAA9
0040AA6C  . E9 33000000    JMP _APDFPRP.0040AAA4        ;可疑跳转
0040AA71    7C 16 8F 1E A9 2B 82 8D                ;可疑数据
0040AA79    CF FA 48 8F 8B 98 05 FA
0040AA81    75 7E E2 6A A0 0F 53 32
0040AA89    BC DE 0F 83 94 5C 37 56
0040AA91    33 48 D5 27 73 74 A3 56
0040AA99    7B 36 F0 BF CF 26 70 3B
0040AAA1    6D B2 AD
0040AAA4  > E9 78000000    JMP _APDFPRP.0040AB21
0040AAA9  > 833D 90AC4500 >CMP DWORD PTR DS:[45AC90],0
0040AAB0  . 74 0E          JE SHORT _APDFPRP.0040AAC0

====下面代码与暴力攻击密码长度限制有关======================================================
00411F05  . E8 3D78FFFF    CALL _APDFPRP.00409747        ;注册验证子过程,成功则返回EAX=1
00411F0A  . 85C0          TEST EAX,EAX
00411F0C  . 75 28          JNZ SHORT _APDFPRP.00411F36
00411F0E  . 833D 34B24500 >CMP DWORD PTR DS:[45B234],4        ;最大密码长度
00411F15  . 7E 0A          JLE SHORT _APDFPRP.00411F21
00411F17  . C705 34B24500 >MOV DWORD PTR DS:[45B234],4
00411F21  > 833D 30B24500 >CMP DWORD PTR DS:[45B230],4        ;最小密码长度
00411F28  . 7E 22          JLE SHORT _APDFPRP.00411F4C
00411F2A  . C705 30B24500 >MOV DWORD PTR DS:[45B230],4
00411F34  . EB 16          JMP SHORT _APDFPRP.00411F4C
00411F36  > E9 11000000    JMP _APDFPRP.00411F4C        ;可疑跳转
00411F3B    A4 52 B7 56 BF 2B 82 0D                ;可疑数据
00411F43    BB F0 C3 29 73 5D 81 E1
00411F4B    6A
00411F4C  > A1 30B24500    MOV EAX,DWORD PTR DS:[45B230]    
00411F51  . 3B05 34B24500  CMP EAX,DWORD PTR DS:[45B234]
00411F57  . 7E 04          JLE SHORT _APDFPRP.00411F5D
00411F59  > 31C0          XOR EAX,EAX
00411F5B  . EB 12          JMP SHORT _APDFPRP.00411F6F
00411F5D  > 85C0          TEST EAX,EAX
00411F5F  .^74 F8          JE SHORT _APDFPRP.00411F59
00411F61  . 833D 34B24500 >CMP DWORD PTR DS:[45B234],0
00411F68  .^74 EF          JE SHORT _APDFPRP.00411F59
00411F6A  . B8 01000000    MOV EAX,1
00411F6F  > 5F            POP EDI
00411F70  . 5E            POP ESI
00411F71  . 5A            POP EDX
00411F72  . 59            POP ECX
00411F73  . 5B            POP EBX
00411F74  . C3            RETN
================================================================================

可以看出上面2处在注册验证调用后都有一个可疑的跳转和一段可疑的数据,我不信编译器正常会编译出这样的代码,
这些地方应该是被加密处理过的,可是怎么也找不到相应的解密过程。
忽然觉察到自己一直跟踪的是脱壳后的程序,如果解码是做在ASProtect壳里的,当然找不到了
重新对未脱壳的程序进行跟踪,还记得注册码验证算法中的下面这行吗:
.text:0040966B                call    ds:off_44FD10        ;注意这行,有后话!!!
后话终于来了,跟踪脱壳后的程序时这行调用过程直接RET返回,什么也没做
而跟踪原来的没脱壳的程序时这个调用代码是指向ASProtect壳里分配的地址空间的,有一段比较复杂的解密代码,
每次程序启动后第一次验证通过会执行一次这段代码,对前面2处可疑跳转及数据解密,(实际跟踪发现,需解密的
代码还不止这2处,其它几处我还不清楚有什么用,因此就没有把他们都贴出来)我猜测这些数据解密后就成
一段SMC代码,用来解除各自的限制。而解密的密钥是由注册码算来的,在没有正确注册码的情况下,想恢复这些代
码的难度不可想象,我对ASProtect壳的研究不多,可能这就是所谓的带KEY的ASProtect保护。

感觉自己象是走上了绝路,要不是自己需要用这个软件,真是要放弃了,再试试看吧,既然它能恢复%10,说明解码
功能都在程序里面,解除限制应该是能实现的,后面的过程就比较苦恼了,没有什么特别的技巧的,慢慢跟踪,瞪大
眼睛,找那%10限制的相关可疑点,印象中好象还碰到一个递归,几经周折终于眼前一亮,来到这里了:
================================================================================
0040D148  |. 8B45 10        MOV EAX,DWORD PTR SS:[EBP+10]    ;PDF文档总页码
0040D14B  |. 83C0 09        ADD EAX,9
0040D14E  |. BB 0A000000    MOV EBX,0A                ;10进制的10呀,等你多时了
0040D153  |. 99            CDQ                    ;
0040D154  |. F7FB          IDIV EBX                ;PDF文档总页码的%10
0040D156  |. 3B01          CMP EAX,DWORD PTR DS:[ECX]        ;已经恢复的页数
0040D158  |. 7E 04          JLE SHORT _APDFPRP.0040D15E        ;
0040D15A  |. FF01          INC DWORD PTR DS:[ECX]        ;还没到%10呢,接着干
0040D15C  |. EB 14          JMP SHORT _APDFPRP.0040D172        ;继续下一页
0040D15E  |> 6A 01          PUSH 1                ;已经到%10了,不玩活了
================================================================================
还不快把0AH改为01H,1/10=%10,1/1=100%,我算的对不?
解决掉了一个限制,可以喘口气了,我需要的就是这项功能,还有2个限制没去掉,我还真用不到。

这会儿心情不错,再努努力,继续跟踪,找到下面2段代码:
================================================================================
00411EC1  . C705 D8BA4500 >MOV DWORD PTR DS:[45BAD8],5        ;初试化的密码长度模常数
00411ECB  . 6A 00          PUSH 0                             
00411ECD  . 6A 00          PUSH 0                             
00411ECF  . 68 2E010000    PUSH 12E                           
00411ED4  . A1 70994500    MOV EAX,DWORD PTR DS:[459970]     
00411ED9  . FF70 38        PUSH DWORD PTR DS:[EAX+38]         
00411EDC  . 2E:FF15 F4B544>CALL DWORD PTR CS:[<&USER32.GetDlgItemInt>]
================================================================================
00414055  |> 8B0D D8BA4500  MOV ECX,DWORD PTR DS:[45BAD8]    ;密码长度模常数=5
0041405B  |. A1 30B24500    MOV EAX,DWORD PTR DS:[45B230]    ;最小密码长度
00414060  |. 99            CDQ                    ;
00414061  |. F7F9          IDIV ECX                ;
00414063  |. 8915 30B24500  MOV DWORD PTR DS:[45B230],EDX    ;最小密码长度 MOD 5
00414069  |. A1 34B24500    MOV EAX,DWORD PTR DS:[45B234]    ;最大密码长度
0041406E  |. 99            CDQ
0041406F  |. F7F9          IDIV ECX
00414071  |. 8915 34B24500  MOV DWORD PTR DS:[45B234],EDX    ;最大密码长度 MOD 5
================================================================================
MOD 5的最大值当然是4了,你要暴力破解多长的密码?随你改了,太长我的机器可受不了哦。

剩下最后一项限制了,词典攻击限制,再鼓其勇气,奋斗了一阵子,没能找到关键代码,非X时期,保重
身体要紧,我还是知难而退吧,留下一处遗憾,实在不想和它较劲了。

... ...期待有人继续下去... ...
*************************************************************************************

-====heXer/iPB====-
-====2003.5.20====-