本帖最后由 44018723 于 2014-6-10 19:31 编辑
[反汇编练习] 160个CrackMe之001。 本系列文章的目的是从一个没有任何经验的新手的角度(其实就是我自己),一步步尝试将160个CrackMe全部破解,如果可以,通过任何方式写出一个类似于注册机的东西。 其中,文章中按照如下逻辑编排(解决如下问题): 1、使用什么环境和工具 2、程序分析 3、思路分析和破解流程 4、注册机的探索
1、工具和环境: WinXP SP3 + 52Pojie六周年纪念版OD + PEID + 汇编金手指。 160个CrackMe的打包文件。 下载地址: http://pan.baidu.com/s/1xUWOY 密码: jbnq 注: 1、Win7系统对于模块和程序开启了随机初始地址的功能,会给分析带来很大的负担,所以不建议使用Win7进行分析。 2、以上工具都是在52PoJie论坛下的原版程序,NOD32不报毒,个人承诺绝对不会进行任何和木马病毒相关内容。
2、程序分析: 想要破解一个程序,必须先了解这个程序。所以,在破解过程中,对最初程序的分析很重要,他可以帮助我们理解作者的目的和意图,特别是对于注册码的处理细节,从而方便我们反向跟踪和推导。 打开CHM文件,将第一个文件 Acid burn.exe 保存下来,新建一个01的文件夹,将exe放到这里,同时将以后的分析文件也存放这里。打开Acid burn.exe,随意输入和点击,熟悉程序流程。 我们发现,这个软件分为了两个部分,一个是Serial/Name,需要输入用户名和注册码才能通过,另外一个Serial只需要输入一个注册码一类的东西。我们随意选一个开始,比如,先进行第一个。 我们随意输入一个用户名和序列号(伪码): 伪码:
Name:112233
Serial:44556677 点击Check it Baby! 它会弹出一个对话框提示: Sorry, The Serial is incorrect ! 再换几个随意试试,发现就这一种情况。 OK,出现了对话框这就很好办,说明作者在校验注册码之后发现如果错误了就直接弹窗,我们只要找到弹出对话框的地方,向上跟踪,就可以找出判断是否正确的地方了,jmp或者Nop就算爆破了。
3、具体步骤如下: 我们随意输入一个用户名和序列号(伪码): Name:112233
Serial:44556677 点击Check it Baby! 它会弹出一个对话框提示: Sorry, The Serial is incorrect ! 此时不要点击确定按钮,返回OD暂停(F12),点击堆栈-K小图标(Ctrl+K) ,如下图: 这里有两个MessageBox的地址,第一个地址为77D5082F这个地址明显太大,不在模块的领空,不是的。第二个地址为0042A1AE,和00400100地址非常接近,十有八九就是它了。 右键 show call, 在Call上面设置断点。 查看附近代码: [Asm] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 0042A170 /$ 55 push ebp
0042A171 |. 8BEC mov ebp , esp
0042A173 |. 83C4 F4 add esp ,-0xC
0042A176 |. 53 push ebx
0042A177 |. 56 push esi
0042A178 |. 57 push edi
0042A179 |. 8BF9 mov edi , ecx
0042A17B |. 8BF2 mov esi , edx
0042A17D |. 8BD8 mov ebx , eax
0042A17F |. E8 7CB4FDFF call < jmp .&user32.GetActiveWindow>
0042A184 |. 8945 F8 mov [ local .2], eax
0042A187 |. 33C0 xor eax , eax
0042A189 |. E8 12A0FFFF call 004241A0
0042A18E |. 8945 F4 mov [ local .3], eax
0042A191 |. 33C0 xor eax , eax
0042A193 |. 55 push ebp
0042A194 |. 68 D0A14200 push 0042A1D0
0042A199 |. 64:FF30 push dword ptr fs :[ eax ]
0042A19C |. 64:8920 mov dword ptr fs :[ eax ], esp
0042A19F |. 8B45 08 mov eax ,[arg.1]
0042A1A2 |. 50 push eax
0042A1A3 |. 57 push edi
0042A1A4 |. 56 push esi
0042A1A5 |. 8B43 24 mov eax , dword ptr ds :[ ebx +0x24]
0042A1A8 |. 50 push eax
0042A1A9 |. E8 FAB5FDFF call < jmp .&user32.MessageBoxA>
|
发现,没有跳转语句,逻辑很简单,在之上几行处就有retn,在头部push ebp下断,重新点击Check it baby 按钮,在右下角堆栈处找到最近一条Return语句: 0012F974 0042FB37 RETURN to Acid_bur.0042FB37 from Acid_bur.0042A170 右键 Follow in Disassm..(反汇编跟随),这里直接连接了一个跳转,代码如下: [Asm] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 0042FAD5 |. 68 C8FB4200 push 0042FBC8
0042FADA |. FF75 F8 push [ local .2]
0042FADD |. 8D45 F4 lea eax ,[ local .3]
0042FAE0 |. BA 05000000 mov edx ,0x5
0042FAE5 |. E8 C23EFDFF call 004039AC
0042FAEA |. 8D55 F0 lea edx ,[ local .4]
0042FAED |. 8B83 E0010000 mov eax , dword ptr ds :[ ebx +0x1E0]
0042FAF3 |. E8 60AFFEFF call 0041AA58
0042FAF8 |. 8B55 F0 mov edx ,[ local .4]
0042FAFB |. 8B45 F4 mov eax ,[ local .3]
0042FAFE |. E8 F93EFDFF call 004039FC
0042FB03 75 1A jnz short 0042FB1F
0042FB05 |. 6A 00 push 0x0
0042FB07 |. B9 CCFB4200 mov ecx ,0042FBCC
0042FB0C |. BA D8FB4200 mov edx ,0042FBD8
0042FB11 |. A1 480A4300 mov eax , dword ptr ds :[0x430A48]
0042FB16 |. 8B00 mov eax , dword ptr ds :[ eax ]
0042FB18 |. E8 53A6FFFF call 0042A170
0042FB1D |. EB 18 jmp short 0042FB37
0042FB1F |> 6A 00 push 0x0
0042FB21 |. B9 74FB4200 mov ecx ,0042FB74
0042FB26 |. BA 80FB4200 mov edx ,0042FB80
0042FB2B |. A1 480A4300 mov eax , dword ptr ds :[0x430A48]
0042FB30 |. 8B00 mov eax , dword ptr ds :[ eax ]
0042FB32 |. E8 39A6FFFF call 0042A170
0042FB37 |> 33C0 xor eax , eax
|
看到和提示框一样的文本,是不是感到很亲切?OK,我们大概浏览下代码,最近部分有两个可疑跳转JNZ 和JMP, JNZ会通过它上面的call 004039FC 判断我们的伪码是否正确,判断的结果存在EAX中,如果EAX不等于就跳转到错误提示信息框那里。我们的目的是无论伪码是否正确都通过验证,所以最简单的办法就是将jnz这句使用NOP填充,我们尝试一下:选择JNZ这句,右键Binary->Fill with NOPS.回到原始程序,再次点击Check it baby! 哈哈,提示Good Job!通过了! 再次比对MessageBox和堆栈(Ctrl+K)窗口最后一个调用,是不是发现什么特殊的地方?对啦!那个CALL就是调用MessageBox的地方,所以,下次我们就不用在MessageBox处下断跟踪了,直接最后一个地址,show call。
4、注册机部分 以上部分是爆破分析,我们看看能否分析出注册机算法。
在原JNZ上面一行CALL下断:点击Ckeck it Baby!按钮,程序断下: [Asm] 纯文本查看 复制代码 1 2 3 | 0042FAF8 |. 8B55 F0 mov edx ,[ local .4]
0042FAFB |. 8B45 F4 mov eax ,[ local .3]
0042FAFE |. E8 F93EFDFF call 004039FC
|
我们发现,EDX存储的是我们的假序列号,EAX看上去像是一个正确的序列号,也有可能是对应的用户名也说不定,我们可疑尝试一下。
现将我们之前修改的代码恢复:选中那两行NOP,右键Undo... 。取消断点。先尝试是否是Name,Name输入EAX的值,下一行继续44556677,结果继续弹错。然后尝试作为序列号,Name为112233,Serial为CW-4018-CRACKED,再尝试,OK!完全正确! 小结一下:在这个CALL之前程序已经将用户名对应的序列号算出来了,然后和我们输入的Serial通过这个CALL对比,最终给出提示信息。我们要的算法不再这个CALL中。 下一步的思路就是继续在这个CALL之上的CALL下断,分析出产生出这个序列号的CALL。由于CALL的返回值一般都存在EAX中,所以我们可以查看附近的CALL之后EAX的值,从而判断出正确的注册码生成函数。 附近的两个CALL: [Asm] 纯文本查看 复制代码 1 2 3 4 5 6 7 8 9 | 0042FADD |. 8D45 F4 lea eax ,[ local .3]
0042FAE0 |. BA 05000000 mov edx ,0x5
0042FAE5 |. E8 C23EFDFF call 004039AC
0042FAEA |. 8D55 F0 lea edx ,[ local .4]
0042FAED |. 8B83 E0010000 mov eax , dword ptr ds :[ ebx +0x1E0]
0042FAF3 |. E8 60AFFEFF call 0041AA58
0042FAF8 |. 8B55 F0 mov edx ,[ local .4]
0042FAFB |. 8B45 F4 mov eax ,[ local .3]
0042FAFE |. E8 F93EFDFF call 004039FC
|
分别在CALL之后的那一句下断,点击按钮,F8步过,发现在 附近的CALL 1 处,EAX出现正确的注册码,说明他可能是关键的注册码CALL,下面对它的进行分析:
重新在call 0041AA58 下断, [Asm] 纯文本查看 复制代码 1 2 3 | 0042FAEA |. 8D55 F0 lea edx ,[ local .4]
0042FAED |. 8B83 E0010000 mov eax , dword ptr ds :[ ebx +0x1E0]
0042FAF3 |. E8 60AFFEFF call 0041AA58
|
其中edx和eax都没有特殊信息出现,我们就可以更加断定注册码都是在call中生成的。 这时我们就有两种方案了,一种分析CALL的参数,直接远程调用CALL生成正确的注册码,另一种是分析CALL的内容,根据他的算法自己写一个这个生成流程。这两个无明显的好坏之分,只看用在什么地方了。 按照我的理解,第一种,内存调用适合算法复杂或者更新不频繁的程序,这么做可以节省时间和复杂度。第二种,适合算法不是特别复杂,或者软件更新频繁的软件,算法的复杂度就不说了,软件无论更新多么频繁,一般注册码算法是不会变的,这样生成的注册机就可以适用所有的版本,大家都省事。 好了,啰嗦了这么多,开始正式分析:
(由于已经在这里CALL被调用了很多次了,各个寄存器参数很乱,建议从新加载一次程序,方便分析)
进入CALL 0041AA58,代码如下:
(由于这个CALL被很多地方一直调用,所以我们必须每次从上层CALL单步F7跟踪,直到找到正确的CALL) [Asm] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | 0041AA58 /$ 53 push ebx
0041AA59 |. 56 push esi
0041AA5A |. 57 push edi
0041AA5B |. 8BFA mov edi , edx
0041AA5D |. 8BF0 mov esi , eax
0041AA5F |. 8BC6 mov eax , esi
0041AA61 |. E8 A2FFFFFF call 0041AA08
0041AA66 |. 8BD8 mov ebx , eax
0041AA68 |. 8BC7 mov eax , edi
0041AA6A |. 8BCB mov ecx , ebx
0041AA6C |. 33D2 xor edx , edx
0041AA6E |. E8 E18CFEFF call 00403754
0041AA73 |. 85DB test ebx , ebx
0041AA75 |. 74 0C je short 0041AA83
0041AA77 |. 8D4B 01 lea ecx , dword ptr ds :[ ebx +0x1]
0041AA7A |. 8B17 mov edx , dword ptr ds :[ edi ]
0041AA7C |. 8BC6 mov eax , esi
0041AA7E |. E8 95FFFFFF call 0041AA18
0041AA83 |> 5F pop edi
0041AA84 |. 5E pop esi
0041AA85 |. 5B pop ebx
0041AA86 \. C3 retn
继续跟踪到这里:
0041AA08 /$ 6A 00 push 0x0
0041AA0A |. 33C9 xor ecx , ecx
0041AA0C |. BA 0E000000 mov edx ,0xE
0041AA11 |. E8 F6070000 call 0041B20C
0041AA16 \. C3 retn
继续:
0041B20C /$ 55 push ebp
0041B20D |. 8BEC mov ebp , esp
0041B20F |. 83C4 F0 add esp ,-0x10
0041B212 |. 53 push ebx
0041B213 |. 8955 F0 mov [ local .4], edx
0041B216 |. 894D F4 mov [ local .3], ecx
0041B219 |. 8B55 08 mov edx ,[arg.1]
0041B21C |. 8955 F8 mov [ local .2], edx
0041B21F |. 33D2 xor edx , edx
0041B221 |. 8955 FC mov [ local .1], edx
0041B224 |. 85C0 test eax , eax
0041B226 |. 74 0B je short 0041B233
0041B228 |. 8D55 F0 lea edx ,[ local .4]
0041B22B |. 8BD8 mov ebx , eax
0041B22D |. 8B43 2C mov eax , dword ptr ds :[ ebx +0x2C]
0041B230 |. FF53 28 call dword ptr ds :[ ebx +0x28]
0041B233 |> 8B45 FC mov eax ,[ local .1]
0041B236 |. 5B pop ebx
0041B237 |. 8BE5 mov esp , ebp
0041B239 |. 5D pop ebp
0041B23A \. C2 0400 retn 0x4
继续F7步入:
0041CB64 /$ 53 push ebx
0041CB65 |. 56 push esi
0041CB66 |. 57 push edi
0041CB67 |. 83C4 F0 add esp ,-0x10
0041CB6A |. 8BF2 mov esi , edx
0041CB6C |. 8BD8 mov ebx , eax
0041CB6E |. 8B06 mov eax , dword ptr ds :[ esi ]
0041CB70 |. 3D 84000000 cmp eax ,0x84
0041CB75 |. 7F 18 jg short 0041CB8F
0041CB77 |. 74 69 je short 0041CBE2
...
|
跟了一大堆,我们发现没有地方可产生注册码,并且所有的CALL都是不断地在被调用,所以,初步判定注册码不是在这里生成的。但是这么想就与之前的猜测完全推翻了,说明我们的想法有问题,即思路有问题。既然他的码不是及时算出来的,那肯定就是事先算好的,我们再次回到产生注册码的CALL那里,向上查找,F8单步进行查看。 [Asm] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 0042FA4D |. A1 6C174300 mov eax , dword ptr ds :[0x43176C]
0042FA52 |. E8 D96EFDFF call 00406930
0042FA57 |. 83F8 04 cmp eax ,0x4
0042FA5A |. 7D 1D jge short 0042FA79
0042FA5C |. 6A 00 push 0x0
0042FA5E |. B9 74FB4200 mov ecx ,0042FB74
0042FA63 |. BA 80FB4200 mov edx ,0042FB80
0042FA68 |. A1 480A4300 mov eax , dword ptr ds :[0x430A48]
0042FA6D |. 8B00 mov eax , dword ptr ds :[ eax ]
0042FA6F |. E8 FCA6FFFF call 0042A170
0042FA74 |. E9 BE000000 jmp 0042FB37
0042FA79 |> 8D55 F0 lea edx ,[ local .4]
0042FA7C |. 8B83 DC010000 mov eax , dword ptr ds :[ ebx +0x1DC]
0042FA82 |. E8 D1AFFEFF call 0041AA58
0042FA87 |. 8B45 F0 mov eax ,[ local .4]
0042FA8A |. 0FB600 movzx eax , byte ptr ds :[ eax ]
0042FA8D |. F72D 50174300 imul dword ptr ds :[0x431750]
0042FA93 |. A3 50174300 mov dword ptr ds :[0x431750], eax
0042FA98 |. A1 50174300 mov eax , dword ptr ds :[0x431750]
0042FA9D |. 0105 50174300 add dword ptr ds :[0x431750], eax
0042FAA3 |. 8D45 FC lea eax ,[ local .1]
0042FAA6 |. BA ACFB4200 mov edx ,0042FBAC
0042FAAB |. E8 583CFDFF call 00403708
0042FAB0 |. 8D45 F8 lea eax ,[ local .2]
0042FAB3 |. BA B8FB4200 mov edx ,0042FBB8
0042FAB8 |. E8 4B3CFDFF call 00403708
0042FABD |. FF75 FC push [ local .1]
0042FAC0 |. 68 C8FB4200 push 0042FBC8
0042FAC5 |. 8D55 E8 lea edx ,[ local .6]
0042FAC8 |. A1 50174300 mov eax , dword ptr ds :[0x431750]
0042FACD |. E8 466CFDFF call 00406718
0042FAD2 |. FF75 E8 push [ local .6]
0042FAD5 |. 68 C8FB4200 push 0042FBC8
0042FADA |. FF75 F8 push [ local .2]
0042FADD |. 8D45 F4 lea eax ,[ local .3]
0042FAE0 |. BA 05000000 mov edx ,0x5
0042FAE5 |. E8 C23EFDFF call 004039AC
0042FAEA |. 8D55 F0 lea edx ,[ local .4]
0042FAED |. 8B83 E0010000 mov eax , dword ptr ds :[ ebx +0x1E0]
0042FAF3 |. E8 60AFFEFF call 0041AA58
0042FAF8 |. 8B55 F0 mov edx ,[ local .4]
0042FAFB |. 8B45 F4 mov eax ,[ local .3]
0042FAFE |. E8 F93EFDFF call 004039FC
0042FB03 75 1A jnz short 0042FB1F
0042FB05 |. 6A 00 push 0x0
0042FB07 |. B9 CCFB4200 mov ecx ,0042FBCC
0042FB0C |. BA D8FB4200 mov edx ,0042FBD8
0042FB11 |. A1 480A4300 mov eax , dword ptr ds :[0x430A48]
0042FB16 |. 8B00 mov eax , dword ptr ds :[ eax ]
0042FB18 |. E8 53A6FFFF call 0042A170
0042FB1D |. EB 18 jmp short 0042FB37
0042FB1F |> 6A 00 push 0x0
0042FB21 |. B9 74FB4200 mov ecx ,0042FB74
0042FB26 |. BA 80FB4200 mov edx ,0042FB80
|
总结:取第一个字母的ASNI的数字,如112233中第一个字符1对应数字0x31,然后用它乘以0x29,结果再自增一倍(即x2),将得到的数字转为10进制的字符串,在前加上”CW-”,后加上”-CRACKED”,就组成了用户名对应的注册码。 C/CPP程序: [C++] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include "stdafx.h"
#include "iostream"
int _tmain( int argc, _TCHAR* argv[])
{
printf ( "Input Name:\r\n" );
int cName = getchar ();
if ( cName > 0x21)
{
cName *= 0x29;
cName *= 2;
printf ( "Serial: CW-%4d-CRACKED\r\n" ,cName);
} else {
printf ( "input error!\r\n" );
}
system ( "pause" );
return 0;
}
|
第二个单独Serial流程同第一个, 但是这个很简单,直接在调用CALL那里向上F8走一遍就基本明白了。 核心代码: CALL之前的那个JNZ是爆破的关键,直接NOP就OK了。 JNZ之前的那个CALL是进行Serial判断的关键,通过单步跟踪发现他就是一个固定值。 代码如下: [Asm] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 0042F470 /. 55 push ebp
0042F471 |. 8BEC mov ebp , esp
0042F473 |. 33C9 xor ecx , ecx
0042F475 |. 51 push ecx
0042F476 |. 51 push ecx
0042F477 |. 51 push ecx
0042F478 |. 51 push ecx
0042F479 |. 53 push ebx
0042F47A |. 8BD8 mov ebx , eax
0042F47C |. 33C0 xor eax , eax
0042F47E |. 55 push ebp
0042F47F |. 68 2CF54200 push 0042F52C
0042F484 |. 64:FF30 push dword ptr fs :[ eax ]
0042F487 |. 64:8920 mov dword ptr fs :[ eax ], esp
0042F48A |. 8D45 FC lea eax ,[ local .1]
0042F48D |. BA 40F54200 mov edx ,0042F540
0042F492 |. E8 7142FDFF call 00403708
0042F497 |. 8D45 F8 lea eax ,[ local .2]
0042F49A |. BA 50F54200 mov edx ,0042F550
0042F49F |. E8 6442FDFF call 00403708
0042F4A4 |. FF75 FC push [ local .1]
0042F4A7 |. 68 60F54200 push 0042F560
0042F4AC |. FF75 F8 push [ local .2]
0042F4AF |. 8D45 F4 lea eax ,[ local .3]
0042F4B2 |. BA 03000000 mov edx ,0x3
0042F4B7 |. E8 F044FDFF call 004039AC
0042F4BC |. 8D55 F0 lea edx ,[ local .4]
0042F4BF |. 8B83 E0010000 mov eax , dword ptr ds :[ ebx +0x1E0]
0042F4C5 |. E8 8EB5FEFF call 0041AA58
0042F4CA |. 8B45 F0 mov eax ,[ local .4]
0042F4CD |. 8B55 F4 mov edx ,[ local .3]
0042F4D0 |. E8 2745FDFF call 004039FC
0042F4D5 75 1A jnz short 0042F4F1
0042F4D7 |. 6A 00 push 0x0
0042F4D9 |. B9 64F54200 mov ecx ,0042F564
0042F4DE |. BA 70F54200 mov edx ,0042F570
0042F4E3 |. A1 480A4300 mov eax , dword ptr ds :[0x430A48]
0042F4E8 |. 8B00 mov eax , dword ptr ds :[ eax ]
0042F4EA |. E8 81ACFFFF call 0042A170
0042F4EF |. EB 18 jmp short 0042F509
0042F4F1 |> 6A 00 push 0x0
0042F4F3 |. B9 84F54200 mov ecx ,0042F584
0042F4F8 |. BA 8CF54200 mov edx ,0042F58C
0042F4FD |. A1 480A4300 mov eax , dword ptr ds :[0x430A48]
0042F502 |. 8B00 mov eax , dword ptr ds :[ eax ]
0042F504 |. E8 67ACFFFF call 0042A170
0042F509 |> 33C0 xor eax , eax
|
这个没有动态生成的注册码,是一个固定的:Hello Dude!
PS: 哎,写个帖子真难,幸亏还是我用WLW写好的。
|