160个Crackme之039学习笔记
本帖最后由 海天一色001 于 2020-7-9 10:43 编辑第39个CM程序,解压出的文件有两个,一个CM程序,一个相关信息文件。
先打开damn.nfo这个文件,查看一下,大致是DAMN这个组织招人的测试要求,需要对这个程序打补丁、编写注册机,然后补丁、注册机还得是win32程序,还不能用VB、Delphi 或 Cpp builder 编写,最好用Win32 asm 或 c / c + + 等。我不必有这么高的要求,只要能爆破,编写出注册机就行了,最后可以学习一下打补丁。
打开damn.exe,上面显示出这不是其他的CM程序,这是DAMN工作室的加入测试,输入name=“52pojie”,Key=“12345677890”,右侧的“Register”按钮仍是灰色的不可用状态,下面程序状态为“LOCKED”即锁定状态,点击了一下这个大按钮,弹出锁定提示窗口:
左键点击标题栏左上角,弹出系统菜单,打开“about”菜单,内容翻译如下:“你正在参加DAMN's 组织的入门测验。本程序由EGOiSTE/DAMN编写。 首先为这个简单的密钥检查程序做一个注册机,然后尝试破解这个程序。锁定按钮应该显示未锁定的标志,如果按下了,程序必须说它现在是未锁定状态。 仅此而已。 你可以给文件打年补丁,不允许有其他的修改!如果你设法解决了这两个任务,请联系DAMN!祝你好运!”与damn.nfo文件内容相似,只是具体表示了补丁是用来更改锁定按钮上的图片的。(后面发现同时需要弹出的信息框内容也要是未锁定状态“UNLOCKED”)
第一步、查壳:
用ASPack加的壳,先用吾爱工具包中的ASPack UnPacker工具脱壳,生成一个damn_u.exe文件,再检测一下:fasm编程的软件。
第二步、爆破:用OD载入damn_u.exe,习惯性地用中文搜索,出来了文本内容:
双击0040110C一句进入CPU窗口,上下查看一下代码,从00401105到00401114处,这是弹出正确提示的消息框;
在00401105这一句是一个从004010EE跳转过来的指令,来到004010EE处,再上下看看代码: 这一段代码中有5个je跳转命令,由WM_COMMAND消息的值ax来决定跳转的目的,而004010EA处的ax要等于0x3ED才能跳到正确提示,所以将这里的cmp ax, 0x3ED改为cmp ax,ax(或者将下一句的je改为jmp均可),将修改后的程序保存为damn_u..cmp.exe(damn_u.jmp.exe),试运行一下:
此时两个程序界面中大按钮上的“LOCKED”图片已变成了“CRACKED”图片,鼠标点击到Name或key文本框,未输入任何字符时,都马上弹出正确信息窗,点掉后又弹出第二个正确信息窗来,可知爆破的位置算是正确的,只不过还不完美,“Register”按钮还是禁用状态。
00401163处改为mov eax,0x1,爆破成功,生成damn_u_eax.exe;
00401357处nop掉,也可爆破,key必须输入8个字符,生成damn_u_ nop.exe;
004012F3处改为mov eax,0x1,下一句改为retn,爆破成功,生成damn_u_retn.exe。
猜测:输入正确的name、key,则Register按钮可用,大按键图片变成CRACKED,点击后弹出正确提示框,大按键图片变成UNLOCKED,先按照这个思路来吧。
第三步、追码:
汇编程序一般都不长,程序代码也很清晰,所以单步从头开始,也算是对ASM编程的一次学习过程吧:
F8向下,到0040101A—00401029处,调用user32.dll中的DialogBoxParamA函数,其中0040101B处的指针指向了00401045处,也就是说运行到00401029处的调用函数时要跳转至00401045处。
00401045--004012E0处的代码段应该是显示主窗体所有内容的,但从00401029处F8向下,却直接弹出了CM的主窗体,只不过大按钮上的图片直接变成了“CRACKED!”
所以为了能更清楚地看到程序运行顺序,在00401045处下断点后重载CM程序,再F8单步向下,到00401029处运行后到00401045处被断下。
继续单步向下,第一次从00401045处向下eax=0x30;运行到00401073处,指令是retn 0x10,运行后来到系统领空user32.dll的77D18734处,Alt+F9键又返回到CM程序的00401045处。
继续单步,这一次(第二次)的eax=0x110;到00401067跳转到得到主窗体句柄的0040117D处,这一句的自动注释是“Case 110 (WM_INITDIALOG) of switch 0040104F”,应该是程序模块的入口点,在这里利用WM_INITDIALOG消息的值对编辑框控件进行初始化或修改,所以下一步会跳到Name或Key文本编辑框的代码段去。
但继续单步向下,不断地按F8键,时不时Alt+F9键,第二十次、第五十次、第N(N>100)次来到00401045处……
快崩溃了,在这个过程中CM程序的窗口开始一点一点地显示出来了,先是外框及标题栏,退出按钮、大按钮区域外框、程序状态小框、程序状态英文字符、大按钮框、大按钮图片(ed)和上方图标框同时出现、DAMN图片、DAMN图片标签文本第三行、DAMN图片标签文本第一行、DAMN图片标签文本第二行及下方第一个文本编辑框……却始终没有显示出主窗体的所有内容,更看不到OD左上角的“运行”字样!没这个标志,CM始终被中断,无法输入文字啊!!!!!!!!!!
隔了一段时间,因为上一次关闭虚拟机时没挂机,重新进入虚拟机的桌面,一眼看到了SoftSnoop的图标,心中一动,用它跟踪一下CM039试试:
很快弹出CM程序窗口,点击Name文本编辑框,SoftSnoop又显示了捕捉到的函数并显示出来,还没输入内容呢,就调用了GetDlgItemTextA函数,然后每输入一个字符,就调用一次GetDlgItemTextA函数,将得到的值存入0x00402353地址,返回值是字符串的长度;再点击Key文本编辑框,也是没输入内容,就调用GetDlgItemTextA函数,值存入0x00402321处,然后调用EnableWindow函数;每输入一次字符,就分别调用这两个函数一次,结合OD,可知上面关于启用“Register”按钮的猜测是正确的。
从OD中查看,00401122至00401134处得到Name字符串,00401147至00401159处得到Key字符串,再到00401163处调用call 004012F3这个注册算法的子程序,所以在004012F3处下断。
运行CM后,在Name文本编辑框中输入字符,再点击Key文本编辑框,程序中断,单步向下:
单步到00401339处,跳到0040136F处使eax=0,又retn到00401168处,让控件的可用属性为false,控件仍不可用;
F8向下,中间又进入user32.dll之中,返回后又来到004012F3处,继续向下,一直在这里打转,出不去了!
这是因为下断的时机不对,此时下断后因为还没有输入Key,所以ecx的值为零,必然会不断重复这个过程。
应该是在输入Key文本后,让Name字符串运算完成后再下断才对。在00401336处知道key必须为8位,所以先取消所有断点,然后F9运行程序,分别输入Name=“52pojie”和Key=“12345678”,返回OD中在0040123B处下断,再点击CM程序窗口中的key文本编辑框,程序中断于0040123B处。此时的大按钮显示的不是“CRACKED”而是“LOCKED”图片!这个问题先放一边,单步向下,继续研究注册算法:
注册算法应该是从004012F4到00401329处,主要是取Name字符串每一位的ASC值,经过一系列运算,将得到的值存入edx中:
004012F3/$90 nop ;注册算法开始:
004012F4|.8B0D 89234000 mov ecx,dword ptr ds: ;ecx=len(name):循环次数
004012FA|.85C9 test ecx,ecx ;ecx=7
004012FC|.74 71 je short damn_u.0040136F ;name框为空时跳
004012FE|.49 dec ecx ;ecx=ecx-1
004012FF|.8BF1 mov esi,ecx ;esi=ecx
00401301|.BF 53234000 mov edi,damn_u.00402353 ;edi=name
00401306|.BB 4E4D4144 mov ebx,0x44414D4E ;ebx=0x44414D4E(1145130318),字符是DAMN
0040130B|.33D2 xor edx,edx ;edx=0
0040130D|.8BCA mov ecx,edx ;ecx=0
0040130F|>33C0 /xor eax,eax ;eax清零
00401311|.8A040F |mov al,byte ptr ds: ;取name的每一个字符的16进制ASC码值
00401314|.03D0 |add edx,eax ;edx=edx+eax=35+0;35+32;35+32+70;asc(name(i))的累加值
00401316|.D1CB |ror ebx,1 ;将操作数ebx(二进制形式)循环右移,并且将右移的数据传递给CF标志位以及最高位
00401318|.D3CB |ror ebx,cl ;循环右移指令,被移出的位,补回到最左端
0040131A|.33DA |xor ebx,edx ;ebx=ebx xor edx
0040131C|.3BCE |cmp ecx,esi ;循环次数对比(以len(name)为循环次数)
0040131E|.74 03 |je short damn_u.00401323 ;跳出循环
00401320|.41 |inc ecx ;ecx=ecx+1:循环次数+1
00401321|.^ EB EC \jmp short damn_u.0040130F ;跳回循环开始
00401323|>81CB 10101010 or ebx,0x10101010 ;ebx=ebx xor 0x10101010=0x7BD63FBE
00401329|.87DA xchg edx,ebx ;交换edx与ebx中的内容
此时edi中得到的值是“7BD63FBE”,这就是根据输入的“52pojie”所对应的的注册码了。试着在CM程序中输入这两串字符,Register按钮可以使用了,点击后,弹出正确提示:
从0040132B到00401339处是注册码长度验证,Key必须输入8个字符:
0040132B|.BF 21234000 mov edi,damn_u.00402321 ;将输入的key存入edi中取代name
00401330 8B0D 8D234000 mov ecx,dword ptr ds: ;Key的字符串长度
00401336|.83F9 08 cmp ecx,0x8 ;ecx必须是8,否则失败
00401339|.75 34 jnz short damn_u.0040136F ;跳到eax清零,使Register按钮不能使用
从0040133B到00401371处是注册码字符验证部分,将输入的Key每一个字符与第一部分得出的结果进行相应对比,全部正确则使Register按钮启用,有一个字符错误则Register按钮禁用。继续单步向下:
0040133B|.33C9 xor ecx,ecx ;ecx清零,作为新的循环次数暂存器
0040133D|>33C0 /xor eax,eax ;eax清零
0040133F|.C1C2 08 |rol edx,0x8 ;edx循环左移8位 edx=rol(edx,8)=0xD63FBE7B/3FBE7BD6/BE7BD63F/7BD63FBE
00401342|.8AC2 |mov al,dl ;al=dl=0x7B/D6/3F/BE
00401344|.8AD8 |mov bl,al ;bl=al=0x7B/D6/3F/BE
00401346|.24 0F |and al,0xF ;al=al and 0xF = 0xB/0x6/0xF/0xE
00401348|.C0EB 04 |shr bl,0x4 ;bl=bl/2/2/2/2 =0x7?右移补0操作,将二进制位逐次在右边去掉一位然后在左边添个0,移多少位重复上述操作多少次
0040134B|.80E3 0F |and bl,0xF ;bl=bl and 0xF = 0x7/0xD/0x3/0xB
0040134E|.3C 0A |cmp al,0xA ;这个比较有什么用?
00401350|.1C 69 |sbb al,0x69 ;带借位减法 al=al-0x69=0xA2
00401352|.2F |das ;al=0x42:减法后的十进制调整,如果 AL 的低四位大于 9,或 AF=1,那么,AL=AL-06H,并置 AF=1;如果 AL 的高四位大于 9,或 CF=1,那么,AL=AL-60H,并置 CF=1;如果以上两点都不成立,则清除标志位 AF 和 CF。经过调整后,AL 的值仍是压缩型 BCD 码
00401353|.38444F 01 |cmp byte ptr ds:,al ;‘B’与‘2’比较?‘6’与‘4’、‘F’与‘6’、‘E’与‘8’比较
00401357|.75 16 |jnz short damn_u.0040136F
00401359|.8AC3 |mov al,bl
0040135B|.3C 0A |cmp al,0xA
0040135D|.1C 69 |sbb al,0x69
0040135F|.2F |das
00401360|.38044F |cmp byte ptr ds:,al ;‘7’与‘1’比较?‘D’与‘3’、‘3’与‘5’、‘B’与‘7’比较
00401363|.75 0A |jnz short damn_u.0040136F
00401365|.41 |inc ecx00401366|.83F9 04 |cmp ecx,0x4
00401369|.^ 75 D2 \jnz short damn_u.0040133D
0040136B|.33C0 xor eax,eax ;eax清零
0040136D|.40 inc eax ;eax=1,下步才能使Register按钮启用
0040136E|.C3 retn ;返回到 00401168 (damn_u.00401168)
0040136F|>33C0 xor eax,eax
00401371\.C3 retn ;返回到 00401168 (damn_u.00401168)
在00401353处,al的值分别是B/6/F/E,ds:从信息窗口可以看出分别是ds:、ds: 、ds: 、ds:,值分别是Key字符串的第2、4、6、8位字符;
在00401360处,al的值分别是7/D/3/B,ds:从信息窗口可以看出分别是ds:、ds: 、ds: 、ds:,分别是Key字符串的第1、3、5、7位字符;
cmp byte ptr ds:和al cmp byte ptr ds:,al的结果肯定是将Z标志置0,让下一句的跳转成立,注册码验证失败,所以每当运行到这两句时,双击寄存器窗口中Z标志位的“0”将其改为“1”,使跳转失效,然后继续单步运行(这里可以将00401357、00401363处指令nop掉,同样是爆破成功),循环4次后,将eax置1,返回到00401168处,Register按钮启用,程序基本上分析完成了。
注册机用VB做太难了,没有类似循环右移等指令,自己编的总是对不上汇编的结果等!在论坛里进行了求助,solly和JuncoJet两位大神热情指导,JuncoJet大神直接给出了VB函数,solly大神指出了我编程中的错误,所以就出现了两个注册机,注册机1是利用JuncoJet大神提供的函数编写,注册机2是在solly大神指正后自己编写的,其实也就是循环右移函数实现方法的不同,其他都是一样的。
终于成功了,但有可能出现溢出错误,目前还没有精力研究太深,如有精通VB的大神还可以多多指教。
第四步、补丁:
从程序的“about”窗口中可知,程序要求大按钮显示的图片既不是“LOCKED”也不是“CRACKED”,而是“UNLOCKED”。在网上搜了很久,才算是弄明白了程序运行顺序,大按钮加载位图在窗口显示之前已经完成了,我一开始的猜测注册成功后改变大按钮显示图片的想法是错误的。要想改变就必须在显示窗口之前更改了参数才行。仔细研究代码,从0040117D到004011BD处应该是大按钮处加载位图的程序段了,GetDlgItem函数取得要修改的控件的句柄,LoadBitmapA函数修改按钮图片:
从下向上分析,004011B6到004011BD处这三句,是加载位图文件资源的函数LoadBitmapA及其两个参数,重点在004011B6一句的eax值上,在爆破及追码的过程中也注意到了,如果先运行程序,输入Name/Key后再下断点,eax的值为0,按钮图片显示的是“LOCKED”,如果先下断点,再运行程序,那么此时eax的值为1,按钮图片显示的是“CRACKED”。
向上查看代码,在004011A7处用ds:对eax赋值;
再向上看,0040119D处指令为call damn_u.00401372,估计这个call得到ds:的值;
再向上,则是GetDlgItem函数取得要修改的控件的句柄了。
用两种下断方式来具体追踪一下eax的值:首先是先运行程序再下断点的方法,程序无法在0040117D至004011BD之间的任一处断下,原因很简单,这一段已经在断下前运行过了,自然无法再中断;此时点击004011A7这一行,可以看到在信息窗口中ds:=00000000,也就是eax将被赋值为0;再向下到004011B4处,edi=ds:[0x4022E7]=0x004022F3,到004011B6处,eax=ds:=0x66,ASCII值是“f”;
从下面内存窗口中可以看到红色部分是CM程序标题栏内容、弹出信息框的标题栏内容、弹出信息框的文本内容三种状态的所有文本,猜测是大按钮图片的“f”“e”“g”三个指针。
接下来是在004011A7处下断,F9运行程序,直接中断于004011A7,此时ds:=00000001;
F8向下到004011B4处,edi=ds:[0x4022E7+4]= ds:=0x004022F7;到004011B6处,eax=ds:=0x65,ASCII值是“e”。
继续向下,到004011BD处加载位图;
继续向下,到004011DA处用SendDlgItemMessageA发送对话框消息;
继续向下,到004011E4至004011EF处,代码与004011A7至004011B6处有了重复,仍然用ds:对eax赋值,最终计算得到eax=ds: =0x0040224C (damn_u.0040224C), ASCII "DAMN's TryMe - CRACKED!",作为SetWindowTextA的文本参数;
继续向下,到004011F8处,调用SetWindowTextA函数改变标题内容;
继续向下,到004012A6处,指令为retn 0x10,返回到 77D18734 (user32.77D18734);
继续单步,进入user32.dll领空内,Alt+F9键运行到用户程序,马上弹出CM窗口,此时的大按钮图片已是“CRACKED”了。
再点击大按钮,弹出提示框“Program has been CRACKED!”。
由上面分析出,ds:的值决定了大按钮图片显示的结果,0为锁定,1为破解,那么2可能就是未锁定了。
所以Ctrl+F2键重新加载CM,F9运行,中断于004011A7处,将004011A7与004011E4处的代码“mov eax,dword ptr ds:”改成“mov eax,2”,
单步向下,到004011B6处,eax=ds:=0x67,ASCII值是“g”;
继续单步,到004011F1处,注释中看到“Text = "DAMN's TryMe - UNLOCKED!"”;
继续向下,到004012A6处,指令为retn 0x10,返回到 77D18734 (user32.77D18734);
继续单步,进入user32.dll领空内,Alt+F9键运行到用户程序,马上弹出CM窗口,此时的大按钮图片已是“UNLOCKED”,CM标题也变成了“DAMN’s TryMe – UNLOCKED!”了。再点击大按钮,弹出提示框仍是“Program has been CRACKED!”而不是“UNLOCKED”,说明可能还有地方没改过来。
点掉提示框,回到OD中,代码停留在004012DC处,上一行是调用MessageBoxA函数;
再向上查看代码,又发现了两处ds::004012BF、004012A9处指令均为mov eax,dword ptr ds:,干脆将这两处也改为“mov eax,2”,总共4处,将修改后的文件保存成damn_u_path.exe,试运行一下,大按钮图片显示、CM程序标题内容、点击大按钮弹出的信息框内容均显示了“UNLOCKED”,说明这种爆破算是正确的,达到了作者的要求。
综上所述,ds:的值(1、2、3)决定了大按钮的图片、CM程序标题栏的内容、弹出信息框的标题及内容。只要找到给ds: 赋值的地方,将返回值改成2即可,不用象上面修改4个地方,说不定还有什么地方没修改到呢。
Ctrl+F2重载CM程序:因为大按钮图片加载是在CM窗口显示之前就确定的,所以来到004011A7处,向上查看代码,在GetDlgItem函数之后,第一次用ds:给eax赋值之前的0040119D处调用子程序call damn_u.00401372应该就是给ds:赋值的call了。在此处下断,F7跟入:
到004013BA处,一个短跳到004013C4处,基本上可以看出上面一大堆的运算没什么用,只看edx与0x11145445是否相等就行了,相等跳到00402000处,不等则令edx=1,然后跳到0040201C处。所以先跟到0040201C处:
看出在这里给ds:赋值为1,下一句返回到 004011A2 处,所以是“CRACKED”状态;
Ctrl+F2再次重载CM程序,F9运行,F7跟入,直接来到004013B4处,F4键,在右侧寄存器窗口将edx的值改为0x11145445,F8键向下,此时edx=0,跳转到00402000处成立,继续单步向下:
可以看到,又冒出了个ds:出来,此时可以看到ds:=1,则又跳到0040201C处,使得edx=ds:=0,返回004011A2后会变成默认的“LOCKED”状态;所以此时的ds:应该为0,不跳到0040201C处,而是继续向下。所以在00402005处直接将eax的值改成0后再单步,运行完0040200F、00402013的指令后,程序从00402019到0040201处的自身代码发生了改变,可能是自校验吧?
先不管它,继续单步向下,到0040201C处,ds:=2了。那么返回到004011A2后,程序状态变成了“UNLOCKED”。
继续向下,到004011F8处运行后,看到004011F2处的注释变成了“hWnd = 000A048C ('DAMN's TryMe - UNLOCKED!',class='#32770')”;继续向下,到004012A6处进入user32.dll领空,Alt+F9键返回到用户领空,弹出CM窗口,标题内容和大按钮均是“UNLOCKED”了。
点击大按钮,弹出信息框,内容却还是“CRACKED”。这样看,说明在点击大按钮后ds:的值又发生了改变。点掉信息框,程序中断于004012DC处,向上看,
004012A9到004012BF之间有一个call damn_u.004012E3,应该就是改变ds:值为1的地方了,nop掉试一试?
再次重载程序,在004011A9和004012B5处下断,然后再F9,和上一次一样进入00401372,到00401384处将edx改成0x11145445,到00402005处将eax改成0,再到到004011A2处,F9键运行,弹出CM程序窗口,点击大按钮,中断于004012B5处,将此处nop掉,F9运行,弹出信息框,内容已变成了“UNLOCKED”了。成功了。
那么还可以将004012B0处的je改成jmp,还可以这里不nop,在004012E3处直接修改成retn,这都应该是正确的。
最终我的修改方案是在004012E3处修改成“retn”,在00401372处第一句修改为“mov dword ptr ds:,0x2”,第二句直接“retn”,保存到文件damn_u_path_1.exe中。这种方案比damn_u_path.exe要少修改两处地方。其实这中间还有很多不是很明白的地方,如00401372中一大堆的运算是什么意思,ds:怎么使用等,还需要努力。
附件,含CM原程序、爆破后的程序、注册机、OD等的调试文件等。160个CM、我已练习过的前39个crackme程序(不含012)都在里面。
百度链接:https://pan.baidu.com/s/1NEpyyUefbKcoJvMGG_9OmQ
提取码:p8dk liwenda321 发表于 2020-8-12 20:53
大神,为什么我用peid9.5侦壳damn_u.exe文件,显示什么也没找到
PEID侦测的结果不一定准确,毕竟好长时间没更新了,可能我们使用的版本虽一样,插件不一定相同,或者是扫描方式不一样啊。
另外还有EXEInfoPE、DetectItEasy等其他软件可以互相参考。 感谢楼主分享,学习了。00401372下面的一段运算是对程序部分代码的的校验,可以检查程序是否下了断点。这也正是先下断再运行后程序与直接运行程序会有区别的原因。 大佬牛皮{:301_973:}{:301_973:}{:301_973:} 感谢分享 学习 学习 厉害,,厉害。佩服 只能套用楼上的话:大佬牛皮{:301_973:} 感谢分享,每天都能学习新的知识。 佩服大佬的坚持,没有什么逆向经验的我也会跟着大佬学习 学习新姿势,学习新技术{:301_988:} 看不懂呐 楼主工具挺多的嘛