【文章标题】: 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的值,若相等,则验证成功。
到此,分析结束。
在此声明,只是为了学习其注册算法,没有其他目的。失误之处敬请诸位大侠赐教!
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课