海天一色001 发表于 2019-6-11 08:54

160个Crackme之024学习笔记

本帖最后由 海天一色001 于 2019-6-11 09:04 编辑

打开024,与023除了版本号外界面几乎完全相同,输入Name/Serial=“52pojie/1234567890”,程序界面如下:

Status栏中仍然是错误提示,点击About按钮,弹出提示框,内容与023一样(除了版本与时间不一样):

这个程序同样是关闭按钮和Alt+F4组合键都不管用,只能用右键点击弹出的菜单中选择“Alt+F4”这一项才能关闭。先查壳,无壳,显示是ASM程序:

第一步、爆破将Chafe.2.exe加载入OD中,习惯性地用智能搜索去查字符串,很轻松地得到了各类文本内容:

双击00401301这一行正确提示处来到CPU窗口:

这里和023不一样之处在于没找到跳转到正确提示的指令,只在信息窗口看到错误提示上有两个跳转来源“跳转来自0040128A,004012A1”:

先看看第一个跳,00401281处命令是GetDlgItemInt是得到整数,结合023分析应该是得到注册码,而0040129A处的命令是GetWindowTextA则是得到文本,应该是name。

但为什么是先得到注册码后得到name,有点不太理解。先不管这些,试试将0040128A处跳到错误提示的语句改到正确提示的地址00401031上来,

将所有修改保存到可执行文件Chafe.2.jmp.exe,试运行一下,鼠标点击到name框,准备输入字符时,下方的Status框中就出现了正确提示,说明这样的爆破也算是成功了。

第二步、追码
撤消刚才爆破时的修改,然后上下观察代码,没有时间控件了,也没找到直接跳转到成功的语句,这个CM明显比上一个复杂多了。

已知GetDlgItemInt()函数是得到注册码,所以在00401281处下断,F9运行一下试试:鼠标刚点入name框,程序就中断了,说明程序只要输入name就开始判断了,但没输入时怎么判断呢?虽然不太清楚作者怎么做到的,但此时任何内容都没输入,断在此处不太合适,改在0040129A处再试:这次name框我输进去了“52pojie”,在serial框点鼠标没事,刚点击键盘上的“1”, 界面上还没出现“1”时程序就中断了。F8向下,注释中马上出现了“CTEX”与刚输入的name“52pojie”,此时ds:= CTEX,0040316C地址处存放ASCII "52pojie";说明这个断点应该可以使用。

由于Serial=1太难以在代码中找到具体位置,所以Ctrl+F12重新加载程序,name输入“52pojie”,serial粘贴入“1234567890”,程序又中断于0040129A处。那么就继续单步向下走,多次重载程序运行,代码分析如下:(1)00401269至0040128C处,给ds:赋值为0x584554,到第(4)步时使用;GetDlgItemInt()得到serial的值,不能为空,将得到的值压入堆栈SS:中:
00401269   > \C705 D9124000>mov dword ptr ds:,0x584554
00401273   .6A 00         push 0x0                                 ; /IsSigned = FALSE
00401275   .8D45 FC       lea eax,dword ptr ss:         ; |
00401278   .50            push eax                                 ; |pSuccess = 015E9FC1
00401279   .6A 64         push 0x64                              ; |ControlID = 64 (100.)
0040127B   .FF35 50314000 push dword ptr ds:             ; |hWnd = 001A077A ('TEXme v2.0',class='CTEX')
00401281   .E8 BC010000   call <jmp.&USER32.GetDlgItemInt>         ; \GetDlgItemInt
00401286   .837D FC 00    cmp dword ptr ss:,0x0         ;Serial不能为空,否则失败
0040128A      74 5F         je short Chafe_2.004012EB
0040128C   .50            push eax                                 ;=hex(serial)
(2) 0040128D至004012A1处,GetWindowTextA()函数取得name的字符串,并判断是否为空,空则失败:
0040128D.6A 14         push 0x14                              ; /Count = 14(20.)
0040128F.68 6C314000   push Chafe_2.0040316C                  ; |Buffer =Chafe_2.0040316C
00401294.FF35 54314000 push dword ptrds:             ; |hWnd =0017072E (class='Edit',parent=001A077A)
0040129A.E8 AF010000   call<jmp.&USER32.GetWindowTextA>       ; \ GetWindowTextA(所在的句柄,存入的内存地址,取字符串的长度)
0040129F.85C0          test eax,eax                           ;eax=len(name)
004012A1      74 48         je short Chafe_2.004012EB                ;name不为空,否则到失败
(3)004012A3至004012B6处,以“CTEX”和得到的name文本进行相应的计算,得到的结果存入中:
004012A3.A1 0B304000   mov eax,dword ptr ds:          ; ="CTEX"的反向取ANSI16进制编码(58455443)
004012A8.BB 6C314000   mov ebx,Chafe_2.0040316C               ;ds:=name字符串逆向取4位的ANSI16进制数值
004012AD>0303          add eax,dword ptr ds:               ;=+ds:
004012AF.43            inc ebx                                  ;=+1
004012B0.81FB 7C314000 cmpebx,Chafe_2.0040317C                ;ebx地址为0040317C时结束循环,共循环16次
004012B6.^ 75 F5         jnz shortChafe_2.004012AD               ;构成循环,不断累加
(4)004012B8至004012C4取得到的serial数值与相加,再与ds:异或,存入ds:中,实际上是修改004012D9至004012DC处的指令,正确的结果应该是将004012D9处指令修改成“jmp 00401301”:
004012B8.5B            pop ebx                                  ;=hex(serial)
004012B9.03C3          add eax,ebx                              ;=+
004012BB.3105 D9124000 xor dword ptrds:,eax          ;ds:=ds: xor
004012C1.C1E8 10       shr eax,0x10                           ;逻辑右移0x10位,即取的高位
004012C4.66:2905 D9124>sub word ptrds:,ax            ;ds:=ds: -
如下所示,004012D9处未修改时这4个字节的16进制值是00584554:

当与异或后变成了4AACE7C7:

再减去后变成了4AAC9CD3:

(5)进行0x3E(62)次的异或运算,最终结果要达到=0xAFFCCFFB:
004012CB.BE EC114000   mov esi,Chafe_2.004011EC
004012D0.B9 3E000000   mov ecx,0x3E
004012D5.33DB          xor ebx,ebx                              ;清零
004012D7.EB 04         jmp short Chafe_2.004012DD
004012D9>54            push esp
004012DA      45            db 45                                    ;CHAR 'E'
004012DB      58            db 58                                    ;CHAR 'X'
004012DC      00            db 00
004012DD>AD                   lods dword ptr ds:                  ;mov eax,dword ptr; add esi,4;
004012DE.33D8               xor ebx,eax                              ;= xor ds:
004012E0.49            dec ecx                                  ;=0时循环结束!!!!(62次)
004012E1.^ 75 FA         jnz shortChafe_2.004012DD               ;应该等于0xAFFCCFFB才行
004012E3.81FB FBCFFCAF cmpebx,0xAFFCCFFB                      ;关键比较004012E9.^ 74 EE         je shortChafe_2.004012D9                ;关键跳???
重点就在于第5部分,004012D7处命令跳到了004012DD处,跳过了被第4部分中修改了内容的004012D9至004012DC处的4个字节,原来未修改前的内存中存储的是00584554,转换成汇编命令是 push esp、db 45、db 58、db 00,而我用“52pojie/1234567890”这一组内容来调试时,甚至将004012DD、004012DF处的代码都给改了,如下所示,这个改动太大了,变成了“rcr dword ptr ss:,cl”,程序肯定不会跳向正确,甚至可能会出现各种错误。实际运行过程中发现,自己一厢情愿的思路是错误的,程序执行中只修改了4个字节,从004012DD处并没有修改,只是和上面的代码被OD合在一起去反汇编解释了一下,好像是错误,实际上仍然能执行。这其实也是计算机原理的知识,以前有一点了解,但不是很明白,遇到这里的例子倒是弄明白了。
这段程序理解时就相当于把004012D7至004012DC这五句移走,将最后一句转向正确提示,也就成了以下代码:
mov esi,Chafe_2.004011EC
mov ecx,0x3E
xor ebx,ebx                              ;清零
lods dword ptr ds:                  ;=ds:
xor ebx,eax
dec ecx                                  ;=0时循环结束!!!!(62次)
jnz short Chafe_2.004012DD               ;应该等于0xAFFCCFFB才行
cmp ebx,0xAFFCCFFB                     ;关键比较
je short Chafe_2.00401301                ;关键跳
等于0xAFFCCFFB时,下一命令跳到了004012D9,按照修改前的代码看,小于0,只会使程序陷入无限的循环之中;所以输入的“name/serial”组合必须在第4部分使生成的指令变成“jmp 00401301”,在OD的CPU窗口中看到16进制数值为“EB 26”,内存中查看应该是“26EB”。那么反过来进行逆推就可以得到被修改后的004012D9至004012DC这四个内存地址的值:
首先004011EC+3E*3=004012E4,也就是说从004011EC至004012E3处的内存总共有62个数值要和进行异或运算,最终得到0xAFFCCFFB,所以先把004012D9至004012DC这四个内存地址的值用“00”填充,也就是将“38 D9 06 01”修改为“0000 00 00”,再用0xAFFCCFFB与004011EC至004012E3处的内存总共有62个已知数值进行异或运算,得到004012DB和004012DC处修改数值与异或的结果为“5426EB58”;004012D9、004012DA处的值已经确定为EB26,则004012DB和004012DC处的数值为54、58中的一个;随意选一个值在004012DB中,另一个在004012DC中,再运行一遍,如果xor最终结果为0,则正确,否则再将两处的数值反过来试试,应该就是正确的:经计算,004012DB处的值为54,004012DC处的数值为58。


   
004012D9处未修改前这4个字节的原始16进制值是00584554,修改后变成了585426EB;也就是说,第四部分的算法可以整理为:
(0xXXXXYYYY xor 0x00584554)-(0x0000XXXX)=0x585426EB(“0xXXXXYYYY”中,“XXXX”表示的高四位值,“YYYY”表示的低四位值),可继续化为:
(0xXXXXYYYY xor 0x00584554)=0x585426EB+0x00000XXXX=0x585400000+(0x26EB+0xXXXX),那么高四位的0xXXXX=0x5854xor 0x0058=0x580C,或者是XXXX=0x5855xor 0x0058=0x580D(有进位的情况);
低四位的YYYY=(0x26EB+0x580C)xor 0x4554= 0x3BA3,或者是YYYY=(0x26EB+0x580D) xor 0x4554= 0x3BAC,但实际上0x26EB+0x580D=0x7EF8,没有进位,所以的高四位值是0x580C,低四位值是0x3BA3,连起来就是0x580C3BA3了。
用0x580C3BA3减去第二步中“CTEX”与取得的name字符串运算结果,就是注册码的16进制数值,再转为10进制数值,注册码就出来了。
说起来容易做起来难,主要是VB数据老溢出太令人难受!
注册机主要代码如下:
Serial = 1480938563 '"CTEX"的十六进制数值58455443转换成10进制的结果
      NameStr = Text1.Text                                    
      If NameStr = "" Then NameStr = "52pojie"               
            Text1.Text = NameStr
      If Len(NameStr) > 19 Then NameStr = Mid(NameStr, 1, 19)
      If Len(NameStr) < 19 Then
            For i = 1 To Len(NameStr)
                NameHex(i) = Hex(Asc(Mid(NameStr, i, 1)))
            Next
            For i = Len(NameStr) + 1 To 19
                NameHex(i) = "00"
            Next
      End If
      For i = 1 To 16
            NameUnit = "&H" & NameHex(i + 3) & NameHex(i + 2) & NameHex(i + 1) & NameHex(i)
            nameD = Val(NameUnit)                     
            nameB = DectoBin(Trim(nameD))            
            Serial = Serial + nameD                  
            If Serial >= 4294967296# Then Serial = Serial - 4294967296#
            'Form1.Print Bin2Hex(Dec2Bin(Trim(Str(Serial))))
      Next
      Serial = 1477196707 - Serial
      If Serial < 0 Then Serial = 4294967296# + Serial
      Text2.Text = Serial
附件,含CM原程序、爆破后的程序、注册机、OD的调试文件等。
百度链接是:http://pan.baidu.com/s/1skMkJY9密码: 86pm,160个CM、我已练习过的前24个crackme程序(不含012)都在里面。

wenguolong 发表于 2019-6-11 09:12

沙发。。。。

bugman168 发表于 2019-6-11 09:40

高手。。

yjj008818 发表于 2019-6-11 13:02

喜欢这样的文章,期待深入学习

zzcl558 发表于 2019-6-12 06:03

高手啊,谢谢分享!

木丁哂 发表于 2019-6-12 12:18

学习到了,最近有学习的欲望,希望大佬多分享一点

鲶鱼的忧郁 发表于 2019-6-13 08:50

感谢楼主分享
页: [1]
查看完整版本: 160个Crackme之024学习笔记