【文章标题】: 动态生成代码CrackMe(De-KryptZo1)分析
【文章作者】: 壹只老虎
【作者邮箱】: tiger..tiger@163.com
【软件名称】: De-KryptZo1
【软件大小】: 5.50 KB
【下载地址】: http://www.crackmes.de/
【加壳方式】: 无
【保护方式】: 动态代码生成+程序崩溃
【编写语言】: 不知道
【使用工具】: od+peid
【操作平台】: windowsxp sp2
【软件介绍】: http://www.crackmes.de/下载的一个crackme
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
先贴一下作者关于本crackme的说明,对分析有些帮助。
===============================================================================
De-KryptZo - 1
No PATCHING
NO BRUTE FORCING
FIND SERIAL(S)
WRITE TUT
Unless you Enter a Valid Serial ... the Decryption will be wrong and the
app will crash !
On entering a Correct Serial ... you will get a goodboy message !
starzboy / iCU
starzboy@gmail.com
===============================================================================
作者的意思好像是说:不要补丁,不要爆破。
当输入正确注册码时,你将得到一个"goodboy"消息框,
当输入错误时,本程序会错误,并且崩溃。
非常有意思,只得分析一下。毕竟对于我来说,算是一个新东西,对里面的奥秘很是好奇。
运行了一下,随便输入了一个密码,获取hash值,解密,ok,很好,程序崩溃了。
如果没有看过说明,我肯定会认为是程序设计的问题。但是看了说明之后,就知道是作者故意这么写的。
peid和fi查都没有提示,不确定是否有壳。
od载入,没有加密提示,应该没有壳。
字符串参考,看一下,发现正确提示“Decryption Successfull ... Good Work !”,
一切都很顺利,双击过去,
来到了这里:
==============================================================================
00401215 /EB 14 jmp short De-Krypt.0040122B
00401217 |6A 00 push 0
00401219 |68 56304000 push De-Krypt.00403056 ; ASCII "[iCU]"
0040121E |68 2F304000 push De-Krypt.0040302F ; ASCII "Decryption Successfull ... Good Work !"
00401223 |FF75 08 push dword ptr ss:[ebp+8]
00401226 |E8 6D000000 call <jmp.&user32.MessageBoxA>
0040122B \90 nop
0040122C 90 nop
0040122D 90 nop
0040122E 90 nop
0040122F 90 nop
00401230 90 nop
00401231 90 nop
00401232 90 nop
00401233 90 nop
00401234 90 nop
00401235 90 nop
00401236 90 nop
00401237 90 nop
00401238 90 nop
00401239 90 nop
0040123A 90 nop
0040123B 90 nop
0040123C 90 nop
0040123D 90 nop
0040123E 90 nop
0040123F 90 nop
00401240 90 nop
00401241 90 nop
00401242 90 nop
00401243 90 nop
===============================================================================
上面这段空白不是为了增加篇幅的,呵呵!后面就知道这里我为什么要贴出来了。
说明一下,上面这个00401217位置就是正确提示的入口。不要高兴的太早。
我上下找了一下,没有一个地方是跳向这里的。也就是说,这是一段永远不可能被执行的代码,
也就是说,永远不可能成功?
当然不是,肯定另有玄机,我也不知道有什么玄机。
我的习惯,规规矩矩的先把前面的算法分析清楚了再说吧。
先在取得试验码的地方下一个断点。
004010C0 E8 CD010000 call <jmp.&user32.GetDlgItemTextA> ; 取得试验码
开始分析吧,运行起来,输入试验码123。“hash it!”,程序断在004010C0。
先看试验码处理算法:
===============================================================================
004010FB /E9 44010000 jmp De-Krypt.00401244
00401100 |33C0 xor eax,eax ; eax=0
00401102 |33F6 xor esi,esi ; esi=0
00401104 |33C9 xor ecx,ecx ; ecx=0
00401106 |BA 94000000 mov edx,94 ; edx=94H
0040110B |33FF xor edi,edi ; edi=0
0040110D |B3 64 mov bl,64 ; bl=64h
0040110F |BE 60304000 mov esi,De-Krypt.00403060 ; esi=试验码地址
00401114 |8A06 mov al,byte ptr ds:[esi] ; al=按位取试验码
00401116 |8AC8 mov cl,al ; cl=al
00401118 |32D8 xor bl,al ; bl=bl xor al
0040111A |02C3 add al,bl ; al=al+bl
0040111C |02C1 add al,cl ; al=al+cl
0040111E |04 32 add al,32 ; al=al+32h
00401120 |34 35 xor al,35 ; al=al xor 35h
00401122 |02D0 add dl,al ; dl=dl+al
00401124 |2C F6 sub al,0F6 ; al=al-f6
00401126 |02D0 add dl,al ; dl=dl+al
00401128 |46 inc esi ; esi=esi+1
00401129 |FF4D FC dec dword ptr ss:[ebp-4]
0040112C |837D FC 00 cmp dword ptr ss:[ebp-4],0
00401130 ^|77 E2 ja short De-Krypt.00401114
00401132 |6A 00 push 0
00401134 |8AC2 mov al,dl
00401136 |66:0FB6C0 movzx ax,al
0040113A |66:50 push ax
0040113C |68 2C304000 push De-Krypt.0040302C ; ASCII "%x"
00401141 |68 60344000 push De-Krypt.00403460
00401146 |E8 29010000 call <jmp.&user32.wsprintfA> ; hash=上面的处理结果的16进制转化为字符串
===============================================================================
总结一下:功能是对试验码进行计算,得到一个hash值,转化其16进制形势为字符串,保存在hash里。
这是一段Delphi代码
/*****************************************************************************/
dl:=$94;
bl:=$64;
for i:=1 to length(pass) do
begin
al:=ord(name[i]);
bl:=bl xor al;
al:=al+bl+al+$32;
al:=al xor $35;
dl:=dl+al+al-$0f6;
end;
/*****************************************************************************/
下面还有一段算法,顺便一起给写了:
===============================================================================
004011C8 BE 30104000 mov esi,De-Krypt.00401030 ; esi=00401030
004011CD BF 2B124000 mov edi,De-Krypt.0040122B ; edi=0040122b
004011D2 8A0D 60344000 mov cl,byte ptr ds:[403460] ; cl=hash[0]
004011D8 884D F7 mov byte ptr ss:[ebp-9],cl ; [ebp-9]=cl
004011DB 8A0D 61344000 mov cl,byte ptr ds:[403461] ; cl=hash[i]
004011E1 004D F7 add byte ptr ss:[ebp-9],cl ; [ebp-9]=[ebp-9]+cl
004011E4 33C0 xor eax,eax ; eax=0
004011E6 8A06 mov al,byte ptr ds:[esi] ; al=按位取[esi]
004011E8 8A4D F7 mov cl,byte ptr ss:[ebp-9] ; cl=[ebp-9]
004011EB 2C 71 sub al,71 ; al=al-71h
004011ED FEC0 inc al ; al=al+1
004011EF 04 51 add al,51
004011F1 04 34 add al,34
004011F3 04 46 add al,46
004011F5 02C1 add al,cl
004011F7 04 53 add al,53
004011F9 04 54 add al,54
004011FB 04 20 add al,20
004011FD 2C 97 sub al,97
004011FF 2C 71 sub al,71
00401201 8807 mov byte ptr ds:[edi],al ; 关键
00401203 46 inc esi
00401204 47 inc edi
00401205 FF4D FC dec dword ptr ss:[ebp-4]
00401208 837D FC 00 cmp dword ptr ss:[ebp-4],0
0040120C ^ 77 D6 ja short De-Krypt.004011E4
0040120E C605 3B124000 59 mov byte ptr ds:[40123B],59
===============================================================================
总结:这段算法里面根据00401030开始的20个字节的内容和hash值,生成一组数据保存在0040122b开始的20个字节里面。
其实这段算法的关键在这里:
00401201 8807 mov byte ptr ds:[edi],al ; 关键
看到这里有助于后面的分析。
注意:00401030开始的20个字节内容是固定的一组数据。如下:
/*****************************************************************************/
const
table:array[1..20]of byte =($B4,$4A,$B2,$A0,$7A,$8A,$4A,$B2,$79,$7A,
$8A,$4A,$49,$BF,$52,$32,$CF,$4A,$4A,$4A);
/*****************************************************************************/
上面的算法改写后如下:
/*****************************************************************************/
key:=inttohex(dl,2);
key:=lowercase(key);
if key[1]='0' then
begin
key[1]:=key[2];
key[2]:=#0;
end;
cl:=ord(key[1])+ord(key[2]);
for i:=1 to 20 do
begin
al:=table[i];
al:=al+26;
al:=al+cl;
code[i]:=al;
end;
/*****************************************************************************/
好了,这个循环完了之后,就会发现一个很好玩的地方,你看----->
===============================================================================
0040120E C605 3B124000 59 mov byte ptr ds:[40123B],59
00401215 EB 14 jmp short De-Krypt.0040122B
00401217 6A 00 push 0
00401219 68 56304000 push De-Krypt.00403056 ; ASCII "[iCU]"
0040121E 68 2F304000 push De-Krypt.0040302F ; ASCII "Decryption Successfull ... Good Work !"
00401223 FF75 08 push dword ptr ss:[ebp+8]
00401226 E8 6D000000 call <jmp.&user32.MessageBoxA>
0040122B 92 xchg eax,edx
0040122C 2890 7E586828 sub byte ptr ds:[eax+2868587E],dl
00401232 90 nop
00401233 57 push edi
00401234 58 pop eax
00401235 68 28279D30 push 309D2728
0040123A 1059 28 adc byte ptr ds:[ecx+28],bl
0040123D 2828 sub byte ptr ds:[eax],ch
0040123F 90 nop
00401240 90 nop
00401241 90 nop
00401242 90 nop
00401243 90 nop
===============================================================================
看得很明白,发现什么了?
看看开始的时候这一段是什么数据。
前面我已经贴出来了。对比一下。
就明白,这一段代码是刚才生成的。
动态的哦,我还是第一次玩这种东西呢。
太有意思了。
(脱壳那种不算哈)
呵呵,我们慢慢的F8走吧。
当达到这里
0040122C 2890 7E586828 sub byte ptr ds:[eax+2868587E],dl
的时候,程序就异常退出了。
原因么:看看堆栈就明白了。
dl=28 ('(')
ds:[2868587F]=???,你说这能不异常吗?能不崩溃么?
其实我也是后来才注意到的,呵呵。
好了,现在能分析的都分析了。
我们现在还是没多少头绪。
我们现在需要总结一下。
我们的目标是要执行那个正确提示,就是说想方设法的要跳转到这里“00401217”,
怎么跳呢?00401215 语句是绝对跳转,只能是回跳了。
呵呵,如果生成代码是这样该多好:
push eax
mov eax,x
inc eax
mov x,eax
pop eax
cmp x,1
je 00401217
我一直在尝试,经过多次的失败,我发现,我的尝试是徒劳的.
在想想作者的说明,他说要找密码.再看看上面的两个算法,
虽然说很简单,但是很明显第一个算法是一个有损压缩算法.
所以没办法逆推到注册密码.所以只能是用注册码来推生成的代码.
(这个时候生成码是什么也就不重要了,只要能注册成功就可以了)
现在注册码不知道,已经是绝路了.怎么办?
眼前似乎还有一丝的光明.
注意一下:hash字符串是由dl转换来的,dl是8位的,也就是说两个16进制字母,
也就是说hash值最多2位,(可能为1位,高位为0),也就是说可能性最多256种,
看看hash的算法还可以知道dl只可能是偶数,可能性就减半,也就是128种,
好了,我们需要分析下程序的写法,为了方便,我们就用正整数来做密码,
每次加1来生成hash值,生成的hash值如果存在了就不保存,不存在就保存.
当hash值个数>127的时候,就跳出循环.输出分析结果到文件.
我也只能这么做了.
看看下面的代码.
/******************************************************************************/
function CrAck(t:string):integer;
var
al,bl,dl:byte;
i:integer;
begin
dl:=$94;
bl:=$64;
for i:=1 to length(t)do
begin
al:=ord(t[i]);
bl:=bl xor al;
al:=al+bl+al+$32;
al:=al xor $35;
dl:=dl+al+al-$0f6;
end;
result:=dl;
end;
tm:=tstringlist.Create();
rs:=tstringlist.Create();
i:=1;
while true do
begin
name:=inttostr(i);
dl:=CrAck(name);
j:=0;
while j<tm.Count do
begin
if tm[j]=inttostr(dl) then
break;
inc(j);
end;
if j=tm.Count then
begin
tm.Add(inttostr(dl));
rs.Add(name+' '+inttostr(dl));
end;
inc(i);
if rs.Count>127 then
break;
end;
rs.SaveToFile('res.txt');
/******************************************************************************/
上面这段代码,运行后就把结果保存到res.txt了,
打开文件来,一个一个测试吧,笨办法,不就128个吗?
慢慢来!下面是测试结果.
===============================================================================
1------>86(异常退出)
2------>80(异常退出)
3------>82(异常退出)
4------>92(异常退出)
5------>78------------>没有反应
6------>40(异常退出)
7------>42(异常退出)
8------>52(异常退出)
9------>6(异常退出)
10------>228---------->没有反应
11------>250(异常退出)
12------>240(异常退出)
13------>246-------------------->成功了
14------>252---------->没有反应
15------>242(异常退出)
16------>200(异常退出)
17------>206(异常退出)
18------>212(异常退出)
19------>170(异常退出)
20------>244(异常退出)
22------>248(异常退出)
23------>234---------->直接退出
24------>236(异常退出)
25------>238(异常退出)
27------>194(异常退出)
28------>164(异常退出)
29------>166(异常退出)
37------>198---------->没有反应
41------>230(异常退出)
42------>0(异常退出)
43------>2(异常退出)
47------>202(异常退出)
49------>214(异常退出)
51------>218(异常退出)
56------>184(异常退出)
57------>190(异常退出)
60------>180(异常退出)
61------>182(异常退出)
64------>156-------------------->成功了
65------>158(异常退出)
66------>160(异常退出)
67------>146(异常退出)
71------>186(异常退出)
75------>162(异常退出)
77------>150(异常退出)
84------>172(异常退出)
91------>122(异常退出)
92------>112(异常退出)
93------>118(异常退出)
94------>124(异常退出)
95------>114(异常退出)
96------>136(异常退出)
97------>142(异常退出)
98------>116(异常退出)
99------>138---------->没有反应
101------>168---------->没有反应
108------>130(异常退出)
109------>88(异常退出)
111------>188(异常退出)
117------>144(异常退出)
118------>154(异常退出)
119------>108---------->没有反应
120------>178(异常退出)
125------>176(异常退出)
126------>174---------->直接退出
127------>132(异常退出)
128------>98(异常退出)
129------>104(异常退出)
138------>106(异常退出)
143------>196(异常退出)
146------>134(异常退出)
147------>140(异常退出)
149------>152(异常退出)
156------>126-------------------->成功了
157------>128(异常退出)
161------>120(异常退出)
164------>90(异常退出)
165------>96(异常退出)
166------>94(异常退出)
167------>84(异常退出)
175------>100(异常退出)
176------>102(异常退出)
191------>60(异常退出)
192------>54(异常退出)
193------>56(异常退出)
194------>66(异常退出)
198------>58(异常退出)
199------>76(异常退出)
219------>110(异常退出)
224------>192(异常退出)
283------>46(异常退出)
284------>48(异常退出)
285------>50(异常退出)
287------>70(异常退出)
288------>72(异常退出)
289------>74(异常退出)
291------>62(异常退出)
391------>64(异常退出)
424------>148(异常退出)
626------>68(异常退出)
648------>16(异常退出)
649------>18(异常退出)
659------>22(异常退出)
676------>36(异常退出)
748------>14(异常退出)
749------>20(异常退出)
759------>24(异常退出)
777------>44(异常退出)
779------>8(异常退出)
911------>12(异常退出)
915------>4(异常退出)
916------>30(异常退出)
917------>32(异常退出)
918------>10(异常退出)
919------>28(异常退出)
926------>254(异常退出)
938------>26(异常退出)
980------>34(异常退出)
989------>232(异常退出)
1049------>38(异常退出)
1067------>226(异常退出)
1094------>208(异常退出)
1096------>220(异常退出)
1097------>222---------->没有反应
1191------>224(异常退出)
1195------>216-------------------->成功了
1284------>204---------->直接退出
1285------>210(异常退出)
===============================================================================
通过结果,我们发现,正确的注册码有4个,还有没有反应的代码有8个,直接退出的注册码有3个,其它都是崩溃的注册码.
现在密码找到了,我们来看看这几类密码生成的汇编代码吧.如下:
===============================================================================
key=5------------>没有反应
00401215 /EB 14 jmp short De-Krypt.0040122B
00401217 |6A 00 push 0
00401219 |68 56304000 push De-Krypt.00403056 ; ASCII "[iCU]"
0040121E |68 2F304000 push De-Krypt.0040302F ; ASCII "Decryption Successfull ... Good Work !"
00401223 |FF75 08 push dword ptr ss:[ebp+8]
00401226 |E8 6D000000 call <jmp.&user32.MessageBoxA>
0040122B \67:FD std
0040122D 65:53 push ebx
0040122F 2D 3DFD652C sub eax,2C65FD3D
00401234 2D 3DFDFC72 sub eax,72FCFD3D
00401239 05 E559FDFD add eax,FDFD59E5
0040123E FD std
0040123F 90 nop
00401240 90 nop
00401241 90 nop
00401242 90 nop
00401243 90 nop
===============================================================================
key=23---------->直接退出
00401215 /EB 14 jmp short De-Krypt.0040122B
00401217 |6A 00 push 0
00401219 |68 56304000 push De-Krypt.00403056 ; ASCII "[iCU]"
0040121E |68 2F304000 push De-Krypt.0040302F ; ASCII "Decryption Successfull ... Good Work !"
00401223 |FF75 08 push dword ptr ss:[ebp+8]
00401226 |E8 6D000000 call <jmp.&user32.MessageBoxA>
0040122B \94 xchg eax,esp
0040122C 2A92 805A6A2A sub dl,byte ptr ds:[edx+2A6A5A80]
00401232 92 xchg eax,edx
00401233 59 pop ecx
00401234 5A pop edx
00401235 6A 2A push 2A
00401237 299F 3212592A sub dword ptr ds:[edi+2A591232],ebx
0040123D 2A2A sub ch,byte ptr ds:[edx]
0040123F 90 nop
00401240 90 nop
00401241 90 nop
00401242 90 nop
00401243 90 nop
===============================================================================
key=13---------->成功了
00401215 /EB 14 jmp short De-Krypt.0040122B
00401217 |6A 00 push 0
00401219 |68 56304000 push De-Krypt.00403056 ; ASCII "[iCU]"
0040121E |68 2F304000 push De-Krypt.0040302F ; ASCII "Decryption Successfull ... Good Work !"
00401223 |FF75 08 push dword ptr ss:[ebp+8]
00401226 |E8 6D000000 call <jmp.&user32.MessageBoxA>
0040122B \6A 00 push 0
0040122D 68 56304000 push De-Krypt.00403056 ; ASCII "[iCU]"
00401232 68 2F304000 push De-Krypt.0040302F ; ASCII "Decryption Successfull ... Good Work !"
00401237 FF75 08 push dword ptr ss:[ebp+8]
0040123A E8 59000000 call <jmp.&user32.MessageBoxA>
0040123F 90 nop
00401240 90 nop
00401241 90 nop
00401242 90 nop
00401243 90 nop
===============================================================================
前面两个就不分析了,没什么意思,看看正确的这个吧.
非常遗憾,很可以,我前面猜错了,没想到作者把代码从新生成了一次,哎!
太奇妙了.不要以为这是自符串复制哦,我试了的,把上面那一段nop掉也一样可以的.
上面那段代码除了有一个提示作用,没有任何作用,nop掉依然可以.
所以,这个crackme分析完了,得到几个密码,如下:
13,64,156,1195(这几个数据生成的代码是完全一样的)
分析完毕,有些创意,非常不错.
注意:如果我们知道了生成的代码的话,那么就可以用程序来实现上面的手工测试,
我将会在下一篇文章中仔细分析.
--------------------------------------------------------------------------------
【经验总结】
这总思路用在软件加壳里面或者加密里面,应该非常不错.
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2007年04月12日 23:20:57
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: