且行且远 » Blog Archive » 对一个crackme的简单分析
且行且远
分类: 逆向手札 由 ssfighter 于 2006年1月3日 发表

先说一下,这个crackme已经很老了,也有无数人分析过这个简单的crackme了,我这个大菜鸟昨天晚上尝试着自己跟了一下,有点小收获,写出来,希望高手看不到我这个帖子,否则肯定要笑掉大牙了:)

crackme文件的下载地址是:http://bbs.pediy.com/upload/files/1084801702.zip
riijj写的的一个很清楚的破文在这里:http://bbs.pediy.com/showthread.php?s=&threadid=7505&perpage=15&pagenumber=1

首先侦壳,用PeiD0.94打开,发现显示为Microsoft Visual C++ 6.0,基本判断为无壳
再用Ollydbg打开,运行之后,选择搜索-所有参考的文本字符串,找到“Registration Fail”的地址,来到这里:

00401058 /75 74 jnz short ncrackme.004010CE
0040105A |8B4424 0C mov eax,dword ptr ss:[esp+C]
0040105E |66:3D EA03 cmp ax,3EA
00401062 |75 42 jnz short ncrackme.004010A6
00401064 |E8 C7010000 call ncrackme.00401230 ;

这个应该是计算的位置,在这里设断,输入用户名ssfighter,密码abcd1234。跟进去看看

00401069 |85C0 test eax,eax ; 比较
0040106B |6A 00 push 0
0040106D |68 80504000 push ncrackme.00405080 ; ASCII “ncrackme”
00401072 |75 1B jnz short ncrackme.0040108F ; 关键跳
00401074 |A1 B8564000 mov eax,dword ptr ds:[4056B8]
00401079 |68 64504000 push ncrackme.00405064 ; ASCII “Registration successful.”
0040107E |50 push eax
0040107F |FF15 C0404000 call dword ptr ds:[<&USER32.Message>; USER32.MessageBoxA
00401085 |E8 A6020000 call ncrackme.00401330
0040108A |33C0 xor eax,eax
0040108C |C2 1000 retn 10
0040108F |8B0D B8564000 mov ecx,dword ptr ds:[4056B8]
00401095 |68 50504000 push ncrackme.00405050 ; ASCII “Registration fail.”
0040109A |51 push ecx
0040109B |FF15 C0404000 call dword ptr ds:[<&USER32.Message>; USER32.MessageBoxA
004010A1 |33C0 xor eax,eax
004010A3 |C2 1000 retn 10

跟进call之后来到这里:
00401230 8B0D BC564000 mov ecx,dword ptr ds:[4056BC]
00401236 83EC 30 sub esp,30
00401239 8D4424 00 lea eax,dword ptr ss:[esp]
0040123D 53 push ebx
0040123E 56 push esi
0040123F 8B35 94404000 mov esi,dword ptr ds:[<&USER32.GetD>; USER32.GetDlgItemTextA
00401245 6A 10 push 10
00401247 50 push eax
00401248 68 E8030000 push 3E8
0040124D 51 push ecx
0040124E 33DB xor ebx,ebx
00401250 FFD6 call esi
00401252 83F8 03 cmp eax,3 ; 用户名>3位
00401255 73 0B jnb short ncrackme.00401262
00401257 5E pop esi
00401258 B8 01000000 mov eax,1
0040125D 5B pop ebx
0040125E 83C4 30 add esp,30
00401261 C3 retn
00401262 A1 BC564000 mov eax,dword ptr ds:[4056BC]
00401267 8D5424 28 lea edx,dword ptr ss:[esp+28]
0040126B 6A 10 push 10
0040126D 52 push edx
0040126E 68 E9030000 push 3E9
00401273 50 push eax
00401274 FFD6 call esi
00401276 0FBE4424 08 movsx eax,byte ptr ss:[esp+8] ; 取用户名第一位
0040127B 0FBE4C24 09 movsx ecx,byte ptr ss:[esp+9] ; 取用户名第二位
00401280 99 cdq ; edx与eax组合
00401281 F7F9 idiv ecx ; eax除以ecx,商为eax
00401283 8BCA mov ecx,edx ; ecx为余数
00401285 83C8 FF or eax,FFFFFFFF ; eax=FFFFFFFF
00401288 0FBE5424 0A movsx edx,byte ptr ss:[esp+A] ; 取用户名第三位到edx
0040128D 0FAFCA imul ecx,edx ; 将刚才的余数与第三位相乘
00401290 41 inc ecx ; ecx加一
00401291 33D2 xor edx,edx ; edx置零
00401293 F7F1 div ecx ; eax/ecx商放到eax
00401295 50 push eax ; eax压栈
00401296 E8 A5000000 call ncrackme.00401340 ; 取刚才的结果放到内存中

又有一个call,跟进之后发现是如下程序段:
00401340 8B4424 04 mov eax,dword ptr ss:[esp+4]
00401344 A3 AC504000 mov dword ptr ds:[4050AC],eax
00401349 C3 retn

看来是把刚才计算的结果写到了4050AC内存中,记住这个地方,后面的计算都是在反复读取这里。

继续运行:
0040129B 83C4 04 add esp,4 ; 堆栈平衡
0040129E 33F6 xor esi,esi ; esi置零
004012A0 E8 A5000000 call ncrackme.0040134A
这个call里面有一大堆复杂的计算,先放着。
004012A5 99 cdq ; edx置零
004012A6 B9 1A000000 mov ecx,1A
004012AB F7F9 idiv ecx ; 刚才计算结果除以26
004012AD 80C2 41 add dl,41 ; 余数再加上65
004012B0 885434 18 mov byte ptr ss:[esp+esi+18],dl ; 算得的结果中一位写入堆栈
004012B4 46 inc esi
004012B5 83FE 0F cmp esi,0F ; 一共15位
004012B8 ^ 72 E6 jb short ncrackme.004012A0
004012BA 57 push edi ; 至此计算完毕

这时发现堆栈中写入了15个奇怪的大写字母:
0012FAE8 4E55564A JVUN
0012FAEC 42425345 ESBB
0012FAF0 43564F45 EOVC
0012FAF4 00525454 TTR.

可是这并不是注册码,继续看下面的程序:
004012BA 57 push edi ; 至此注册码计算完毕
004012BB 8D7C24 0C lea edi,dword ptr ss:[esp+C]
004012BF 83C9 FF or ecx,FFFFFFFF
004012C2 33C0 xor eax,eax
004012C4 33F6 xor esi,esi
004012C6 F2:AE repne scas byte ptr es:[edi]
004012C8 F7D1 not ecx
004012CA 49 dec ecx ; 得到用户名位数
004012CB 74 59 je short ncrackme.00401326
004012CD 8A4434 0C mov al,byte ptr ss:[esp+esi+C] ; 取用户名的一位
004012D1 C0F8 05 sar al,5 ; 右移5位
004012D4 0FBEC0 movsx eax,al
004012D7 8D1480 lea edx,dword ptr ds:[eax+eax*4]
004012DA 8D04D0 lea eax,dword ptr ds:[eax+edx*8]
004012DD 8D0440 lea eax,dword ptr ds:[eax+eax*2] ; eax=123乘以右移之后的数
004012E0 85C0 test eax,eax
004012E2 7E 0A jle short ncrackme.004012EE
004012E4 8BF8 mov edi,eax
004012E6 E8 5F000000 call ncrackme.0040134A ; 又看到了这个call
004012EB 4F dec edi
004012EC ^ 75 F8 jnz short ncrackme.004012E6 ; 这里是一个循环
004012EE E8 57000000 call ncrackme.0040134A ; 又是这个call,这应该是最后一次计算

004012F3 99 cdq ; edx置零
004012F4 B9 1A000000 mov ecx,1A
004012F9 8D7C24 0C lea edi,dword ptr ss:[esp+C]
004012FD F7F9 idiv ecx
004012FF 0FBE4C34 2C movsx ecx,byte ptr ss:[esp+esi+2C] ; 取填入的注册码
00401304 80C2 41 add dl,41 ; 余数加65
00401307 0FBEC2 movsx eax,dl
0040130A 2BC1 sub eax,ecx
0040130C 885434 1C mov byte ptr ss:[esp+esi+1C],dl
00401310 99 cdq
00401311 33C2 xor eax,edx
00401313 83C9 FF or ecx,FFFFFFFF
00401316 2BC2 sub eax,edx
00401318 03D8 add ebx,eax ; 比较注册码一位,相同则ebx为0
0040131A 33C0 xor eax,eax ; eax=0
0040131C 46 inc esi
0040131D F2:AE repne scas byte ptr es:[edi]
0040131F F7D1 not ecx
00401321 49 dec ecx
00401322 3BF1 cmp esi,ecx
00401324 ^ 72 A7 jb short ncrackme.004012CD
00401326 5F pop edi
00401327 8BC3 mov eax,ebx
00401329 5E pop esi
0040132A 5B pop ebx
0040132B 83C4 30 add esp,30
0040132E C3 retn

后面的程序基本就在比较了,看到了0040132E的retn,知道这里就返回了,由于刚才我们看到主程序中是test eax, eax就是要eax=0的时候注册成功,我们又看到00401327 mov eax, ebx。呵呵,原来eax就是ebx啊,前面对ebx的操作只有ebx置零和ebx累加,由此我们知道必须在00401311行的地方eax=edx才能保证ebx=ebx+0,再往前看,看到这里:

004012FF 0FBE4C34 2C movsx ecx,byte ptr ss:[esp+esi+2C] ; 取填入的注册码
00401304 80C2 41 add dl,41 ; 余数加65
00401307 0FBEC2 movsx eax,dl
0040130A 2BC1 sub eax,ecx

ecx的值就是我们填入的注册码,而只要让40130A时eax=ecx,后面就能保证这位计算正确,因此在这里设断,记下每次的eax,串成字符串就是所要的注册码,至于注册码的位数,看这里

0040131D F2:AE repne scas byte ptr es:[edi]
0040131F F7D1 not ecx
00401321 49 dec ecx
00401322 3BF1 cmp esi,ecx
00401324 ^ 72 A7 jb short ncrackme.004012CD

前面是取用户名的位数,后面比较,因此可以知道注册码的位数应该和用户名的位数一样。

在40130A设断点后得到的一组eax值为:
47 55 50 51 49 44 50 51 49
分别对应ASCII码为:
G U P Q I D P Q I

注册通过,后面主要参考了riijj大侠的贴子,受他贴子启发才明白了好多地方。程序基本都看懂了,改天写个注册机出来。




评论已关闭.