【文章标题】:【原创】PDFcamp Printer (PDF Writer) V2.2 最新版破解分析
【文章作者】: playboysen
【作者邮箱】: playboysen@126.com
【作者相册】: playboysen2.photo.163.com
【软件名称】: PDFcamp Printer (PDF Writer) V2.2
【使用工具】: ollydbg
【操作平台】: Windows XP SP2
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
接触破解一年多,付出的心血多少自己知道,每天面对一堆的汇编指令,多少次想到了放弃,但是最终还是坚守住了自己的兴趣,一年多来经手的软件不下200个,大多是对她们实行”强暴“,只因水平不高,还看不太懂算法,这个小程序是我算法分析的处女作,虽然简单,但是整体分析下来却很不易,希望能给小鸟一点提示,高手掠过:
PDFcamp Printer,可以将doc、xls、ppt、txt文档直接“打印”成pdf文件,未注册版本试用30天,且转换后的pdf文件有水印。程序只有几百K,安装后在安装目录中没有主文件,经过半天的摸索才找到如何注册,汗,呵呵,随便打开一个word文档,选择“文件”“打印(Ctrl+P)”,调出打印框后点“属性”可以看到它的界面,在About选项卡中就有注册框了,如图1
大家看到了,只有一个文本框,没有机器码和用户名输入提示,输入任意假码后点“Register”,弹出提示框 “Your register number error ……”,用Wsyscheck查看word.exe加载的模块发现两个可疑dll pdfui.dll和pdfprn.dll(而且这两个文件是加了UPX壳的),由此猜测它是dll文件注册类型
打开任意一个word文档,OD附加word.exe,bp GetWindowTextA下断后点注册,OD提示断下,Alt+F9返回后F8单步
032961D9 8BB424 D0000000 mov esi, dword ptr [esp+D0] 032961E0 8B3D 04112903 mov edi, dword ptr [3291104] ; USER32.GetDlgItemTextA 032961E6 8D4C24 4C lea ecx, dword ptr [esp+4C] 032961EA 68 80000000 push 80 032961EF 51 push ecx 032961F0 68 EC030000 push 3EC 032961F5 56 push esi 032961F6 FFD7 call edi 032961F8 8D5424 0C lea edx, dword ptr [esp+C] 032961FC 6A 40 push 40 032961FE 52 push edx 032961FF 68 EF030000 push 3EF 03296204 56 push esi 03296205 FFD7 call edi 03296207 8D4424 0C lea eax, dword ptr [esp+C] ; 取假码 0329620B 8D4C24 4C lea ecx, dword ptr [esp+4C] 0329620F 50 push eax ; 假码入栈 03296210 51 push ecx 03296211 E8 FA220000 call 03298510 ; 关键Call,F7跟进 03296216 85C0 test eax, eax 03296218 0F84 9A000000 je 032962B8 ;关键跳转,不能跳 0329621E 8D5424 0C lea edx, dword ptr [esp+C] 03296222 8D4424 4C lea eax, dword ptr [esp+4C]
03298510 8B5424 08 mov edx, dword ptr [esp+8] 03298514 85D2 test edx, edx 03298516 74 27 je short 0329853F 03298518 803A 00 cmp byte ptr [edx], 0 0329851B 74 22 je short 0329853F ; 比较输入的注册码是否为空 0329851D 57 push edi 0329851E 8BFA mov edi, edx 03298520 83C9 FF or ecx, FFFFFFFF 03298523 33C0 xor eax, eax 03298525 F2:AE repne scas byte ptr es:[edi] 03298527 F7D1 not ecx 03298529 49 dec ecx 0329852A 5F pop edi 0329852B 83F9 0D cmp ecx, 0D ; ecx中是假码位数,与13相比较,如果注册码小于等于13位就跳向错误 0329852E 7E 0F jbe short 0329853F 03298530 52 push edx 03298531 E8 4AFFFFFF call 03298480 ; 唯一的“算法”比较处F7进去 03298536 F7D8 neg eax 03298538 1BC0 sbb eax, eax 0329853A F7D8 neg eax 0329853C C2 0800 retn 8
03298483 56 push esi 03298484 8B7424 20 mov esi, dword ptr [esp+20] 03298488 8D5424 04 lea edx, dword ptr [esp+4] 0329848C 57 push edi 0329848D 8A06 mov al, byte ptr [esi] 0329848F 8A4E 01 mov cl, byte ptr [esi+1] 03298492 884424 14 mov byte ptr [esp+14], al 03298496 32C0 xor al, al 03298498 52 push edx 03298499 884424 19 mov byte ptr [esp+19], al 0329849D 884C24 0C mov byte ptr [esp+C], cl 032984A1 884424 0D mov byte ptr [esp+D], al 032984A5 E8 D0000000 call 0329857A ; 跟踪发现,至此假码第二位的十六进制放入ECX,得知此处为关键比较1,F7跟进 032984AA 8BF8 mov edi, eax 032984AC 8D4424 18 lea eax, dword ptr [esp+18] 032984B0 50 push eax 032984B1 E8 C4000000 call 0329857A ; 此处为关键比较2 032984B6 03F8 add edi, eax ; 上面两个call过去后这里的add的作用是:把注册码一二位数值的16进制值分别减去30后,两个余数相加>结果放到EDI 032984B8 83C4 08 add esp, 8 032984BB 83FF 08 cmp edi, 8 ; EDI必须是8 ****注册码的第一个约束条件**** 032984BE 74 0A je short 032984CA 032984C0 5F pop edi 032984C1 33C0 xor eax, eax 032984C3 5E pop esi 032984C4 83C4 18 add esp, 18 032984C7 C2 0400 retn 4 032984CA 807E 02 56 cmp byte ptr [esi+2], 56 ; 注册码第三位是“V” 032984CE 74 0A je short 032984DA 032984D0 5F pop edi 032984D1 33C0 xor eax, eax 032984D3 5E pop esi 032984D4 83C4 18 add esp, 18 032984D7 C2 0400 retn 4 032984DA 807E 03 32 cmp byte ptr [esi+3], 32 ; 注册码第四位是“2” 032984DE 74 0A je short 032984EA 032984E0 5F pop edi 032984E1 33C0 xor eax, eax 032984E3 5E pop esi 032984E4 83C4 18 add esp, 18 032984E7 C2 0400 retn 4 032984EA 807E 05 31 cmp byte ptr [esi+5], 31 ; 注册码第六位是“1” 032984EE 74 0A je short 032984FA 032984F0 5F pop edi 032984F1 33C0 xor eax, eax 032984F3 5E pop esi 032984F4 83C4 18 add esp, 18 032984F7 C2 0400 retn 4 032984FA 8A4E 0F mov cl, byte ptr [esi+F] ; 注册码第十六位的十六进制放入cl 032984FD 33C0 xor eax, eax 032984FF 80F9 38 cmp cl, 38 ; 注册码第十六位是“8” 03298502 5F pop edi 03298503 0F94C0 sete al ;如果条件全部满足则al置1 03298506 5E pop esi 03298507 83C4 18 add esp, 18 0329850A C2 0400 retn 4 0329850D 90 nop 0329850E 90 nop
7C944C36 55 push ebp 7C944C37 8BEC mov ebp, esp 7C944C39 56 push esi 7C944C3A 8B75 08 mov esi, dword ptr [ebp+8] 7C944C3D 85F6 test esi, esi 7C944C3F 0F84 B3AA0200 je 7C96F6F8 7C944C45 833D 70C1997C 0>cmp dword ptr [7C99C170], 1 7C944C4C 0FB606 movzx eax, byte ptr [esi] ; 假码第二位十六进制放入eax 7C944C4F 0F8F AAAA0200 jg 7C96F6FF 7C944C55 8B0D 30C3997C mov ecx, dword ptr [7C99C330] 7C944C5B 0FB60441 movzx eax, byte ptr [ecx+eax*2] 7C944C5F 83E0 08 and eax, 8 7C944C62 85C0 test eax, eax 7C944C64 0F85 A4AA0200 jnz 7C96F70E ; 这几句没看懂什么功能,但并不影响分析 7C944C6A 0FB60E movzx ecx, byte ptr [esi] ; 假码第二位十六进制放入ecx 7C944C6D 46 inc esi 7C944C6E 83F9 2D cmp ecx, 2D ; 假码第二位十六进制与“-”连接符比较 7C944C71 8BD1 mov edx, ecx 7C944C73 74 2A je short 7C944C9F 7C944C75 83F9 2B cmp ecx, 2B 7C944C78 74 25 je short 7C944C9F 7C944C7A 33C0 xor eax, eax 7C944C7C 83F9 30 cmp ecx, 30 ; ******************************** 7C944C7F 7C 19 jl short 7C944C9A ; 这四句是比较第二位是否为数字 7C944C81 83F9 39 cmp ecx, 39 7C944C84 7F 14 jg short 7C944C9A ; ************************************** 7C944C86 83E9 30 sub ecx, 30 ; 假码第二位十六进制减去30>值放入ECX 7C944C89 83F9 FF cmp ecx, -1 7C944C8C ^ 74 8A je short 7C944C18 7C944C8E 8D0480 lea eax, dword ptr [eax+eax*4] 7C944C91 8D0441 lea eax, dword ptr [ecx+eax*2] ; ecx+eax*2(其实这里eax大部分时候是0,那么这句就是把ecx值放入eax)>值放入EAX 7C944C94 0FB60E movzx ecx, byte ptr [esi] 7C944C97 46 inc esi 7C944C98 ^ EB E2 jmp short 7C944C7C 7C944C9A 83C9 FF or ecx, FFFFFFFF 7C944C9D ^ EB EA jmp short 7C944C89 7C944C9F 0FB60E movzx ecx, byte ptr [esi] 7C944CA2 46 inc esi 7C944CA3 ^ EB D5 jmp short 7C944C7A
至此分析全部结束,算法过程总结如下:
注册码第一二位16进制分别减去30后的余数之和必须等于8
注册码第三位必须是 V
第四位 2
第六位 1
十六位 8
顺便写出注册机,源码公布如下(Delphi 7.0编译通过):
procedure TForm1.btn1Click(Sender: TObject);
var
a,b,c,Temp:integer;
begin
randomize; //初始化随机数
repeat
a:=Random(10);
b:=Random(10);
c:=Random(10);
Temp:=a + b;
until Temp=8;
Form1.Edit1.Text:=IntToStr(a) + IntToStr(b) + 'V2'+IntToStr(c)+'1Senhuan018';
end;
end.