文章作者: qduwg
题目:注册图像浏览器“豪杰大眼睛HeroPhotoShowV2.1”
工具:Softice,PEID
功能简介:独创超级图网,轻松点击看遍互联网;图像处理简单快捷,电子相册,屏保制作;支持图片格式60余种,动画图象随意抽取;序列播放,进度随意更改,任意角度看图。
引子:这是继分析“豪杰超级解霸”和“金山影霸”以来的又一个豪杰产品的注册码分析。尽管Acdsee是图像浏览器的大腕,尽管大眼睛还有些缺陷,但是我们应该支持我们的民族软件产业。PEID检查没有加壳,VC开发。启动程序,输入用户名wanggang,输入注册码1111-2222-3333-4444,打开softice,下断点bpxgetwindowtexta,F5退出,点击“确定”,被拦住。按一次F12来到下面代码处。
00402480/$83EC40SUBESP,40
00402483|.8B0D38CC4000MOVECX,DWORDPTRDS:[40CC38]
00402489|.56PUSHESI
0040248A|.8B3598914000MOVESI,DWORDPTRDS:[<&USER32.GetWindow>
00402490|.8D442404LEAEAX,DWORDPTRSS:[ESP+4]
00402494|.6A08PUSH8
00402496|.50PUSHEAX
00402497|.51PUSHECX
00402498|.FFD6CALLESI//取注册码第一段。
0040249A|.A134CC4000MOVEAX,DWORDPTRDS:[40CC34]
0040249F|.8D542409LEAEDX,DWORDPTRSS:[ESP+9]
004024A3|.6A08PUSH8
004024A5|.52PUSHEDX
004024A6|.50PUSHEAX
004024A7|.FFD6CALLESI//取注册码第二段。
004024A9|.8B1540CC4000MOVEDX,DWORDPTRDS:[40CC40]
004024AF|.8D4C240ELEAECX,DWORDPTRSS:[ESP+E]
004024B3|.6A08PUSH8
004024B5|.51PUSHECX
004024B6|.52PUSHEDX
004024B7|.FFD6CALLESI//取注册码第三段。
004024B9|.8B0D3CCC4000MOVECX,DWORDPTRDS:[40CC3C]
004024BF|.8D442413LEAEAX,DWORDPTRSS:[ESP+13]
004024C3|.6A08PUSH8
004024C5|.50PUSHEAX
004024C6|.51PUSHECX
004024C7|.FFD6CALLESI//取注册码第四段。
004024C9|.8B1530CC4000MOVEDX,DWORDPTRDS:[40CC30]
004024CF|.6800010000PUSH100
004024D4|.B02DMOVAL,2D//2D就是减号'-'。
004024D6|.6880CD4000PUSHAUTHREG.0040CD80
004024DB|.52PUSHEDX
004024DC|.8844241EMOVBYTEPTRSS:[ESP+1E],AL//减号分别放到输入的注册码内。隔4位一个。
004024E0|.88442419MOVBYTEPTRSS:[ESP+19],AL
004024E4|.88442414MOVBYTEPTRSS:[ESP+14],AL
004024E8|.C644242300MOVBYTEPTRSS:[ESP+23],0
004024ED|.FFD6CALLESI//取用户名。
004024EF|.A158C74000MOVEAX,DWORDPTRDS:[40C758]
004024F4|.5EPOPESI
004024F5|.85C0TESTEAX,EAX
004024F7|.740EJESHORTAUTHREG.00402507
004024F9|.8D4C2400LEAECX,DWORDPTRSS:[ESP]
004024FD|.51PUSHECX
004024FE|.6880CD4000PUSHAUTHREG.0040CD80
00402503|.FFD0CALLEAX//这个函数就是计算注册码的地方。(*)
00402505|.EB0FJMPSHORTAUTHREG.00402516//这里跳走。
00402507|>8D542400LEAEDX,DWORDPTRSS:[ESP]
0040250B|.52PUSHEDX
0040250C|.6880CD4000PUSHAUTHREG.0040CD80
00402511|.E8DA140000CALLAUTHREG.004039F0
00402516|>33C9XORECX,ECX
00402518|.8D542400LEAEDX,DWORDPTRSS:[ESP]
0040251C|.85C0TESTEAX,EAX//如果注册码正确则EAX返回1。这里测试EAX是否非0。
0040251E|.0F95C1SETNECL//如果EAX非0,让CL=1。
00402521|.52PUSHEDX
00402522|.6880CD4000PUSHAUTHREG.0040CD80
00402527|.890D40F84000MOVDWORDPTRDS:[40F840],ECX
0040252D|.E82E000000CALLAUTHREG.00402560//加密注册码。
00402532|.8B4C244CMOVECX,DWORDPTRSS:[ESP+4C]
00402536|.8B1564CD4000MOVEDX,DWORDPTRDS:[40CD64]
0040253C|.83C408ADDESP,8
0040253F|.8D442400LEAEAX,DWORDPTRSS:[ESP]
00402543|.50PUSHEAX
00402544|.68E0234000PUSHAUTHREG.004023E0
00402549|.51PUSHECX
0040254A|.6A69PUSH69
0040254C|.52PUSHEDX
0040254D|.FF15C4914000CALLDWORDPTRDS:[<&USER32.DialogBoxPar>//显示成功注册信息。
00402553|.A140F84000MOVEAX,DWORDPTRDS:[40F840]
00402558|.83C440ADDESP,40
0040255B\.C3RETN
======================================================================
下面进入(*)处的函数分析:
:100010A083EC20subesp,00000020
:100010A356pushesi
:100010A457pushedi
:100010A5B908000000movecx,00000008
:100010AA33C0xoreax,eax
:100010AC8D7C2408leaedi,dwordptr[esp+08]
:100010B0F3repz
:100010B1ABstosd
:100010B28B44242Cmoveax,dwordptr[esp+2C]
:100010B650pusheax
:100010B7E884010000call10001240//这个函数处理用户名(**)
:100010BC83C404addesp,00000004
:100010BF89442408movdwordptr[esp+08],eax
:100010C333F6xoresi,esi
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:100010E8(C)
|
:100010C50FBE443408movsxeax,byteptr[esp+esi+08]//依次取前面最后一次得到的4位用户名字符送EAX。
:100010CA83F841cmpeax,00000041//与41比较。
:100010CD7C08jl100010D7//如果小于41,则进行处理。
:100010CF83F85Acmpeax,0000005A//与5A比较
:100010D27F03jg100010D7//如果大于5A,则进行处理。
:100010D483C020addeax,00000020//否则加20,把大写字符变为小写字符。
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddresses:
|:100010CD(C),:100010D2(C)
|
:100010D750pusheax
:100010D8E813020000call100012F0//这个函数把处理过的用户名转换为可见字符。(***)
:100010DD83C404addesp,00000004
:100010E088443408movbyteptr[esp+esi+08],al
:100010E446incesi
:100010E583FE04cmpesi,00000004
:100010E87CDBjl100010C5
:100010EA8B7C2430movedi,dwordptr[esp+30]//注册码地址送EDI。
:100010EE8D4C2408leaecx,dwordptr[esp+08]//变换后的注册码地址送ECX。
:100010F28BF7movesi,edi
:100010F433D2xoredx,edx
:100010F62BF1subesi,ecx
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:1000111C(C)
|
:100010F88D4C1408leaecx,dwordptr[esp+edx+08]
:100010FC0FBE040Emovsxeax,byteptr[esi+ecx]//注册码逐位送EAX。
:1000110083F841cmpeax,00000041
:100011037C08jl1000110D
:1000110583F85Acmpeax,0000005A
:100011087F03jg1000110D
:1000110A83C020addeax,00000020
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddresses:
|:10001103(C),:10001108(C)
|
:1000110D0FBE09movsxecx,byteptr[ecx]//第一组真码依次送ECX。
:100011103BC1cmpeax,ecx//真假比较。这里就应该用E命令修改你的假码为真码了。以下类推。
:100011120F8514010000jne1000122C//不等则OVER。
:1000111842incedx
:1000111983FA04cmpedx,00000004
:1000111C7CDAjl100010F8//没有比较完,则继续。
:1000111E8B442408moveax,dwordptr[esp+08]//变换后的用户名4位送EAX。
:100011228D5008leaedx,dwordptr[eax+08]
:100011250FAFD0imuledx,eax//EDX=EDX*EAX。
:100011288954240Cmovdwordptr[esp+0C],edx//结果送到[ESP+C]处。
:1000112C33F6xoresi,esi
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:1000114F(C)
|
:1000112E8A44340Cmoval,byteptr[esp+esi+0C]
:1000113250pusheax
:1000113356pushesi
:10001134E847030000call10001480//这个函数把变换得到的新4位注册码进一步变换。(****)
:1000113925FF000000andeax,000000FF
:1000113E50pusheax
:1000113FE8AC010000call100012F0//变为可见字符。
:1000114483C40Caddesp,0000000C
:100011478844340Cmovbyteptr[esp+esi+0C],al
:1000114B46incesi
:1000114C83FE04cmpesi,00000004
:1000114F7CDDjl1000112E
:1000115133C9xorecx,ecx
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:10001176(C)
|
:100011530FBE440F05movsxeax,byteptr[edi+ecx+05]//下面是比较真假注册码的循环。
:1000115883F841cmpeax,00000041
:1000115B7C08jl10001165
:1000115D83F85Acmpeax,0000005A
:100011607F03jg10001165
:1000116283C020addeax,00000020
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddresses:
|:1000115B(C),:10001160(C)
|
:100011650FBE540C0Cmovsxedx,byteptr[esp+ecx+0C]
:1000116A3BC2cmpeax,edx
:1000116C0F85BA000000jne1000122C
:1000117241incecx
:1000117383F904cmpecx,00000004
:100011767CDBjl10001153
:100011788B44240Cmoveax,dwordptr[esp+0C]//第一段注册码送EAX。
:1000117C8B4C2408movecx,dwordptr[esp+08]//第二段注册码送ECX。
:100011808BD0movedx,eax
:1000118233D1xoredx,ecx//异或运算
:1000118442incedx//加1。
:100011850FAFD1imuledx,ecx//EDX=EDX*ECX
:1000118803D0addedx,eax//EDX=EDX+EAX。
:1000118A33F6xoresi,esi
:1000118C89542410movdwordptr[esp+10],edx//保存结果。
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:100011A6(C)
|
:100011900FBE443410movsxeax,byteptr[esp+esi+10]
:1000119550pusheax
:10001196E855010000call100012F0//处理刚刚得到的4位结果。
:1000119B83C404addesp,00000004
:1000119E88443410movbyteptr[esp+esi+10],al//保存
:100011A246incesi
:100011A383FE04cmpesi,00000004
:100011A67CE8jl10001190
:100011A833C9xorecx,ecx
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:100011C9(C)
|
:100011AA0FBE440F0Amovsxeax,byteptr[edi+ecx+0A]//下面是比较第三段注册码的循环。
:100011AF83F841cmpeax,00000041
:100011B27C08jl100011BC
:100011B483F85Acmpeax,0000005A
:100011B77F03jg100011BC
:100011B983C020addeax,00000020
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddresses:
|:100011B2(C),:100011B7(C)
|
:100011BC0FBE540C10movsxedx,byteptr[esp+ecx+10]
:100011C13BC2cmpeax,edx
:100011C37567jne1000122C
:100011C541incecx
:100011C683F904cmpecx,00000004
:100011C97CDFjl100011AA
:100011CB8B4C240Cmovecx,dwordptr[esp+0C]//第二段注册码送ECX。
:100011CF8B442408moveax,dwordptr[esp+08]//第一段注册码送EAX。
:100011D30FAFC8imulecx,eax//相乘
:100011D641incecx//ECX增一。
:100011D70FAF4C2410imulecx,dwordptr[esp+10]//ECX与第三段相乘。
:100011DC03C8addecx,eax//ECX加EAX。
:100011DE33F6xoresi,esi
:100011E0894C2414movdwordptr[esp+14],ecx//保存。
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:100011FA(C)
|
:100011E40FBE543414movsxedx,byteptr[esp+esi+14]//处理刚刚得到的第四段注册码。
:100011E952pushedx
:100011EAE801010000call100012F0
:100011EF83C404addesp,00000004
:100011F288443414movbyteptr[esp+esi+14],al
:100011F646incesi
:100011F783FE04cmpesi,00000004
:100011FA7CE8jl100011E4
:100011FC33C9xorecx,ecx
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:1000121D(C)
|
:100011FE0FBE440F0Fmovsxeax,byteptr[edi+ecx+0F]//下面是第四段注册码的比较循环。
:1000120383F841cmpeax,00000041
:100012067C08jl10001210
:1000120883F85Acmpeax,0000005A
:1000120B7F03jg10001210
:1000120D83C020addeax,00000020
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddresses:
|:10001206(C),:1000120B(C)
|
:100012100FBE540C14movsxedx,byteptr[esp+ecx+14]
:100012153BC2cmpeax,edx
:100012177513jne1000122C
:1000121941incecx
:1000121A83F904cmpecx,00000004
:1000121D7CDFjl100011FE
:1000121F5Fpopedi
:10001220B801000000moveax,00000001//注册码正确则让EAX=1。
:100012255Epopesi
:1000122683C420addesp,00000020
:10001229C20800ret0008
======================================================================
下面分析:100010B7处的函数call10001240:
|:100010B7
|
:1000124081EC00020000subesp,00000200
:10001246B980000000movecx,00000080
:1000124B33C0xoreax,eax
:1000124D53pushebx
:1000124E55pushebp
:1000124F56pushesi
:100012508BB42410020000movesi,dwordptr[esp+00000210]
:1000125757pushedi
:100012588D7C2410leaedi,dwordptr[esp+10]
:1000125CF3repz
:1000125DABstosd
:1000125E56pushesi
:1000125F33EDxorebp,ebp
*ReferenceTo:KERNEL32.lstrlenA,Ord:0308h
|
:10001261FF1504600010Calldwordptr[10006004]//求串长。
:100012678BD8movebx,eax
:1000126981FB00020000cmpebx,00000200
:1000126F7612jbe10001283
:10001271B980000000movecx,00000080
:100012768D7C2410leaedi,dwordptr[esp+10]
:1000127ABB00020000movebx,00000200
:1000127FF3repz
:10001280A5movsd
:10001281EB0Cjmp1000128F
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:1000126F(C)
|
:100012838D442410leaeax,dwordptr[esp+10]
:1000128756pushesi
:1000128850pusheax
*ReferenceTo:KERNEL32.lstrcpyA,Ord:0302h
|
:10001289FF1584600010Calldwordptr[10006084]//串拷贝。
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:10001281(U)
|
:1000128F8BC3moveax,ebx//用户名串长送EAX。
:1000129199cdq
:1000129283E203andedx,00000003
:1000129503C2addeax,edx
:100012978BF8movedi,eax//串长送EDI。
:10001299C1FF02saredi,02//右循环2次,相当于除以4。
:1000129CF6C303testbl,03
:1000129F7401je100012A2//此处跳走。
:100012A147incedi
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:1000129F(C)
|
:100012A233C9xorecx,ecx
:100012A485DBtestebx,ebx
:100012A67E17jle100012BF
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:100012BD(C)
|
:100012A88A540C10movdl,byteptr[esp+ecx+10]//依次取用户名送DL。
:100012AC52pushedx
:100012AD51pushecx
:100012AEE8CD010000call10001480//这个函数变换每位字符。
:100012B383C408addesp,00000008
:100012B688440C10movbyteptr[esp+ecx+10],al//转换后的字符送原来位置。
:100012BA41incecx
:100012BB3BCBcmpecx,ebx
:100012BD7CE9jl100012A8//循环直至处理完用户名。
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:100012A6(C)
|
:100012BF33F6xoresi,esi
:100012C185FFtestedi,edi
:100012C37E1Cjle100012E1
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:100012DF(C)
|
:100012C58B5CB410movebx,dwordptr[esp+4*esi+10]//转换完的前4位送EBX。
:100012C98BC6moveax,esi//ESI值送EAX。ESI初始值是0。
:100012CB83E01Fandeax,0000001F
:100012CE03EBaddebp,ebx
:100012D050pusheax//循环移位次数参数进栈。
:100012D155pushebp//四位用户名字符参数进栈。
:100012D2E879020000call10001550//把用户名循环移位EAX次。
:100012D783C408addesp,00000008
:100012DA46incesi
:100012DB3BF7cmpesi,edi//EDI为循环次数。
:100012DD8BE8movebp,eax//循环移位后的结果送EBP。
:100012DF7CE4jl100012C5//没有结束,则继续循环。
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:100012C3(C)
|
:100012E15Fpopedi
:100012E28BC5moveax,ebp//把最后一次得到的4位用户名送EAX返回主调函数。
:100012E45Epopesi
:100012E55Dpopebp
:100012E65Bpopebx
:100012E781C400020000addesp,00000200
:100012EDC3ret
======================================================================
下面是:100010D8处的函数call100012F0的代码(***):
:100012F08B442404moveax,dwordptr[esp+04]
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddresses:
|:10001312(U),:10001323(U),:1000133E(U)
|
:100012F483E07Fandeax,0000007F
:100012F783F841cmpeax,00000041//判断是否是在字符'A'与'Z'之间。
:100012FA7C07jl10001303
:100012FC83F85Acmpeax,0000005A
:100012FF7F02jg10001303//如果不在A-Z之间,则跳。
:100013010C20oral,20//如果在A-Z之间,则变为小写字符。
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddresses:
|:100012FA(C),:100012FF(C)
|
:1000130383F86Fcmpeax,0000006F//判断是否是字符'o'。
:10001306750Cjne10001314
:10001308B890000000moveax,00000090
:1000130D83F00Exoreax,0000000E
:100013100C31oral,31
:10001312EBE0jmp100012F4
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:10001306(C)
|
:1000131483F830cmpeax,00000030//判断是否是0。
:10001317750Cjne10001325//如果不是则跳。
:10001319B8CF000000moveax,000000CF
:1000131E83F00Exoreax,0000000E
:100013210C31oral,31
:10001323EBCFjmp100012F4
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:10001317(C)
|
:1000132583F861cmpeax,00000061//判断是否在a-z之间。
:100013287C05jl1000132F//如果小于a则跳。
:1000132A83F87Acmpeax,0000007A
:1000132D7E11jle10001340//如果在a-z之间则OK。
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:10001328(C)
|
:1000132F83F831cmpeax,00000031//判断是否在1-9之间。
:100013327C05jl10001339
:1000133483F839cmpeax,00000039
:100013377E07jle10001340//如果在1-9之间也OK。
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:10001332(C)
|
:1000133983F00Exoreax,0000000E//如果小于1则与E异或。
:1000133C0C31oral,31//加31。
:1000133EEBB4jmp100012F4
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddresses:
|:1000132D(C),:10001337(C)
|
:10001340C3ret
======================================================================
下面是(****)处的函数调用分析::10001134call10001480
:1000148055pushebp
:100014818BECmovebp,esp
:1000148353pushebx
:100014848A4508moval,byteptr[ebp+08]
:100014878A5D0Cmovbl,byteptr[ebp+0C]//取每位注册码送BL。
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:10001494(C)
|
:1000148AF6C3C3testbl,C3//与C3(11000011)进行"与"运算。
:1000148D7A01jpe10001490//如果为偶数个1则跳。
:1000148FF9stc//否则置进位标志为1。
*Referencedbya(U)nconditionalor(C)onditionalJumpatAddress:
|:1000148D(C)
|
:10001490D0DBrcrbl,1//BL带进位C右循环一次。
:10001492FEC8decal//计数器减一。
:1000149475F4jne1000148A//继续循环。
:10001496885D0Cmovbyteptr[ebp+0C],bl//循环完毕,BL保存。
:100014998A450Cmoval,byteptr[ebp+0C]//送AL返回主调函数。
:1000149C5Bpopebx
:1000149D5Dpopebp
:1000149EC3ret
======================================================================
后记:
我不知道豪杰是否一直采用这样的注册码生成算法,不过这些老软件倒是基本一样的思路,他们的新版本我没有得到,暂时无法尝试。本文只针对菜鸟,大侠切莫见笑!!恭祝各位坛友新春愉快!万事如意!
结论:
用户名:wanggang
注册码:7zq3-d53t-pht3-gxva