文章作者: killl
【破文标题】某英语复读软件MFC简单算法
【破文作者】KiLlL
【破解时间】2006-01-2123:25
【破解声明】仅为技术交流之用!
【破解过程】
很好的复读软件,这里仅做算法分析。名字省去了。
MFC,没有壳,很方便。机器码的生成用到了dll,那个dll是aspack压缩的。
根据错误提示的对话框很容易定位到下面:
0041A88F.E8BC7AFFFFcall00412350//关键call
0041A894.84C0testal,al
0041A896.53pushebx
0041A897.6858A84200push0042A858
0041A89C.752Djnzshort0041A8CB//注册成功
开始直接爆破00412350,让al返回值为1,结果发现有暗桩,某些功能在使用时还是有提示,索性跟进去看看:
0041235051pushecx
0041235153pushebx
0041235255pushebp
00412353|.56pushesi
00412354|.57pushedi
00412355|.8BE9movebp,ecx
00412357|.E874110000call004134D0
0041235C|.8BCDmovecx,ebp
0041235E|.E85D110000call004134C0
00412363|.8BCDmovecx,ebp
00412365|.E8F60A0000call00412E60
0041236A|.8B4514moveax,[arg.4]
0041236D|.8D7D14leaedi,[arg.4]
00412370|.8378F808cmpdwordptrds:[eax-8],8;注册码8位
004123747408jeshort0041237E
004123765Fpopedi
004123775Epopesi
004123785Dpopebp
00412379|.32C0xoral,al
0041237B|.5Bpopebx
0041237C|.59popecx
0041237D|.C3retn
0041237E|>8BCFmovecx,edi
00412380|.E88DCD0000call
00412385|.8B7518movesi,[arg.5];我的机器码"46674926D"
00412388|.8B3Fmovedi,dwordptrds:[edi];假码12345678
0041238A|.0FBE4605movsxeax,byteptrds:[esi+5];机器码第六位9
0041238E|.0FBE16movsxedx,byteptrds:[esi];机器码第一位4
00412391|.8D0C40leaecx,dwordptrds:[eax+eax*2];39*3=AB
00412394|.8D0488leaeax,dwordptrds:[eax+ecx*4];eax=eax+AB*4
00412397|.8BCAmovecx,edx;34
00412399|.C1E105shlecx,5;ecx=ecx*2^5
0041239C|.03CAaddecx,edx;ecx=680+34
0041239E|.8D0C49leaecx,dwordptrds:[ecx+ecx*2];ecx=ecx*36b4*3=141c
004123A1|.8D144Aleaedx,dwordptrds:[edx+ecx*2];edx+=ecx*2286c
004123A4|.B924000000movecx,24
004123A9|.03C2addeax,edx;eax+=edx2e5+286c
004123AB|.33D2xoredx,edx
004123AD|.F7F1divecx;2b51/24
004123AF|.0FBE0Fmovsxecx,byteptrds:[edi];注册码第一位
004123B2|.8D41D0leaeax,dwordptrds:[ecx-30];Switch(cases30..7A)
004123B5|.83F84Acmpeax,4A;ascii-30
004123B8|.0F8734010000ja004124F2
004123BE|.33DBxorebx,ebx
004123C0|.8A98302A4100movbl,byteptrds:[eax+412A30];查表
004123C6|.FF249D9C294100jmpdwordptrds:[ebx*4+41299C]
004123CD|>B813000000moveax,13;Case31('1')ofswitch004123B2
004123D2|.E920010000jmp004124F7
004123D7|>B802000000moveax,2;Case32('2')ofswitch004123B2
004123DC|.E916010000jmp004124F7
004123E1|>B801000000moveax,1;Case33('3')ofswitch004123B2
004123E6|.E90C010000jmp004124F7
004123EB|>B807000000moveax,7;Case34('4')ofswitch004123B2
004123F0|.E902010000jmp004124F7
004123F5|>B822000000moveax,22;Case35('5')ofswitch004123B2
004123FA|.E9F8000000jmp004124F7
004123FF|>B805000000moveax,5;Case36('6')ofswitch004123B2
00412404|.E9EE000000jmp004124F7
00412409|>B804000000moveax,4;Case37('7')ofswitch004123B2
0041240E|.E9E4000000jmp004124F7
00412413|>B810000000moveax,10;Case38('8')ofswitch004123B2
00412418|.E9DA000000jmp004124F7
0041241D|>33C0xoreax,eax;Case39('9')ofswitch004123B2
0041241F|.E9D3000000jmp004124F7
00412424|>B811000000moveax,11;Case30('0')ofswitch004123B2
00412429|.E9C9000000jmp004124F7
0041242E|>B81C000000moveax,1C;Case61('a')ofswitch004123B2
00412433|.E9BF000000jmp004124F7
00412438|>B80C000000moveax,0C;Case62('b')ofswitch004123B2
0041243D|.E9B5000000jmp004124F7
00412442|>B814000000moveax,14;Case63('c')ofswitch004123B2
00412447|.E9AB000000jmp004124F7
0041244C|>B80E000000moveax,0E;Case64('d')ofswitch004123B2
00412451|.E9A1000000jmp004124F7
00412456|>B80D000000moveax,0D;Case65('e')ofswitch004123B2
0041245B|.E997000000jmp004124F7
00412460|>B808000000moveax,8;Case66('f')ofswitch004123B2
00412465|.E98D000000jmp004124F7
0041246A|>B809000000moveax,9;Case67('g')ofswitch004123B2
0041246F|.E983000000jmp004124F7
00412474|>B812000000moveax,12;Case68('h')ofswitch004123B2
00412479|.EB7Cjmpshort004124F7
0041247B|>B803000000moveax,3;Case69('i')ofswitch004123B2
00412480|.EB75jmpshort004124F7
00412482|>B80F000000moveax,0F;Case6A('j')ofswitch004123B2
00412487|.EB6Ejmpshort004124F7
00412489|>B815000000moveax,15;Case6B('k')ofswitch004123B2
0041248E|.EB67jmpshort004124F7
00412490|>B81B000000moveax,1B;Case6C('l')ofswitch004123B2
00412495|.EB60jmpshort004124F7
00412497|>B818000000moveax,18;Case6D('m')ofswitch004123B2
0041249C|.EB59jmpshort004124F7
0041249E|>B817000000moveax,17;Case6E('n')ofswitch004123B2
004124A3|.EB52jmpshort004124F7
004124A5|>B821000000moveax,21;Case6F('o')ofswitch004123B2
004124AA|.EB4Bjmpshort004124F7
004124AC|>B81A000000moveax,1A;Case70('p')ofswitch004123B2
004124B1|.EB44jmpshort004124F7
004124B3|>B816000000moveax,16;Case72('r')ofswitch004123B2
004124B8|.EB3Djmpshort004124F7
004124BA|>B81D000000moveax,1D;Case73('s')ofswitch004123B2
004124BF|.EB36jmpshort004124F7
004124C1|>B81E000000moveax,1E;Case74('t')ofswitch004123B2
004124C6|.EB2Fjmpshort004124F7
004124C8|>B823000000moveax,23;Case75('u')ofswitch004123B2
004124CD|.EB28jmpshort004124F7
004124CF|>B820000000moveax,20;Case76('v')ofswitch004123B2
004124D4|.EB21jmpshort004124F7
004124D6|>B819000000moveax,19;Case77('w')ofswitch004123B2
004124DB|.EB1Ajmpshort004124F7
004124DD|>B806000000moveax,6;Case78('x')ofswitch004123B2
004124E2|.EB13jmpshort004124F7
004124E4|>B81F000000moveax,1F;Case79('y')ofswitch004123B2
004124E9|.EB0Cjmpshort004124F7
004124EB|>B80A000000moveax,0A;Case7A('z')ofswitch004123B2
004124F0|.EB05jmpshort004124F7
004124F2|>B80B000000moveax,0B;Defaultcaseofswitch004123B2
004124F7|>3BD0cmpedx,eax
004124F9|.740Cjeshort00412507
004124FB|.5Fpopedi
004124FC|.C6450F10movbyteptrss:[ebp+F],10
00412500|.5Epopesi
00412501|.5Dpopebp
00412502|.32C0xoral,al
00412504|.5Bpopebx
00412505|.59popecx
00412506|.C3retn
这里首先验证注册码位数,然后逐位验证。验证时没有出现真正的注册码,而是通过假码第一位跟第六位运行后得到一个数字,再利用假码第一位查表得到另外一个数字,比较这两个数字,相等则继续,否则注册失败。
注意movbyteptrss:[ebp+F],10这句,成功了是movbyteptrss:[ebp+F],13,这里估计是个暗桩。有兴趣下个读断点试试。
给出vb的注册机源码,对比程序代码很容易看:
str="1234567890abcdefghijklmnoprstuvwxyz{"
str1="130201072205041000111c0c140e0d080912030f151b1817211a161d1e232019061f0a0b"
eax=Asc(Mid$(mc,6,1))
edx=Asc(Mid$(mc,1,1))
ecx=eax*3
eax=eax+ecx*4
ecx=edx
ecx=ecx*2^5
ecx=ecx+edx
ecx=ecx*3
edx=edx+ecx*2
ecx=&H24
eax=eax+edx
edx=eaxModecx
Fori=1ToLen(str1)-1Step2
IfRight$("00"&Hex$(edx),2)=UCase$(Mid$(str1,i,2))Then
Tmp=Mid$(str,i\2+1,1)
ExitFor
EndIf
Next
IfTmp=""ThenTmp="{"
后面的7位计算方法与之类似。有几个地方要注意:
004128E8|.C6450C01movbyteptrss:[ebp+C],1;1
00412941|.C6450D01movbyteptrss:[ebp+D],1;2
0041298E|.C6450F13movbyteptrss:[ebp+F],13;3
【破解心得】
有了mfc的Lib文件,mfc的函数一目了然,所以很容易看清注册流程。该软件的思路不错,在关键call里面设置了其他返回数据,因此不能采取在函数开始直接返回al值的方法来爆破,但是还是很容易找到关键点。注册成功后结果保存在注册表内。
关于利用dll取得机器码的方法不是很可靠,简单编写一个dll就可以一码多用了。