首页
社区
课程
招聘
[原创]CrackMe016的破解分析(献给初学者)
2005-12-16 15:22 8793

[原创]CrackMe016的破解分析(献给初学者)

2005-12-16 15:22
8793
[前言]
Crack016.exe,这是《加密与解密》(第二版)中的一个动态跟踪调试示例,我没有这本书,是看到有人上传了这个附件,就下来看看,正好有人要求写篇面向初学者的文章,于是写了篇破解分析,我尽量写清楚自己的思路和想法,希望便于初学者理解。
[作者]HillCat
[日期]2005-12-8
[分析]
破解的一般步骤是:
1.查壳,如果有壳看是否能快速搞定;壳有点麻烦的话,我一般是先放一边。
2.了解编写语言,了解程序注册方式,形成破解思路。(这里分类展开可以写成长篇了,我就省了)
3.由简到易,查看字符参考,设置API断点,使用内存断点,使用万能断点,其他方法。
4.破解形式包括,找出明码,修改跳转爆破,跟踪算法分析出注册码。
 

好了,按照上面的步骤,进行实际操练。

PEID查看无壳,VC++编写;序列号注册方式,注册错误有提示。
于是想到可以尝试:MessageBoxA,GetDlgItemTextA

OD载入程序,不管怎样,首先还是查看一下超级字符参考,发现:

004010DD        MOV ESI,CrackMe0.00405060        你输入字符要大于四个!
0040110F        MOV ESI,CrackMe0.00405038        序列号错误,再来一次!

没有成功的提示,看来是对注册成功的字符串进行了处理,或者注册成功后没有消息提示。

从字符参考返回来到:
004010D6   .  56            PUSH ESI
004010D7   .  57            PUSH EDI
004010D8   .  B9 05000000   MOV ECX,5
004010DD   .  BE 60504000   MOV ESI,CrackMe0.00405060                        ;  你输入字符要大于四个!
004010E2   .  8D7C24 18     LEA EDI,DWORD PTR SS:[ESP+18]
004010E6   .  A1 50504000   MOV EAX,DWORD PTR DS:[405050]
004010EB   .  F3:A5         REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
004010ED   .  8B0D 54504000 MOV ECX,DWORD PTR DS:[405054]
004010F3   .  8B15 58504000 MOV EDX,DWORD PTR DS:[405058]
004010F9   .  66:A5         MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]
004010FB   .  894C24 0C     MOV DWORD PTR SS:[ESP+C],ECX
004010FF   .  8A0D 5E504000 MOV CL,BYTE PTR DS:[40505E]
00401105   .  A4            MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
00401106   .  884C24 16     MOV BYTE PTR SS:[ESP+16],CL
0040110A   .  B9 05000000   MOV ECX,5
0040110F   .  BE 38504000   MOV ESI,CrackMe0.00405038                        ;  序列号错误,再来一次!
00401114   .  8D7C24 30     LEA EDI,DWORD PTR SS:[ESP+30]
00401118   .  F3:A5         REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
0040111A   .  894424 08     MOV DWORD PTR SS:[ESP+8],EAX
0040111E   .  66:A1 5C50400>MOV AX,WORD PTR DS:[40505C]
00401124   .  66:A5         MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]
00401126   .  66:894424 14  MOV WORD PTR SS:[ESP+14],AX
0040112B   .  8B8424 040100>MOV EAX,DWORD PTR SS:[ESP+104]

看这些代码几乎都是MOV,我不知道是什么意思,但不像是算法分析。如果你不放心的话,就在这里设断运行看看,设断后按多次F9都没有出来输入信息的窗口,决定放弃!

重新载入。既然有消息提示,不妨BP MessageBoxA;结果没断下来,可能使用了别的函数,我不知道用的什么,试下别的办法先!

重新载入。既然有输入信息的对话框,我们就 BP GetDlgItemTextA;
F9,输入注册信息:
HillCat
7123456
确定,断在这里:
77E04CC5 >  55              PUSH EBP
77E04CC6    8BEC            MOV EBP,ESP
77E04CC8    FF75 0C         PUSH DWORD PTR SS:[EBP+C]
77E04CCB    FF75 08         PUSH DWORD PTR SS:[EBP+8]
77E04CCE    E8 201CFFFF     CALL USER32.GetDlgItem
77E04CD3    85C0            TEST EAX,EAX
77E04CD5    0F84 8F880300   JE USER32.77E3D56A
77E04CDB    FF75 14         PUSH DWORD PTR SS:[EBP+14]
77E04CDE    FF75 10         PUSH DWORD PTR SS:[EBP+10]
77E04CE1    50              PUSH EAX
77E04CE2    E8 8323FFFF     CALL USER32.GetWindowTextA
77E04CE7    5D              POP EBP
77E04CE8    C2 1000         RETN 10

看OD标题,是模块-USER32,这不是我们要分析的,
于是alt+F9,返回到程序空间,OD标题显示模块-CrackMe016:
======================================================
004011AE   .  6A 51         PUSH 51                                          ; /Count = 51 (81.)
004011B0   .  50            PUSH EAX                                         ; |Buffer
004011B1   .  6A 6E         PUSH 6E                                          ; |ControlID = 6E (110.)
004011B3   .  56            PUSH ESI                                         ; |hWnd
004011B4   .  FFD7          CALL EDI                                         ; \GetDlgItemTextA
004011B6   .  8D8C24 9C0000>LEA ECX,DWORD PTR SS:[ESP+9C]
004011BD   .  6A 65         PUSH 65                                          ; /Count = 65 (101.)
004011BF   .  51            PUSH ECX                                         ; |Buffer
004011C0   .  68 E8030000   PUSH 3E8                                         ; |ControlID = 3E8 (1000.)
004011C5   .  56            PUSH ESI                                         ; |hWnd
004011C6   .  8BD8          MOV EBX,EAX                                      ; |
004011C8   .  FFD7          CALL EDI                                         ; \GetDlgItemTextA
004011CA   .  8A4424 4C     MOV AL,BYTE PTR SS:[ESP+4C]
004011CE   .  84C0          TEST AL,AL
004011D0   .  74 76         JE SHORT CrackMe0.00401248
004011D2   .  83FB 05       CMP EBX,5
004011D5   .  7C 71         JL SHORT CrackMe0.00401248
004011D7   .  8D5424 4C     LEA EDX,DWORD PTR SS:[ESP+4C]

看看这里的代码,F8向下粗略走,可以看到输入的注册信息,看来这里应该是我们要找的地方。
好,下面我们就refresh(什么意思?自己想^_^)。

重新载入,在函数开头F2下断,F9,输入注册信息,断下:
=================================================================
004011AE   .  6A 51         PUSH 51                                  ; /Count = 51 (81.)
004011B0   .  50            PUSH EAX                                 ; |Buffer
004011B1   .  6A 6E         PUSH 6E                                  ; |ControlID = 6E (110.)
004011B3   .  56            PUSH ESI                                 ; |hWnd
004011B4   .  FFD7          CALL EDI                                 ; \GetDlgItemTextA
004011B6   .  8D8C24 9C0000>LEA ECX,DWORD PTR SS:[ESP+9C]
004011BD   .  6A 65         PUSH 65                                  ; /Count = 65 (101.)
004011BF   .  51            PUSH ECX                                 ; |Buffer
004011C0   .  68 E8030000   PUSH 3E8                                 ; |ControlID = 3E8 (1000.)
004011C5   .  56            PUSH ESI                                 ; |hWnd
004011C6   .  8BD8          MOV EBX,EAX                              ; |
004011C8   .  FFD7          CALL EDI                                 ; \GetDlgItemTextA
004011CA   .  8A4424 4C     MOV AL,BYTE PTR SS:[ESP+4C]
004011CE   .  84C0          TEST AL,AL
004011D0   .  74 76         JE SHORT CrackMe0.00401248               ;  //判断用户名是否为空
004011D2   .  83FB 05       CMP EBX,5
004011D5   .  7C 71         JL SHORT CrackMe0.00401248               ;  //用户名位数不能小于5
004011D7   .  8D5424 4C     LEA EDX,DWORD PTR SS:[ESP+4C]            ;  //用户名地址入EDX
004011DB   .  53            PUSH EBX                                 ;  //EBX保存着用户名位数
004011DC   .  8D8424 A00000>LEA EAX,DWORD PTR SS:[ESP+A0]            ;  //输入的假码地址入EAX
004011E3   .  52            PUSH EDX                                 ;  //用户名入栈
004011E4   .  50            PUSH EAX                                 ;  //输入的假码入栈
004011E5   .  E8 56010000   CALL CrackMe0.00401340                   ;  //算法CALL,F7跟进!转Lable1

(为什么要跟入这里?前面是push输入信息,后面是进行比较判断,这里应该是信息处理的地方哈。如果你看不出来,你就F7进去再F8看看,事实证明这个CALL就是计算注册码的。)

004011EA   .  8B3D BC404000 MOV EDI,DWORD PTR DS:[<&USER32.GetDlgIte>;  USER32.GetDlgItem
004011F0   .  83C4 0C       ADD ESP,0C
004011F3   .  85C0          TEST EAX,EAX                             ;  //标志位判断是否为0
004011F5   .  74 37         JE SHORT CrackMe0.0040122E               ;  //跳向出错,爆破的话就NOP掉这里!
004011F7   .  8D4C24 0C     LEA ECX,DWORD PTR SS:[ESP+C]
004011FB   .  51            PUSH ECX                                 ; |String2
004011FC   .  68 E4544000   PUSH CrackMe0.004054E4                   ; |String1 = CrackMe0.004054E4
00401201   .  FF15 60404000 CALL DWORD PTR DS:[<&KERNEL32.lstrcpyA>] ; \lstrcpyA
00401207   .  6A 00         PUSH 0                                   ; /Enable = FALSE
00401209   .  6A 6E         PUSH 6E                                  ; |/ControlID = 6E (110.)
0040120B   .  56            PUSH ESI                                 ; ||hWnd
0040120C   .  FFD7          CALL EDI                                 ; |\GetDlgItem
0040120E   .  8B1D A4404000 MOV EBX,DWORD PTR DS:[<&USER32.EnableWin>; |USER32.EnableWindow
00401214   .  50            PUSH EAX                                 ; |hWnd
00401215   .  FFD3          CALL EBX                                 ; \EnableWindow
00401217   .  6A 00         PUSH 0                                   ; /Enable = FALSE
00401219   .  68 E8030000   PUSH 3E8                                 ; |/ControlID = 3E8 (1000.)
0040121E   .  56            PUSH ESI                                 ; ||hWnd
0040121F   .  FFD7          CALL EDI                                 ; |\GetDlgItem
00401221   .  50            PUSH EAX                                 ; |hWnd
00401222   .  FFD3          CALL EBX                                 ; \EnableWindow
00401224   .  68 E8030000   PUSH 3E8                                 ; /ControlID = 3E8 (1000.)
00401229   .  56            PUSH ESI                                 ; |hWnd
0040122A   .  FFD7          CALL EDI                                 ; \GetDlgItem
0040122C   .  EB 33         JMP SHORT CrackMe0.00401261                        //JMP跳过错误
0040122E   >  8D5424 34     LEA EDX,DWORD PTR SS:[ESP+34]
00401232   .  52            PUSH EDX                                 ; //String2 = "序列号错误,再来一次!"
00401233   .  68 E4544000   PUSH CrackMe0.004054E4                   ; |String1 = CrackMe0.004054E4
00401238   .  FF15 60404000 CALL DWORD PTR DS:[<&KERNEL32.lstrcpyA>] ; \lstrcpyA
0040123E   .  68 E8030000   PUSH 3E8
00401243   .  56            PUSH ESI
00401244   .  FFD7          CALL EDI
00401246   .  EB 19         JMP SHORT CrackMe0.00401261
00401248   >  8D4424 1C     LEA EAX,DWORD PTR SS:[ESP+1C]            ;  //“你输入字符要大于四个”
0040124C   .  50            PUSH EAX                                 ; /String2
0040124D   .  68 E4544000   PUSH CrackMe0.004054E4                   ; |String1 = CrackMe0.004054E4
00401252   .  FF15 60404000 CALL DWORD PTR DS:[<&KERNEL32.lstrcpyA>] ; \lstrcpyA
00401258   .  6A 6E         PUSH 6E                                  ; /ControlID = 6E (110.)
0040125A   .  56            PUSH ESI                                 ; |hWnd
0040125B   .  FF15 BC404000 CALL DWORD PTR DS:[<&USER32.GetDlgItem>] ; \GetDlgItem
00401261   >  50            PUSH EAX                                 ; /hWnd
00401262   .  FF15 A8404000 CALL DWORD PTR DS:[<&USER32.SetFocus>]   ; \SetFocus
00401268   .  6A 00         PUSH 0                                   ; /BeepType = MB_OK
0040126A   .  FF15 AC404000 CALL DWORD PTR DS:[<&USER32.MessageBeep>>; \MessageBeep
00401270   .  8B0D E0544000 MOV ECX,DWORD PTR DS:[4054E0]            ;  CrackMe0.00400000
00401276   .  6A 00         PUSH 0                                   ; /lParam = NULL
00401278   .  68 60104000   PUSH CrackMe0.00401060                   ; |DlgProc = CrackMe0.00401060
0040127D   .  56            PUSH ESI                                 ; |hOwner
0040127E   .  6A 79         PUSH 79                                  ; |pTemplate = 79
00401280   .  51            PUSH ECX                                 ; |hInst => 00400000
00401281   .  FF15 C8404000 CALL DWORD PTR DS:[<&USER32.DialogBoxPar>; //“恭喜你!成功!”
00401287   .  5B            POP EBX
00401288   .  5F            POP EDI
00401289   .  33C0          XOR EAX,EAX
0040128B   .  5E            POP ESI
0040128C   .  81C4 F4000000 ADD ESP,0F4
00401292   .  C2 1000       RETN 10

上面004011E5处的CALL来到这里:
Lable1
==================================================
00401340  /$  55            PUSH EBP
00401341  |.  8B6C24 0C     MOV EBP,DWORD PTR SS:[ESP+C]             ;  //用户名入EBP
00401345  |.  56            PUSH ESI
00401346  |.  57            PUSH EDI
00401347  |.  8B7C24 18     MOV EDI,DWORD PTR SS:[ESP+18]            ;  //用户名位数入EDI
0040134B  |.  B9 03000000   MOV ECX,3
00401350  |.  33F6          XOR ESI,ESI
00401352  |.  33C0          XOR EAX,EAX
00401354  |.  3BF9          CMP EDI,ECX                              ;  //比较用户名位数是否大于3
00401356  |.  7E 21         JLE SHORT CrackMe0.00401379              ;  //小于等于3就跳
00401358  |.  53            PUSH EBX
00401359  |>  83F8 07       /CMP EAX,7                               ;  //EAX作计数器
0040135C  |.  7E 02         |JLE SHORT CrackMe0.00401360             ;  //循环条件判断
0040135E  |.  33C0          |XOR EAX,EAX                                //当EAX大于7时清零
00401360  |>  33D2          |XOR EDX,EDX                             ;  //EDX清空
00401362  |.  33DB          |XOR EBX,EBX                             ;  //EBX清空
00401364  |.  8A1429        |MOV DL,BYTE PTR DS:[ECX+EBP]            ;  //DL<=从用户名第4位起取字
00401367  |.  8A98 30504000 |MOV BL,BYTE PTR DS:[EAX+405030]         ;  //BL<=从405030处开始取字
0040136D  |.  0FAFD3        |IMUL EDX,EBX                            ;  //EDX=EDX*EBX
00401370  |.  03F2          |ADD ESI,EDX                             ;  //ESI=ESI+EDX
00401372  |.  41            |INC ECX                                 ;  //ECX++
00401373  |.  40            |INC EAX                                 ;  //EAX++
00401374  |.  3BCF          |CMP ECX,EDI                             ;  //循环条件
00401376  |.^ 7C E1         \JL SHORT CrackMe0.00401359                        //直到取完用户名最后一位
00401378  |.  5B            POP EBX
00401379  |>  56            PUSH ESI                                 ; //ESI保存着上面的计算结果
0040137A  |.  68 78504000   PUSH CrackMe0.00405078                   ; |Format = "%ld"
0040137F  |.  55            PUSH EBP                                 ; //EBP入栈
00401380  |.  FF15 9C404000 CALL DWORD PTR DS:[<&USER32.wsprintfA>]  ; //这个Call的作用是把结果转成十进制,以字符输出
00401386  |.  8B4424 1C     MOV EAX,DWORD PTR SS:[ESP+1C]            ; //输入的假码入EAX
0040138A  |.  83C4 0C       ADD ESP,0C
0040138D  |.  55            PUSH EBP                                 ; //String2="注册码"
0040138E  |.  50            PUSH EAX                                 ; //String1="输入码"
0040138F  |.  FF15 04404000 CALL DWORD PTR DS:[<&KERNEL32.lstrcmpA>] ; //lstrcmpA,字符串比较
00401395  |.  F7D8          NEG EAX
00401397  |.  1BC0          SBB EAX,EAX
00401399  |.  5F            POP EDI
0040139A  |.  5E            POP ESI
0040139B  |.  40            INC EAX
0040139C  |.  5D            POP EBP
0040139D  \.  C3            RETN                                     ;  //返回到004011EA

上面的代码,跟踪到下面的语句时看到注册码明码:
0040138D  |.  55            PUSH EBP                                 ; //String2="注册码"

总结:
========================================
该注册码计算方法是:
从用户名第四位起,逐位与一固定字符串对应位相乘,将乘积求和。
F8到相应语句时(00401367),查看转存,在命令行 d 405030,
看到固定的数列(作为乘数)为:0C 0A 13 09 0C 0B 0A
取7位,如果该数列对应于用户名的位数不够,就返回到第一位循环。

下面来看看我的用户名计算过程:

输入的用户名:HillCat,从第四位取,即lCat,对应ASCII值为:6C 43 61 74
与固定的数列0C 0A 13 09 0C 0B 0A对位相乘,再求和,也就是:

6C 43 61 74
0C 0A 13 09 0C 0B 0A

ESI=6C*0C+43*0A+61*13+74*09=12F5

得到的结果12F5转换为十进制是4853,即为注册码。

附注:
其实,因为是VC++的程序,直接Ctrl+N查找lstrcmpA函数就可以到达Lable1下断点分析了。
(要是每个C++的程序都这么简单就好了)

注册机:
=======================================
string str1=textBox1.Text;
str1=str1.SubString(3);
char[] arr1=str1.ToCharArray();
int[] arr0={12,10,19,9,12,11,10};
int sum=0;
for(int i=0;i<arr1.Length;i++)
{
   int flag=i%7;
   sum=arr1[i]*arr0[flag];
}
textBox2.Text=sum.ToString();
附件:crackme016.rar 附件:crackme016.rar

[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞7
打赏
分享
最新回复 (12)
雪    币: 32401
活跃值: (18845)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 8 2005-12-16 15:38
2
0
最初由 HillCat 发布
附注:
其实,因为是VC++的程序,直接Ctrl+N查找lstrcmpA函数就可以到达Lable1下断点分析了。
(要是每个C++的程序都这么简单就好了)

文章条理很好,辛苦了。
由于这个是书中第一个动态跟踪实例,为了降低难度特意选了lstrcmpA这个函数来比较字符串。
雪    币: 242
活跃值: (163)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
林海雪原 6 2005-12-16 16:13
3
0
的确是入门的好文
雪    币: 212
活跃值: (40)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
starluck 1 2005-12-16 17:00
4
0
不错不错!
我缺少的就是楼主的那种写东西的毅力
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xfreelee 2005-12-16 17:04
5
0
仔细的看一下,想入门
雪    币: 2256
活跃值: (941)
能力值: (RANK:2210 )
在线值:
发帖
回帖
粉丝
逍遥风 55 2005-12-16 19:42
6
0
NAME  lovewxt
CODE  5726
谢谢~楼主的破文呀~受益匪浅
雪    币: 100
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
浪费吧 2005-12-16 20:23
7
0
好好啃一啃
雪    币: 245
活跃值: (55)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
matali 2005-12-16 21:34
8
0
是不是快出精华了,牛人才露脸呀?
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
robertbg 2005-12-17 13:23
9
0
不错,楼住!!!!!!
雪    币: 214
活跃值: (15)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
ljy3282393 1 2005-12-17 20:07
10
0
支持一下楼主!
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jjwangjun 2005-12-20 14:30
11
0
学习中,
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
msfm 2006-10-24 14:41
12
0
begin
    table[0]:=12;     //查表计算
    table[1]:=10;
    table[2]:=19;
    table[3]:=9;
    table[4]:=12;
    table[5]:=11;
    table[6]:=10;
    table[7]:=8;
    k:=0;
    sum:=0;
    SendMessage(useredit,WM_GETTEXT,254,integer(sname)) ;
    str:=StrPas(sname);
  for i:=1 to Length(str)-3 do
    begin
      j:=ord(str[i+3])* table[k];
      k:=k+1;
      k:=k mod 8;
      sum:=sum+j;
    end;
    str:=inttostr(sum);
    SendMessage(passwordedit,WM_SETTEXT,0,integer(str));
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
叶孓落落 2006-10-28 12:08
13
0
游客
登录 | 注册 方可回帖
返回