首页
社区
课程
招聘
[原创]PhotoZoom Pro 5注册码算法分析
发表于: 2014-3-18 23:38 9514

[原创]PhotoZoom Pro 5注册码算法分析

2014-3-18 23:38
9514

【文章标题】: PhotoZoom Pro 5注册码算法分析
【文章作者】: VIDEN
【作者QQ号】: 38352959
【作者邮箱】: viden@live.cn
【操作系统】: Windows 7 32-bit
【生产日期】: 2014318
【软件名称】: BenVista PhotoZoom Pro 5.0.8 (32-bit)
【软件介绍】: 一款图片无损放大软件
【加壳方式】: UPX 0.89.6 - 1.02 / 1.05 - 1.24 -> Markus & Laszlo [Overlay]
【保护方式】: 注册码
【编写语言】: Microsoft Visual C++ 8
【使用工具】: OD+PEID
【作者声明】: 只是感兴趣,学习其注册算法,没有其他目的。失误之处敬请诸位大侠赐教!
-----------------------------------------------------------------------------------------------------
【详细过程】
PhotoZoom Pro是一款可以对图像进行(尽可能的)无损放大并保证画质不会变差,图片放大后依然能保持清晰的软件。网上已经放出一组注册码,在这里只是学习一下其注册算法,该软件算法可能会比较繁琐,跟大家共同学习一下。:D
该软件使用UPX 0.89.6 - 1.02 / 1.05 - 1.24 -> Markus & Laszlo [Overlay],为了调试方便,直接手脱修复IAT,脱壳后Microsoft Visual C++ 8 [Overlay] *,我脱壳后区段有点问题,需要手动修复。进入正题,软件注册码(软件称为解锁码)为25位,在此我使用"AFAAAAAAAAAAAAAAAAAAAAAAF"进行分析。软件注册页面有三项内容可填写,其中注册码必填,软件会根据用户填写的注册码进行判断后两项内容是否必填,其中前六位会判断注册码类型,注册码的前8位比较关键,剩下的会辅助算法生成。在此我使用的注册码后两项可为空。
过程可能会有些长,只说些关键部分。
通过出错弹出消息框,对MessageBoxW下断,逆向找到关键代码。
我是从这里开始分析注册码算法。
00453AD8    83C8 FF         or eax,FFFFFFFF
00453ADB    8945 D8         mov dword ptr ss:[ebp-28],eax
00453ADE    894D E8         mov dword ptr ss:[ebp-18],ecx
00453AE1    398E 1C020000   cmp dword ptr ds:[esi+21C],ecx
00453AE7    0F86 CA000000   jbe pz5.00453BB7                         
00453AED    8B7D E8         mov edi,dword ptr ss:[ebp-18]

接下来的6个CALL分别读取到刚才填写的三项注册内容。关键CALL在这
00453B5C    57              push edi
00453B5D    51              push ecx
00453B5E    52              push edx
00453B5F    50              push eax
00453B60    C645 FC 11      mov byte ptr ss:[ebp-4],11
00453B64    FF96 20020000   call dword ptr ds:[esi+220]              ; 在此跟进去
00453B6A    83C4 10         add esp,10
00453B6D    8D4D B4         lea ecx,dword ptr ss:[ebp-4C]
00453B70    8845 F3         mov byte ptr ss:[ebp-D],al            
00453B73    C645 FC 10      mov byte ptr ss:[ebp-4],10

跟进去往下看,这里有CALL,进去分析以后,会有部分处理,但是对于注册码的算法分析关系不大,可以直接跳过
004040E5    8D4C24 08       lea ecx,dword ptr ss:[esp+8]
004040E9    51              push ecx
004040EA    E8 E1FCFFFF     call pz5.00403DD0                        ; 这里可以直接跳过,感兴趣的可以进去看看
004040EF    8B00            mov eax,dword ptr ds:[eax]
004040F1    8B5424 28       mov edx,dword ptr ss:[esp+28]
004040F5    50              push eax

往下看有个关键CALL
00404104    68 08009B00     push pz5.009B0008
00404109    C74424 30 00000>mov dword ptr ss:[esp+30],0
00404111    E8 D83D0400     call pz5.00447EEE                        ; 这里进去
00404116    83C4 1C         add esp,1C
00404119    8D4C24 04       lea ecx,dword ptr ss:[esp+4]
0040411D    A3 A84C9D00     mov dword ptr ds:[9D4CA8],eax

进去往下看,紧接着有个关键CALL进去
00447F02    FF75 10         push dword ptr ss:[ebp+10]
00447F05    FF34B0          push dword ptr ds:[eax+esi*4]
00447F08    E8 ADFBFFFF     call pz5.00447ABA                        ; 这里进去
00447F0D    83C4 10         add esp,10
00447F10    84C0            test al,al
00447F12    75 0C           jnz short pz5.00447F20

进去往下看,接着会有几个CALL,为我们的注册码申请内存空间,直到这里
00447B15    FF75 CC         push dword ptr ss:[ebp-34]
00447B18    8D45 BC         lea eax,dword ptr ss:[ebp-44]
00447B1B    50              push eax
00447B1C    E8 C2FCFFFF     call pz5.004477E3                        ; 跟进去
00447B21    FF75 CC         push dword ptr ss:[ebp-34]
00447B24    C645 FC 02      mov byte ptr ss:[ebp-4],2

进去往下看,这里开始根据注册码循环得出5位字符串,到这里说下软件内注册码算法经常会用到的一组字符串str1 = "RWHV6N58C7JEGYFT94PKSZMAU2XDQ3B"
0044783C    C645 FC 02      mov byte ptr ss:[ebp-4],2
00447840    8BF3            mov esi,ebx
00447842    897D F0         mov dword ptr ss:[ebp-10],edi
00447845    FF75 F0         push dword ptr ss:[ebp-10]
00447848    8D4D E0         lea ecx,dword ptr ss:[ebp-20]
0044784B    E8 A0350100     call pz5.0045ADF0                        ; 这里返回我们的注册码,每次调用后注册码指针指+1
00447850    0FB600          movzx eax,byte ptr ds:[eax]              ; 得到EAX所指注册码第一个字符
00447853    50              push eax
00447854    E8 11F3FFFF     call pz5.00446B6A                        ; 在字符串str1里匹配EAX字符,有返回位置减1,无则返回0xffffffff
00447859    0FAFC6          imul eax,esi
0044785C    6BF6 1F         imul esi,esi,1F
0044785F    03F8            add edi,eax             ; 运算结果储存在edi,如此循环6次,取edi的值
00447861    FF45 F0         inc dword ptr ss:[ebp-10]
00447864    837D F0 06      cmp dword ptr ss:[ebp-10],6
00447868    59              pop ecx
00447869  ^ 7C DA           jl short pz5.00447845

接着往下,这个循环确定最后5位字符串,这里用到软件内的另一个固定字符串str2 = "yc85LKaFH6pOTR7h90dtvsZjEJobqrQWeDmPAB1kY24fMnwUV3iNxzlCSIXguG"
0044786B    6A 3E           push 3E
0044786D    C745 F0 0500000>mov dword ptr ss:[ebp-10],5
00447874    5E              pop esi
00447875    8BC7            mov eax,edi
00447877    99              cdq
00447878    8BCE            mov ecx,esi
0044787A    F7F9            idiv ecx
0044787C    8BC2            mov eax,edx
0044787E    C1F8 1F         sar eax,1F
00447881    23C6            and eax,esi
00447883    03C2            add eax,edx
00447885    99              cdq
00447886    F7F9            idiv ecx
00447888    8D4D D8         lea ecx,dword ptr ss:[ebp-28]
0044788B    8BC2            mov eax,edx
0044788D    C1F8 1F         sar eax,1F
00447890    23C6            and eax,esi
00447892    0305 34039C00   add eax,dword ptr ds:[9C0334]
00447898    8A0410          mov al,byte ptr ds:[eax+edx]       ; 这里根据上面换算来的EAX和EDX的值确定str2字符串里的字符
0044789B    8845 E8         mov byte ptr ss:[ebp-18],al
0044789E    FF75 E8         push dword ptr ss:[ebp-18]
004478A1    E8 5A3C0100     call pz5.0045B500                        ; 这个CALL返回5位新字符串的地址指针
004478A6    8BC7            mov eax,edi
004478A8    33D2            xor edx,edx
004478AA    8BCE            mov ecx,esi
004478AC    F7F1            div ecx
004478AE    FF4D F0         dec dword ptr ss:[ebp-10]
004478B1    8BF8            mov edi,eax
004478B3  ^ 75 C0           jnz short pz5.00447875                   ; 结束循环确定5位字符串

循环结束后接下来的几个CALL对字符串申请内存空间,以及释放之前用到字符串的内存空间。
运行到返回,到这里
00447B1B    50              push eax
00447B1C    E8 C2FCFFFF     call pz5.004477E3                        ; 刚才的CALL
00447B21    FF75 CC         push dword ptr ss:[ebp-34]
00447B24    C645 FC 02      mov byte ptr ss:[ebp-4],2
00447B28    E8 1AF1FFFF     call pz5.00446C47                        ; 进去,会根据注册码换算出新的25位字符串
00447B2D    83C4 0C         add esp,0C
00447B30    6A 06           push 6

到这里,循环25次得到新字符串NewStr1
00446C63    0FB6043E        movzx eax,byte ptr ds:[esi+edi]          ; 获取注册码第一个字符
00446C67    50              push eax
00446C68    E8 FDFEFFFF     call pz5.00446B6A                        ; 这个CALL会经常用到,返回字符在字符串str1内的位置减1
00446C6D    59              pop ecx
00446C6E    8BC8            mov ecx,eax
00446C70    2BC3            sub eax,ebx
00446C72    99              cdq
00446C73    6A 1F           push 1F
00446C75    5B              pop ebx
00446C76    F7FB            idiv ebx
00446C78    8BC2            mov eax,edx
00446C7A    C1F8 1F         sar eax,1F
00446C7D    23C3            and eax,ebx
00446C7F    0305 20039C00   add eax,dword ptr ds:[9C0320]
00446C85    8BD9            mov ebx,ecx
00446C87    8A0410          mov al,byte ptr ds:[eax+edx]              ; 经过上面的换算,确定一位新字符
00446C8A    88043E          mov byte ptr ds:[esi+edi],al
00446C8D    46              inc esi
00446C8E    3B75 FC         cmp esi,dword ptr ss:[ebp-4]
00446C91  ^ 72 D0           jb short pz5.00446C63          ; 结束循环生成25位新字符串NewStr1

继续,返回到这里
00447B24    C645 FC 02      mov byte ptr ss:[ebp-4],2
00447B28    E8 1AF1FFFF     call pz5.00446C47                        ; 刚才的CALL
00447B2D    83C4 0C         add esp,0C
00447B30    6A 06           push 6
00447B32    8D45 DC         lea eax,dword ptr ss:[ebp-24]
00447B35    50              push eax
00447B36    8D4D CC         lea ecx,dword ptr ss:[ebp-34]
00447B39    E8 A2420100     call pz5.0045BDE0                        ; 这个很简单,返回NewStr1的前6位
00447B3E    8B00            mov eax,dword ptr ds:[eax]

继续往下看,这个CALL把刚才6位与固定的0x381个做比较,稍微有点多,如果存在,就可以将注册时的剩下两项为空,否则下个跳转不会跳走,退出验证,输入剩下的两项内容。
00447B44    50              push eax
00447B45    B9 88639D00     mov ecx,pz5.009D6388
00447B4A    C645 FC 03      mov byte ptr ss:[ebp-4],3
00447B4E    E8 CD3A0100     call pz5.0045B620                        ; 这里与0x381个匹配
00447B53    85C0            test eax,eax                             
00447B55    8D4D DC         lea ecx,dword ptr ss:[ebp-24]
00447B58    0F9DC3          setge bl
00447B5B    C645 FC 02      mov byte ptr ss:[ebp-4],2
00447B5F    E8 CC320100     call pz5.0045AE30                       
00447B64    84DB            test bl,bl
00447B66    74 33           je short pz5.00447B9B                    ; 如果存在直接跳走,继续验证

跳到这里,下面几个CALL是处理几个固定的数据,在接下来的验证中会用到。
00447B9B    56              push esi
00447B9C    57              push edi
00447B9D    68 81E5BFDB     push DBBFE581
00447BA2    8D4D 90         lea ecx,dword ptr ss:[ebp-70]
00447BA5    E8 666E0100     call pz5.0045EA10                        ; 可以进去看看,对0xDBBFE581进行处理,进去
00447BAA    68 A19C0F12     push 120F9CA1
00447BAF    E8 01E10000     call pz5.00455CB5                        ; 这里同样

进去来到这里,直接返回
0045EA10    8BC1            mov eax,ecx
0045EA12    8B4C24 04       mov ecx,dword ptr ss:[esp+4]
0045EA16    8BD1            mov edx,ecx
0045EA18    83F2 25         xor edx,25
0045EA1B    8910            mov dword ptr ds:[eax],edx
0045EA1D    8BD1            mov edx,ecx
0045EA1F    81F2 50020000   xor edx,250
0045EA25    8950 04         mov dword ptr ds:[eax+4],edx
0045EA28    8BD1            mov edx,ecx
0045EA2A    81F2 00250000   xor edx,2500
0045EA30    8950 08         mov dword ptr ds:[eax+8],edx
0045EA33    8BD1            mov edx,ecx
0045EA35    81F2 00500200   xor edx,25000
0045EA3B    8950 0C         mov dword ptr ds:[eax+C],edx
0045EA3E    8BD1            mov edx,ecx
0045EA40    81F2 00002500   xor edx,250000
0045EA46    8950 10         mov dword ptr ds:[eax+10],edx
0045EA49    8BD1            mov edx,ecx
0045EA4B    81F2 00005002   xor edx,2500000
0045EA51    81F1 00000025   xor ecx,25000000
0045EA57    8950 14         mov dword ptr ds:[eax+14],edx
0045EA5A    8948 18         mov dword ptr ds:[eax+18],ecx
0045EA5D    C2 0400         retn 4

返回后也会有几个CALL,是几个固定的运算,往下到这个循环
00447BCF    68 FFFFFF07     push 7FFFFFF
00447BD4    8D4D 90         lea ecx,dword ptr ss:[ebp-70]
00447BD7    E8 C46F0100     call pz5.0045EBA0                        
00447BDC    53              push ebx
00447BDD    8D4D CC         lea ecx,dword ptr ss:[ebp-34]
00447BE0    8945 E8         mov dword ptr ss:[ebp-18],eax
00447BE3    E8 08320100     call pz5.0045ADF0                        ; 这里返回新字符串NewStr1,指针会逐渐加1
00447BE8    0FB600          movzx eax,byte ptr ds:[eax]         ;得到第一个字符
00447BEB    50              push eax
00447BEC    E8 79EFFFFF     call pz5.00446B6A                        ; 还是在str1里进行匹配返回位置减1
00447BF1    0345 E8         add eax,dword ptr ss:[ebp-18]
00447BF4    59              pop ecx
00447BF5    99              cdq
00447BF6    6A 1F           push 1F
00447BF8    59              pop ecx
00447BF9    F7F9            idiv ecx
00447BFB    8BC2            mov eax,edx
00447BFD    C1F8 1F         sar eax,1F
00447C00    23C1            and eax,ecx
00447C02    03C2            add eax,edx
00447C04    0FAFC6          imul eax,esi
00447C07    6BF6 1F         imul esi,esi,1F
00447C0A    03F8            add edi,eax                               ; 进行一些简单运算后,保存到EDI,最后需要EDI的值
00447C0C    43              inc ebx
00447C0D    83FB 06         cmp ebx,6
00447C10  ^ 7C BD           jl short pz5.00447BCF                    ;结束循环,会用到EDI的数值,这里记作m

继续往下看,到这个循环,会把EDI的数值与0x8D个之前换算出的固定值作比较,如果EDI的值比较小的话,会从下面跳走,结束验证。
00447C36    8B15 7C639D00   mov edx,dword ptr ds:[9D637C]            ;
00447C3C    8955 F0         mov dword ptr ss:[ebp-10],edx
00447C3F    8B02            mov eax,dword ptr ds:[edx]
00447C41    3BF8            cmp edi,eax
00447C43    8B5A 04         mov ebx,dword ptr ds:[edx+4]
00447C46    7C 34           jl short pz5.00447C7C
00447C48    3BFB            cmp edi,ebx
00447C4A    7F 30           jg short pz5.00447C7C
00447C4C    99              cdq
00447C4D    6A 03           push 3
00447C4F    5B              pop ebx
00447C50    F7FB            idiv ebx                              ;这里注意一下,这地方是把上面m的值除以3,结果记作n
00447C52    8BC2            mov eax,edx
00447C54    C1F8 1F         sar eax,1F
00447C57    23C3            and eax,ebx
00447C59    03C2            add eax,edx
00447C5B    3BC6            cmp eax,esi
00447C5D    75 1A           jnz short pz5.00447C79
00447C5F    8B45 F0         mov eax,dword ptr ss:[ebp-10]
00447C62    8B40 04         mov eax,dword ptr ds:[eax+4]
00447C65    99              cdq
00447C66    F7FB            idiv ebx
00447C68    8BC2            mov eax,edx
00447C6A    C1F8 1F         sar eax,1F
00447C6D    23C3            and eax,ebx
00447C6F    03C2            add eax,edx
00447C71    3BC6            cmp eax,esi
00447C73    0F84 A0000000   je pz5.00447D19
00447C79    8B55 F0         mov edx,dword ptr ss:[ebp-10]
00447C7C    FF45 E8         inc dword ptr ss:[ebp-18]
00447C7F    83C2 08         add edx,8
00447C82    394D E8         cmp dword ptr ss:[ebp-18],ecx
00447C85    8955 F0         mov dword ptr ss:[ebp-10],edx
00447C88  ^ 7C B5           jl short pz5.00447C3F                    ; 0x8D个生成的数值作比较

继续往下看,来到这里,下面的CALL会把NewStr1剩下的字符串进行转换
00447C8A    8B5D CC         mov ebx,dword ptr ss:[ebp-34]
00447C8D    6A 00           push 0
00447C8F    FF75 08         push dword ptr ss:[ebp+8]
00447C92    83C3 06         add ebx,6                                ; 这里新字符串NewStr指针+6,
00447C95    53              push ebx
00447C96    E8 EAEEFFFF     call pz5.00446B85                        ; 这里进去
00447C9B    83C4 0C         add esp,0C

进去以后直接往下看,来到一个大的循环,这里会用到第三组字符串,是版本信息str3 = "PhotoZoom Pro 5 Win"
00446BBC    03FE            add edi,esi
00446BBE    6BFF 03         imul edi,edi,3                           ;EDI的值为循环次数,计算方法 剩下NewStr1的字符数+str3的字符数,结果*3
00446BC1    895D F8         mov dword ptr ss:[ebp-8],ebx
00446BC4    3BFB            cmp edi,ebx
00446BC6    76 7A           jbe short pz5.00446C42
00446BC8    EB 03           jmp short pz5.00446BCD
00446BCA    8B75 F0         mov esi,dword ptr ss:[ebp-10]
00446BCD    8BC3            mov eax,ebx
00446BCF    99              cdq
00446BD0    F77D FC         idiv dword ptr ss:[ebp-4]
00446BD3    8BC3            mov eax,ebx
00446BD5    8BCA            mov ecx,edx
00446BD7    C1F9 1F         sar ecx,1F
00446BDA    234D FC         and ecx,dword ptr ss:[ebp-4]
00446BDD    03CA            add ecx,edx
00446BDF    99              cdq
00446BE0    F7FE            idiv esi
00446BE2    8BC2            mov eax,edx
00446BE4    C1F8 1F         sar eax,1F
00446BE7    23C6            and eax,esi
00446BE9    03C2            add eax,edx
00446BEB    8B55 0C         mov edx,dword ptr ss:[ebp+C]
00446BEE    0FBE0410        movsx eax,byte ptr ds:[eax+edx]          ;这里是得到str3的第N位字符,N为当前循环次数,若等于str3长度后从1开始
00446BF2    8B55 F8         mov edx,dword ptr ss:[ebp-8]
00446BF5    6BD2 03         imul edx,edx,3
00446BF8    25 FF000000     and eax,0FF
00446BFD    03C2            add eax,edx
00446BFF    25 FFFF7F00     and eax,7FFFFF
00446C04    807D 10 00      cmp byte ptr ss:[ebp+10],0
00446C08    8945 F8         mov dword ptr ss:[ebp-8],eax
00446C0B    74 02           je short pz5.00446C0F
00446C0D    F7D8            neg eax
00446C0F    8945 F4         mov dword ptr ss:[ebp-C],eax
00446C12    8B45 08         mov eax,dword ptr ss:[ebp+8]
00446C15    8D3401          lea esi,dword ptr ds:[ecx+eax]
00446C18    0FB606          movzx eax,byte ptr ds:[esi]              ;得到NewStr1剩下的第N位字符同样为长度,只不过这里NewStr1的长度固定为25-6=19位
00446C1B    50              push eax
00446C1C    E8 49FFFFFF     call pz5.00446B6A                        ; 匹配str1字符串返回位置减1
00446C21    0345 F4         add eax,dword ptr ss:[ebp-C]
00446C24    59              pop ecx
00446C25    99              cdq
00446C26    6A 1F           push 1F
00446C28    59              pop ecx
00446C29    F7F9            idiv ecx
00446C2B    8BC2            mov eax,edx
00446C2D    C1F8 1F         sar eax,1F
00446C30    23C1            and eax,ecx
00446C32    0305 20039C00   add eax,dword ptr ds:[9C0320]            
00446C38    43              inc ebx
00446C39    8A0410          mov al,byte ptr ds:[eax+edx]             ;经过运算,在str1里确定一位字符
00446C3C    8806            mov byte ptr ds:[esi],al
00446C3E    3BDF            cmp ebx,edi
00446C40  ^ 72 88           jb short pz5.00446BCA                     ;循环次数由上面EDI决定,最后生成的字符串记作NewStr2

继续,返回,到这里,这有几个跳转,当注册信息第二三项为必填的时候会相应的跳走进行计算,在这里只有注册码,所以第一个直接跳走
00447C95    53              push ebx
00447C96    E8 EAEEFFFF     call pz5.00446B85                        ; 刚才的CALL
00447C9B    83C4 0C         add esp,0C
00447C9E    8D4D D4         lea ecx,dword ptr ss:[ebp-2C]
00447CA1    E8 EA3B0100     call pz5.0045B890                       
00447CA6    83EE 00         sub esi,0
00447CA9    C645 FC 04      mov byte ptr ss:[ebp-4],4
00447CAD    0F84 D4000000   je pz5.00447D87                         
00447CB3    4E              dec esi
00447CB4    0F84 97000000   je pz5.00447D51                         
00447CBA    4E              dec esi
00447CBB    0F85 ED000000   jnz pz5.00447DAE                         
00447CC1    FF75 0C         push dword ptr ss:[ebp+C]

跳走,继续往下看,到这里,稍微提一下,释放内存时大部分都是ECX的地址
00447DA2    8D4D AC         lea ecx,dword ptr ss:[ebp-54]
00447DA5    C645 FC 04      mov byte ptr ss:[ebp-4],4
00447DA9    E8 82300100     call pz5.0045AE30                        ; 释放ECX对应字符串的内存
00447DAE    FF75 E0         push dword ptr ss:[ebp-20]
00447DB1    E8 F7FBFFFF     call pz5.004479AD                        ; 这里跟进去
00447DB6    48              dec eax

到这里,会有连续的好几个数值进行比较,根据结果确定接下来的3位字符串,其中EAX的值为上面提到的n
004479AD    8B4424 04       mov eax,dword ptr ss:[esp+4]
004479B1    B9 FF2C3101     mov ecx,1312CFF
004479B6    3BC1            cmp eax,ecx
004479B8    77 03           ja short pz5.004479BD
004479BA    33C0            xor eax,eax
004479BC    C3              retn
004479BD    8D90 00D3CEFE   lea edx,dword ptr ds:[eax+FECED300]      
004479C3    3BD1            cmp edx,ecx
004479C5    77 04           ja short pz5.004479CB
004479C7    33C0            xor eax,eax
004479C9    40              inc eax
004479CA    C3              retn
004479CB    8D88 00A69DFD   lea ecx,dword ptr ds:[eax+FD9DA600]
004479D1    BA 7F969800     mov edx,pz5.0098967F
004479D6    3BCA            cmp ecx,edx
004479D8    77 04           ja short pz5.004479DE
004479DA    6A 02           push 2
004479DC    58              pop eax
004479DD    C3              retn

根据n的值继续,返回到这里,根据CALL的返回值,确定接下来的3位字符串,或者为空
00447DB1    E8 F7FBFFFF     call pz5.004479AD                        ; 刚才的CALL
00447DB6    48              dec eax
00447DB7    59              pop ecx
00447DB8    83F8 0E         cmp eax,0E
00447DBB    77 68           ja short pz5.00447E25
00447DBD    FF2485 B27E4400 jmp dword ptr ds:[eax*4+447EB2]
00447DC4    68 84788500     push pz5.00857884                        ; ASCII "K3K"
00447DC9    EB 52           jmp short pz5.00447E1D
00447DCB    68 80788500     push pz5.00857880                        ; ASCII "GK6"
00447DD0    EB 4B           jmp short pz5.00447E1D
00447DD2    68 7C788500     push pz5.0085787C                        ; ASCII "3DP"
00447DD7    EB 44           jmp short pz5.00447E1D

继续往下,跳到这里,产生上面组合的结果,为新字符串NewStr2的前6位+刚才的3位,或者只有前6位。
这里说明一下,下面的CALL是之前调用过一次的,根据主副字符串长度判断循环次数。在这主字符串为NewStr2指针+6,副字符串为组合后的,然后循环生成新字符串NewStr3
00447E1D    8D4D D4         lea ecx,dword ptr ss:[ebp-2C]
00447E20    E8 7B360100     call pz5.0045B4A0                        ; 为字符串申请内存
00447E25    6A 01           push 1
00447E27    FF75 D4         push dword ptr ss:[ebp-2C]               ; 这里为字符串组合后的结果
00447E2A    53              push ebx
00447E2B    E8 55EDFFFF     call pz5.00446B85                        ; 这里就是之前的循环,这里不做说明了,生成新NewStr3
00447E30    83C4 0C         add esp,0C

继续往下,到这个循环
00447E33    6A 08           push 8
00447E35    33F6            xor esi,esi
00447E38    53              push ebx
00447E39    6BF6 07         imul esi,esi,7
00447E3C    8D4D CC         lea ecx,dword ptr ss:[ebp-34]
00447E3F    E8 AC2F0100     call pz5.0045ADF0                        ; NewStr3字符串位置+8位置开始逐渐+1
00447E44    0FBE00          movsx eax,byte ptr ds:[eax]              ;从+8位置开始获取字符
00447E47    C1C6 05         rol esi,5
00447E4A    03F0            add esi,eax                              ;进行几步简单的运算,得到ESI的值
00447E4C    43              inc ebx
00447E4D    83FB 19         cmp ebx,19
00447E50  ^ 72 E6           jb short pz5.00447E38                    ; 结束循环,用到ESI的值,记为A

继续到这里,接近尾声了。
00447E64    6A 07           push 7
00447E66    8BF2            mov esi,edx
00447E68    C1FE 1F         sar esi,1F
00447E6B    23F1            and esi,ecx
00447E6D    8D4D CC         lea ecx,dword ptr ss:[ebp-34]
00447E70    03F2            add esi,edx
00447E72    E8 792F0100     call pz5.0045ADF0                        ; NewStr3指针+7
00447E77    0FB600          movzx eax,byte ptr ds:[eax]              ;得到该字符
00447E7A    50              push eax
00447E7B    E8 EAECFFFF     call pz5.00446B6A                        ; 匹配str1 返回位置减1
00447E80    59              pop ecx
00447E81    8BF8            mov edi,eax
00447E83    6A 06           push 6
00447E85    6BFF 1F         imul edi,edi,1F
00447E88    8D4D CC         lea ecx,dword ptr ss:[ebp-34]
00447E8B    E8 602F0100     call pz5.0045ADF0                        ; NewStr3指针+6
00447E90    0FB600          movzx eax,byte ptr ds:[eax]              ;得到该字符,注意EAX的值为字符对应的ASCII码
00447E93    50              push eax
00447E94    E8 D1ECFFFF     call pz5.00446B6A                        ; 匹配str1 返回位置减1
00447E99    03C7            add eax,edi                              ;这里得到EAX的值,记为B
00447E9B    59              pop ecx
00447E9C    3BF0            cmp esi,eax                              ;判断来了,这里A要等于B!
00447E9E    8D4D D4         lea ecx,dword ptr ss:[ebp-2C]
00447EA1    0F94C3          sete bl                      
00447EA4    C645 FC 02      mov byte ptr ss:[ebp-4],2
00447EA8    E8 832F0100     call pz5.0045AE30                        ; 释放ECX对应字符串内存
00447EAD  ^ E9 72FEFFFF     jmp pz5.00447D24                         ;跳走,根据bl的值进行判断,结束验证。

分析中大部分都是加减乘除移位的运算,并没有细说。简单总结一下,不贴代码了,比较繁琐。全部算法分为几个部分。首先软件内的固定字符串。
str1 = "RWHV6N58C7JEGYFT94PKSZMAU2XDQ3B"
str2 = "yc85LKaFH6pOTR7h90dtvsZjEJobqrQWeDmPAB1kY24fMnwUV3iNxzlCSIXguG"
str3 = "PhotoZoom Pro 5 Win"    
其中这个版本信息有多个,当第一个不匹配的时候,会验证其他版本,如果正确会提示旧版注册码,要求升级注册码。
根据注册码前6位换算得到一个数值,根据该数值和str2得出5位字符串,进行验证。
将注册码每一位与str1进行换算,得到25位新字符串NewStr。
根据新字符串NewStr的前6位与软件内固定0x381个6位字符串进行比较,判断当前注册码是什么类型。
接下来处理几个固定的数值,取其中6个结果分别与NewStr的前6位进行换算,得到一个数值m。
m的值除以3,得到n。
NewStr字符串6位以后的为主字符串,str3版本信息位副字符串进行换算,得到新的字符NewStr2。这里说明一下,这里只是处理6位以后的字符,前6位不变。
根据上面n的值与对应几个固定数值作比较,确定接下来的字符,可能是三位字符,也可能是空,当然如果当前的注册码邮箱必填的话,还可能是邮箱地址+固定字符串。
NewStr2字符串6位以后的为主字符串,上一步生成的为副字符串进行换算,得到最终NewStr3。同上,只是处理剩下的字符。
最后就是分别对新字符串指针+8,+7,+6位置的字符进行换算,得到两个数值A和B。
最终判断A和B的值,若相等,则验证成功。

到此,分析结束。
在此声明,只是为了学习其注册算法,没有其他目的。失误之处敬请诸位大侠赐教!


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 5
支持
分享
最新回复 (6)
雪    币: 406
活跃值: (164)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
2
写得很详细,赞一个~~
2014-3-19 01:12
0
雪    币: 3277
活跃值: (1992)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
管理员哪里去了,这样的贴子应该给精华,不然会打击作者写教程的信心。
2014-3-19 15:04
0
雪    币: 119
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
确实有点儿长,卤煮能把您分析的版本放上来么,或者上传网盘后给个链接!
2014-3-22 11:09
0
雪    币: 707
活跃值: (1301)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
5
学习。。。。。。。。不错哦
2014-3-22 17:07
0
雪    币: 88
活跃值: (115)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
恩,不错,学习学习~~
2014-3-22 23:40
0
雪    币: 558
活跃值: (112)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
7
分析的很到位!前来学习!
2014-3-24 11:34
0
游客
登录 | 注册 方可回帖
返回
//