海天一色001 发表于 2019-7-21 13:23

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)都在里面。

rongdadaya 发表于 2019-7-21 15:37

学习学习

tsai1220 发表于 2019-7-21 15:46

厉害了,学习

fuzheng 发表于 2019-7-21 17:13

谢谢,学习到了!!

qhczdy 发表于 2019-7-21 17:15

谢谢分享!

易小志 发表于 2019-7-21 18:29

感谢分享,这个需要慢慢体会学习
页: [1]
查看完整版本: 160个Crackme之032学习笔记