标 题: 对一个具有VM的CrackMe的详细分析
作 者: 曹无咎
时 间: 2011.3.17 18:25
详细信息:
这个CM是我刚学crack的时候就下载了,当时看的时候,看到代码JMP来JMP去的,很是纠结,于是就保
存在了硬盘上,差不多有一年了,今天拿出来玩玩(宿舍路由器被万恶的网通公司给封了,不能上网了
),看看水平有没有提高。
好吧,现在开始分析了:
首先直接OD载入这个CM,没有搜索到什么有用的字符串,直接输入用户名(pediy)和注册码(123456
),点击确定,出现错误的提示,回溯法找到了关键点,如下所示:
004017AF . 68 BF204000 push 004020BF
004017B4 . 68 A9204000 push 004020A9
004017B9 . 6A 00 push 0
004017BB . 8D45 EF lea eax, dword ptr [ebp-11]
004017BE . 50 push eax
004017BF . E8 9CF8FFFF call 00401060 ; 用户名
004017C4 . B8 BF204000 mov eax, 004020BF
004017C9 . 83C0 04 add eax, 4
004017CC . C700 00000000 mov dword ptr [eax], 0
004017D2 . C740 04 01000>mov dword ptr [eax+4], 1
004017D9 . 68 BF204000 push 004020BF
004017DE . 68 CF204000 push 004020CF
004017E3 . 6A 00 push 0
004017E5 . 8D45 DE lea eax, dword ptr [ebp-22]
004017E8 . 50 push eax
004017E9 . E8 72F8FFFF call 00401060 ; 注册码
004017EE . 68 BF204000 push 004020BF
004017F3 . 68 F7204000 push 004020F7
004017F8 . 68 F4144000 push 004014F4 ; ASCII "Wrong!"
004017FD . 6A 00 push 0
004017FF . E8 5CF8FFFF call 00401060
00401804 . A1 CB204000 mov eax, dword ptr [4020CB]
00401809 . 50 push eax ; /Style =>
MB_OK|MB_ICONHAND|MB_APPLMODAL
0040180A . 68 F4144000 push 004014F4 ; |Title = "Wrong!"
0040180F . 68 F4144000 push 004014F4 ; |Text = "Wrong!"
00401814 . FF35 54204000 push dword ptr [402054] ; |hOwner = 000101C2
(class='#32770')
0040181A . E8 4D000000 call <jmp.&user32.MessageBoxA> ; \MessageBoxA
很明显了,00401060这里就是call进虚拟机了,004017BF此处出现了用户名字,即在虚拟机里面处理用
户名字了,同理,004017E9和004017FF分别是对注册码处理和对用户名和注册码处理结果的比较(以及
处理字符串的问题)。
现在我们进虚拟机里面看看,首先是开辟一个大小为130h的栈(我对虚拟机的理解,就是模仿CUP各个
寄存器的工作方式,不知对不对,还请大家指点):
00401060 $ 55 push ebp
00401061 . 8BEC mov ebp, esp
00401063 . 81C4 D0FEFFFF add esp, -130
到了00401060后,注意看数据窗口,转换成地址模式,可以看到从00402000——0040204C共有20个地址
,这些就是虚拟机模拟的一些指令了(这些指令把一些指令搞复杂了)
00402000 004010D7 crackme.004010D7
00402004 004010D9 crackme.004010D9
00402008 004010FF crackme.004010FF
0040200C 00401133 crackme.00401133
00402010 00401169 crackme.00401169
00402014 00401196 crackme.00401196
00402018 004013FC crackme.004013FC
0040201C 004012B8 crackme.004012B8
00402020 004011C6 crackme.004011C6
00402024 00401203 crackme.00401203
00402028 00401240 crackme.00401240
0040202C 00401268 crackme.00401268
00402030 00401290 crackme.00401290
00402034 004012CF crackme.004012CF
00402038 004012E6 crackme.004012E6
0040203C 00401309 crackme.00401309
00402040 0040132E crackme.0040132E
00402044 0040135D crackme.0040135D
00402048 00401392 crackme.00401392
0040204C 004013C7 crackme.004013C7
向下看就会发现,这些地址跑跑之后都会有个JMP
继续看:
00401069 . C745 E4 00000>mov dword ptr [ebp-1C], 0
00401070 . C745 E8 00000>mov dword ptr [ebp-18], 0
00401077 . C745 F1 00000>mov dword ptr [ebp-F], 0
0040107E . C645 FD 00 mov byte ptr [ebp-3], 0
00401082 . C645 FE 00 mov byte ptr [ebp-2], 0
00401086 . C745 F5 00000>mov dword ptr [ebp-B], 0
0040108D . 8D85 D0FEFFFF lea eax, dword ptr [ebp-130]
00401093 . 8945 F1 mov dword ptr [ebp-F], eax
00401096 . 8B45 14 mov eax, dword ptr [ebp+14]
00401099 . 8945 E0 mov dword ptr [ebp-20], eax
0040109C . 8B45 08 mov eax, dword ptr [ebp+8] ; 用户名
0040109F . 8945 D0 mov dword ptr [ebp-30], eax
004010A2 . 8B45 0C mov eax, dword ptr [ebp+C]
004010A5 . 8945 D8 mov dword ptr [ebp-28], eax
004010A8 . C745 DC 00000>mov dword ptr [ebp-24], 0
004010AF . C745 D4 00000>mov dword ptr [ebp-2C], 0
004010B6 . 8B45 10 mov eax, dword ptr [ebp+10]
004010B9 . 8945 EC mov dword ptr [ebp-14], eax
这些是对刚刚开辟的栈进行初始化的,比如说esp,eip,ebp等
仔细分析的话,就会发现,JMP经常会JMP到这里(004010BC),我认为这里应该是在分析到底应该执行
哪条指令,现在就有些头绪了,JMP来JMP去就是在处理数据了
004010BC > /FF45 EC inc dword ptr [ebp-14] ; crackme.004020A9
004010BF . |8B45 EC mov eax, dword ptr [ebp-14]
004010C2 . |8A00 mov al, byte ptr [eax]
004010C4 . |8845 F0 mov byte ptr [ebp-10], al
004010C7 . |B8 00204000 mov eax, 00402000
004010CC . |0FB65D F0 movzx ebx, byte ptr [ebp-10]
004010D0 . |C1E3 02 shl ebx, 2
004010D3 . |03C3 add eax, ebx
004010D5 . |FF20 jmp dword ptr [eax]
虚拟机的难点应该就是分析这些虚拟过的指令了,现在分析一下这些指令的实际功能是什么。
004010D7 :这个没有进行什么实际的运算
004010D9 :将一个byte转换成dword,并压入eax,可以看作是压栈操作(push)
004010FF :pop指令
004010FF . FF45 EC inc dword ptr [ebp-14]
00401102 . 8B45 EC mov eax, dword ptr [ebp-14]
00401105 . 0FB600 movzx eax, byte ptr [eax]
00401108 . 8845 F0 mov byte ptr [ebp-10], al
0040110B . 8B45 E0 mov eax, dword ptr [ebp-20]
0040110E . 0FB65D F0 movzx ebx, byte ptr [ebp-10]
00401112 . C1E3 02 shl ebx, 2
00401115 . 03C3 add eax, ebx
00401117 . 8945 E4 mov dword ptr [ebp-1C], eax
0040111A . 8B18 mov ebx, dword ptr [eax]
0040111C . 895D E8 mov dword ptr [ebp-18], ebx
0040111F . 8345 F1 04 add dword ptr [ebp-F], 4
00401123 . 8B45 F1 mov eax, dword ptr [ebp-F]
00401126 . 8945 E4 mov dword ptr [ebp-1C], eax
00401129 . 8B5D E8 mov ebx, dword ptr [ebp-18]
0040112C . 8918 mov dword ptr [eax], ebx
0040112E . 895D F5 mov dword ptr [ebp-B], ebx
00401131 .^ EB 89 jmp short 004010BC
00401133 :pop指令,从栈中弹出值
00401133 . FF45 EC inc dword ptr [ebp-14]
00401136 . 8B45 EC mov eax, dword ptr [ebp-14]
00401139 . 8A00 mov al, byte ptr [eax]
0040113B . 8845 F0 mov byte ptr [ebp-10], al
0040113E . 8B45 E0 mov eax, dword ptr [ebp-20]
00401141 . 0FB65D F0 movzx ebx, byte ptr [ebp-10]
00401145 . C1E3 02 shl ebx, 2
00401148 . 03C3 add eax, ebx
0040114A . 8B5D F5 mov ebx, dword ptr [ebp-B]
0040114D . 8945 E4 mov dword ptr [ebp-1C], eax
00401150 . 8918 mov dword ptr [eax], ebx
00401152 . 836D F1 04 sub dword ptr [ebp-F], 4
00401156 . 8B45 F1 mov eax, dword ptr [ebp-F]
00401159 . 8945 E4 mov dword ptr [ebp-1C], eax
0040115C . 8B00 mov eax, dword ptr [eax]
0040115E . 8945 F5 mov dword ptr [ebp-B], eax
00401161 . 8945 E8 mov dword ptr [ebp-18], eax
00401164 .^ E9 53FFFFFF jmp 004010BC
00401169 :加法运算
00401169 . 8B45 F5 mov eax, dword ptr [ebp-B]
0040116C . 836D F1 04 sub dword ptr [ebp-F], 4
00401170 . 8B5D F1 mov ebx, dword ptr [ebp-F]
00401173 . 895D E4 mov dword ptr [ebp-1C], ebx
00401176 . 8B13 mov edx, dword ptr [ebx]
00401178 . 8955 E8 mov dword ptr [ebp-18], edx
0040117B . 0145 E8 add dword ptr [ebp-18], eax add指令,加运算(
不知道这样认为对不对,如何判断指令的实际意义,还望大侠指点,谢谢)
0040117E . 0F9445 FE sete byte ptr [ebp-2]
00401182 . 0F9845 FD sets byte ptr [ebp-3]
00401186 . FF75 E8 push dword ptr [ebp-18]
00401189 . 8F45 F5 pop dword ptr [ebp-B]
0040118C . 8B45 E8 mov eax, dword ptr [ebp-18]
0040118F . 8903 mov dword ptr [ebx], eax
00401191 .^ E9 26FFFFFF jmp 004010BC
00401196 :减法运算
00401196 . 8B45 F5 mov eax, dword ptr [ebp-B]
00401199 . 836D F1 04 sub dword ptr [ebp-F], 4
0040119D . 8B5D F1 mov ebx, dword ptr [ebp-F]
004011A0 . 895D E4 mov dword ptr [ebp-1C], ebx
004011A3 . 8B13 mov edx, dword ptr [ebx]
004011A5 . 8955 E8 mov dword ptr [ebp-18], edx
004011A8 . 8745 E8 xchg dword ptr [ebp-18], eax
004011AB . 2945 E8 sub dword ptr [ebp-18], eax 这里有个sub指令
,应该是减运算
004011AE . 0F9445 FE sete byte ptr [ebp-2]
004011B2 . 0F9845 FD sets byte ptr [ebp-3]
004011B6 . FF75 E8 push dword ptr [ebp-18]
004011B9 . 8F45 F5 pop dword ptr [ebp-B]
004011BC . 8B45 E8 mov eax, dword ptr [ebp-18]
004011BF . 8903 mov dword ptr [ebx], eax
004011C1 .^ E9 F6FEFFFF jmp 004010BC
004011C6 :这段代码中我看见有个MUL操作,猜测是为乘法运算(关于虚拟指令我也是猜测其中的功
能,如有错误还望大侠指教,谢谢了)
00401203 :除运算
00401203 . 8B45 F5 mov eax, dword ptr [ebp-B]
00401206 . 836D F1 04 sub dword ptr [ebp-F], 4
0040120A . 8B5D F1 mov ebx, dword ptr [ebp-F]
0040120D . 895D E4 mov dword ptr [ebp-1C], ebx
00401210 . 8B1B mov ebx, dword ptr [ebx]
00401212 . 895D E8 mov dword ptr [ebp-18], ebx
00401215 . 33D2 xor edx, edx
00401217 . 66:C705 20124>mov word ptr [401220], 0F3F7
00401220 . 33C0 xor eax, eax 异或运算,分析下后发现是除法
00401222 . 8945 E8 mov dword ptr [ebp-18], eax
00401225 . 0F9445 FE sete byte ptr [ebp-2]
00401229 . 0F9845 FD sets byte ptr [ebp-3]
0040122D . FF75 E8 push dword ptr [ebp-18]
00401230 . 8F45 F5 pop dword ptr [ebp-B]
00401233 . 8B45 E8 mov eax, dword ptr [ebp-18]
00401236 . 8B5D F1 mov ebx, dword ptr [ebp-F]
00401239 . 8903 mov dword ptr [ebx], eax
0040123B .^ E9 7CFEFFFF jmp 004010BC
00401240 :与运算
00401240 . 8B45 F5 mov eax, dword ptr [ebp-B]
00401243 . 836D F1 04 sub dword ptr [ebp-F], 4
00401247 . 8B5D F1 mov ebx, dword ptr [ebp-F]
0040124A . 895D E4 mov dword ptr [ebp-1C], ebx
0040124D . 8B13 mov edx, dword ptr [ebx]
0040124F . 8955 E8 mov dword ptr [ebp-18], edx
00401252 . 8745 E8 xchg dword ptr [ebp-18], eax
00401255 . 2145 E8 and dword ptr [ebp-18], eax 与运算
00401258 . FF75 E8 push dword ptr [ebp-18]
0040125B . 8F45 F5 pop dword ptr [ebp-B]
0040125E . 8B45 E8 mov eax, dword ptr [ebp-18]
00401261 . 8903 mov dword ptr [ebx], eax
00401263 .^ E9 54FEFFFF jmp 004010BC
00401268 :或运算
00401290 :异或运算
004012CF :保存用户名的字节
004012CF . 836D F1 04 sub dword ptr [ebp-F], 4
004012D3 . 8B45 F1 mov eax, dword ptr [ebp-F]
004012D6 . 8945 E4 mov dword ptr [ebp-1C], eax
004012D9 . 8B18 mov ebx, dword ptr [eax]
004012DB . 895D E8 mov dword ptr [ebp-18], ebx
004012DE . 895D F5 mov dword ptr [ebp-B], ebx
004012E1 .^ E9 D6FDFFFF jmp 004010BC
004012E6 : 这条指令是取用户名的第一个字节(当然是循环取啦,p=70H)
004012E6 . 8B45 D0 mov eax, dword ptr [ebp-30]
004012E9 . 0345 D4 add eax, dword ptr [ebp-2C]
004012EC . FF45 D4 inc dword ptr [ebp-2C]
004012EF . 0FB600 movzx eax, byte ptr [eax]
004012F2 . 8945 F5 mov dword ptr [ebp-B], eax
004012F5 . 8345 F1 04 add dword ptr [ebp-F], 4
004012F9 . 8B5D F1 mov ebx, dword ptr [ebp-F]
004012FC . 895D E4 mov dword ptr [ebp-1C], ebx
004012FF . 8945 E8 mov dword ptr [ebp-18], eax
00401302 . 8903 mov dword ptr [ebx], eax
00401304 .^ E9 B3FDFFFF jmp 004010BC
004012B8 :将用户名第一个字节压入栈中
004012B8 . 8345 F1 04 add dword ptr [ebp-F], 4
004012BC . 8B5D F5 mov ebx, dword ptr [ebp-B]
004012BF . 895D E8 mov dword ptr [ebp-18], ebx
004012C2 . 8B45 F1 mov eax, dword ptr [ebp-F]
004012C5 . 8945 E4 mov dword ptr [ebp-1C], eax
004012C8 . 8918 mov dword ptr [eax], ebx
00401309 :
0040132E :
0040135D :
00401392 :
004013C7 :条件跳转指令,这个是算法的循环处,几jl之类的指令,现在返回到循环开始处,进行下
一轮的循环运算
004013FC :retn,退出虚拟机
这些基本的操作都明了了(这些我也有些不知道正确与否,但是算出来的注册码貌似是对的,运气运气
啊)
重新载入CM,输入username:pediy serial:123456
有了这些指令,分析就简单多了(分析过程太繁琐了,这里就不浪费空间贴出来了):
首先取了2保存,然后逐字节去用户名相乘,
既 2*70*65*64*69*79= B1437580+7F=B14375FF(16进制)=2973988351
处理注册码:1(31h) 2(32h) 3(33h) 4(34h) 5(35h) 6(36)
首先将数字转换为十进制数,然后做指数运算 1*10^0+2*10^1+3*10^2+4*10^3+5*10^4+6*10^5=654321
1 20 300 4000 50000 600000
9FBF1(十六进制)-EB-5(减5这个操作是在这个call004017F3实现的)
然后比较 B14375FF和(9FBF1(十六进制)-EB-5)是否相等,如果相等的话,注册成功!
这样可以给出pediy的正确注册码:1958893792
终于分析完了,小菜鸟第一次分析,如有失误之处,还望同学们指出,多谢指教了
知道了运算过程,注册机也就好写了,但我编程一窍不通,还要好好学习!
感谢各种大牛,纠结不能上网,现在也不能发出去。。。。
注册机代码如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void main()
{
char Name[32] = "0";
char Key[32] = "0";
printf("please input your name:\n");
scanf("%s",Name);
int count = 2;
for(int i = 0; i<strlen(Name); i++)
{
count *= *(Name + i);
}
count += 0x7F;
count += (5 + 0x0EB);
itoa(count, Key, 10);
i = strlen(Key)-1;
printf(" the key:\n");
for(int j = i ;j>=0 ;j--)
{
printf("%c",*(Key+j));
}
printf("\n");
}
注册机只能用户名是数字的时候可以用(编程菜鸟,不懂)



[培训]科锐逆向工程师培训第53期2025年7月8日开班!