160个Crackme之032学习笔记
本帖最后由 海天一色001 于 2019-7-21 13:25 编辑第32个CM程序,这个程序一运行就是弹出一个提示,大意是缺少了Reg.dat文件,如果没有这个文件,程序就不能正确显示之类。点击确定,程序关闭,再次打开,仍是这样。
那么试着在CM032所在目录新建一个Reg.dat空文件,运行crackme.2.exe,成功了,出现程序主界面,在菜单栏上File中只有一个退出命令;About菜单内一个“About”命令,打开没有什么对破解有帮助的;
一个“Register”命令,弹出注册界面:
输入name=“52pojie”,选择4个数字作为serial,“OK”按钮是灰色,没法继续下去,那么就需要破解了。
将Reg.dat删除或改名,重新运行程序又弹出提示,无法进入主界面了。
第一步、查壳:
有壳,用Delphi编程的,先脱壳吧:使用PEiD自带的通用脱壳器,成功生成crackme.2.exe.unpacked_.exe文件:
先试运行,程序打开正常,再查壳,已经脱壳了:
第二步、爆破:
用OD载入crackme.2.exe.unpacked_.exe,CPU窗口中直接看到了不能进入主界面的提示窗口内容:
向上查看至00438411处,指令是je short crackme_.00438428,跳过了这个提示,所以将这里的关键跳je改为jmp试一下,看能否不用Reg.dat也进入主界面:
修改后的文件保存为crackme.2.exe.unpacked_jmp.dat.exe,先将Reg.dat改名为Reg.dat1,运行crackme.2.exe,又弹出不能进入主界面的提示窗口;运行crackme.2.exe.unpacked_jmp.dat.exe,CM主窗口出现,说明这一点成功了。
这应该不算是主程序的爆破,所以还是将Reg.dat1改回Reg.dat,将00438411处指令修改撤消,继续找注册界面上的按钮事件:
上下查看代码,一时半会儿有点找不到入手点,这次用IDR来辅助吧:
从IDR中很简单地看出三个窗体的名称“Reg”、“About”、“Main”,“Reg”窗口的事件在下边窗口中也显示出来了:
将IDR生成的map文件导入OD中,这个map文件仍然没把注释完整转存过来,还得手动对照复制一下:
Ctrl+G转到00437EA0处,在Button1Click事件中可以看出没有什么算法和比较之类的,直接就是调用正确提示,所以应该是输入name和serial时就进行了注册码的运算,二者适配之后就使OK按钮生效,弹出正确提示。
所以就要看是哪个事件里来调用了OK按钮事件:
Edit1-4事件的内容基本一致,参数eax分别是1、2、3、4,然后call 00437D1C,所以再进入00437D1C处看看:
注册码处理程序从调用00437D1C开始,那么在00437D1C处改成跳到OK按钮事件开始00437EA0处,保存为crackme.437D1C.jmp.437EA0.exe,试运行,点击“Register”命令时弹出正确提示,点提示框的确定按钮,再一次弹
出正确提示,共弹出4次后出现了Reg窗口,仍是OK按钮灰色不可用的状态。这说明这里的跳是不正确的,
从00437D34处到00437D3E处,是注册码第一位到第四位时的处理代码,跳转后的代码也是一样的,调用参数不同的call(0041D1C0、00406930),跳转到00437DD3处后再调用00437BD8这个call,再运行到00437DE8处调用004036FC处的call。因为四个注册码数字处理后都要跳到00437BD8处,所以Ctrl+G来到00437BD8处,试一下将此处直接改为跳到00437EA0处,保存为crackme.437BD8.jmp.437EA0.exe,运行一下,和crackme.437D1C.jmp.437EA0.exe一样先弹出4次正确提示再出现Reg窗口,仍未爆破成功。
再想一想,这两处爆破的jmp确实不正确,没有找到相应的算法,或者说还没有找到让OK按钮生效的比较语句,急于跳转,所以会出错。
撤消刚才的修改,在00437BD8下断,F9运行,F8单步向下,
结合IDR中的注释,一直到00437CC9处,指令为jnz short crackme_.00437CDF,是关键跳,跳过成功提示,查看上下文代码,可在此nop掉,则程序应该爆破成功。试一下:
保存修改后的可执行文件为crackme.2.exe.unpacked_.nop.exe,运行一下,OK按钮已能用,点击后弹出成功提示框,爆破成功。
第三步、追码:
从爆破的过程中已经看到,关键call 00437D1C中每得到1个serial的数字,就转入子程序00437BD8中取1次name进行运算比较,4次之后才比较完成,所以为提高效率,在OD的CPU窗口中先来到00437BD8处, F9运行CM程序,输入name=“52pojie”,serial的4个框内先随意选择4个数字,如“3、2、1、5”,返回OD在00437BD8处下断,再F9运行CM,点击第4个注册码数字旁边的选择小三角,使CM中断(此时最后1个serial数字应该要发生改变但还没来得及到“6”),然后再F8单步向下:
从00437BF2到00437C0D处,得到name文本及长度,如果name长度不到5位则跳向失败;
从00437C13到00437C5D处为取name文本中的第1、3、4、5个字符转成对应的ASC值(00437C16、00437C29、00437C3D、00437C51处),整除0xA的值存入连续的内存地址ds:、ds:、ds:、ds:中去;
00437C13 8B45 FC mov eax,dword ptr ss: ; =name.text
00437C16 0FB600 movzx eax,byte ptr ds: ; =asc(mid(name.text,1,1))
00437C19 B9 0A000000 mov ecx,0xA ; ecx=0xA
00437C1E 99 cdq ; if eax<80000000 then edx=00000000;esle edx=FFFFFFFF
00437C1F F7F9 idiv ecx ; eax(商)=eax\ecx,edx(余数)=eax mod ecx
00437C21 A3 2CA74300 mov dword ptr ds:,eax ;ds:==asc(mid(name.text,1,1,1))\0xA
00437C26 8B45 FC mov eax,dword ptr ss: ; =name.text
00437C29 0FB640 02 movzx eax,byte ptr ds: ; =asc(mid(name.text,3,1))
00437C2D B9 0A000000 mov ecx,0xA ; ecx=0xA
00437C32 99 cdq
00437C33 F7F9 idiv ecx ; =asc(mid(name.text,3,1))\0xA=asc(mid(name.text,3,1)) mod 0xA
00437C35 A3 30A74300 mov dword ptr ds:,eax ; ds:=asc(mid(name.text,3,1))\0xA
00437C3A 8B45 FC mov eax,dword ptr ss:
00437C3D 0FB640 03 movzx eax,byte ptr ds: ; =asc(mid(name.text,4,1))
00437C41 B9 0A000000 mov ecx,0xA
00437C46 99 cdq
00437C47 F7F9 idiv ecx
00437C49 A3 34A74300 mov dword ptr ds:,eax ; ds:=asc(mid(name.text,4,1))\0xA
00437C4E 8B45 FC mov eax,dword ptr ss:
00437C51 0FB640 04 movzx eax,byte ptr ds: ; =asc(mid(name.text,5,1))
00437C55 B9 0A000000 mov ecx,0xA
00437C5A 99 cdq
00437C5B F7F9 idiv ecx
00437C5D A3 38A74300 mov dword ptr ds:,eax ; ds:=asc(mid(name.text,5,1))\0xA
00437C62到00437C94处循环,对得到的4个数据进一步处理,如果数值大于10,则将数据再整除10的值存入ds:、ds:[ 0043A730]、ds:[ 0043A734]、ds:[ 0043A738],也就是说得到的注册码数字都是小于10的数字:
00437C62 BE 01000000 mov esi,0x1
00437C67 BB 2CA74300 mov ebx,crackme_.0043A72C
00437C6C 8D55 F8 lea edx,dword ptr ss:
00437C6F 8B03 mov eax,dword ptr ds: ; 得到取name.text中字符进行运算后的每一个数字
00437C71 E8 8AECFCFF call <crackme_.SysUtils.IntToStr> ; 数值转换成字符形式
00437C76 8B45 F8 mov eax,dword ptr ss: ; =str(mid(name.text,1,1)\0xA),存入中
00437C79 E8 FABCFCFF call <crackme_.System.@LStrLen> ; 取长度
00437C7E 48 dec eax ; 如果长度为1位,也就是说计算出的结果是10以内的数字时,跳走
00437C7F 74 0C je short crackme_.00437C8D ; 如果超过1位,则进入下一行
00437C81 8B03 mov eax,dword ptr ds:
00437C83 B9 0A000000 mov ecx,0xA
00437C88 99 cdq
00437C89 F7F9 idiv ecx ; =asc(mid(name.text,1,1)\0xA)
00437C8B 8903 mov dword ptr ds:,eax
00437C8D 46 inc esi ; esi累加1:循环变量加1
00437C8E 83C3 04 add ebx,0x4 ; ebx=ebx+4:数据存储地址+4
00437C91 83FE 05 cmp esi,0x5 ; esi作为循环次数,总计4次
00437C94^ 75 D6 jnz short crackme_.00437C6C ; esi<5时循环
从00437C96到00437CC0处,循环比较ds:、+4、+4、+4; ds:、+4、+4、+4:
00437C96 BE 01000000 mov esi,0x1 ; esi=5时令esi=1
00437C9B B8 2CA74300 mov eax,crackme_.0043A72C ; 计算出的注册码存储地址
00437CA0 BA 3CA74300 mov edx,crackme_.0043A73C ; 输入的注册码存储地址
00437CA5 8B0A mov ecx,dword ptr ds:
00437CA7 3B08 cmp ecx,dword ptr ds: ; 循环比较ds: ds:
00437CA9 74 07 je short crackme_.00437CB2 ; 两数相等,则ecx=0,继续比较下一个数字;不等则ecx=1,跳到失败
00437CAB B9 01000000 mov ecx,0x1
00437CB0 EB 15 jmp short crackme_.00437CC7
00437CB2 33C9 xor ecx,ecx
00437CB4 46 inc esi
00437CB5 83C2 04 add edx,0x4
00437CB8 83C0 04 add eax,0x4
00437CBB 83FE 05 cmp esi,0x5
00437CBE^ 75 E5 jnz short crackme_.00437CA5 ; 循环
00437CC0 EB 05 jmp short crackme_.00437CC7
综上分析可知,name必须不少于5位字符,然后取name字符串的第1、3、4、5位字符的ASC值分别与10整除,得数小于10,则作为注册码的一个值;大于10,则得数再整除10得到的结果作为注册码的一个值。
算法的VB主要代码如下:
Private Sub Command1_Click()
Dim name, serial As String
Dim Pwd(1 To 5), i As Integer
name = Text1.Text
If name = "" Then
Text1.Text = "52pojie"
Text2.Text = "5-1-1-1"
Exit Sub
End If
If Len(name) < 5 Then MsgBox ("请输入不少于5位的字符!")
For i = 1 To 5
Pwd(i) = Asc(Mid(name, i, 1)) \ 10
If Pwd(i) > 9 Then Pwd(i) = Pwd(i) \ 10
Next i
serial = Pwd(1) & "-" & Pwd(3) & "-" & Pwd(4) & "-" & Pwd(5)
Text2.Text = serial
End Sub
附件,含CM原程序、爆破后的程序、注册机、OD的调试文件等。百度链接是:http://pan.baidu.com/s/1skMkJY9,密码: 86pm,160个CM、我已练习过的前32个crackme程序(不含012)都在里面。
学习学习 厉害了,学习 谢谢,学习到了!! 谢谢分享! 感谢分享,这个需要慢慢体会学习
页:
[1]