首页
社区
课程
招聘
[原创][看雪读书月][学习心得]一个陷阱crackme分析
发表于: 2008-8-2 18:22 7330

[原创][看雪读书月][学习心得]一个陷阱crackme分析

2008-8-2 18:22
7330

【文章标题】:【原创】 【看雪读书月】【学习心得】一个陷阱crackme分析
【文章作者】: eanson
【软件名称】: crackme.exe
【软件大小】: 16KB
【下载地址】: 附件上传
【加壳方式】: 无壳
【保护方式】: 无壳
【编写语言】: MASM32 / TASM32
【使用工具】: OllyDbg 1.10, PEiD 0.94
【操作平台】: win2k3
【软件介绍】: 不知哪位大侠写的crackme,我拿来学习。
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!

先上PEID查壳,显示程序无壳,编写程序为MASM32 / TASM32,难怪程序这么短小精悍。不过陷阱在后面,呵呵。
运行程序,随便输入序列号,提示it's not so easy boy. keep tryin...
OK,我知道没那么容易呵呵,根据字符串参考搜索到下面这段:

004010AB   .  E8 6C000000   CALL crackme.0040111C                    ;  F7跟进
004010B0   .  0BC0          OR EAX,EAX
004010B2      75 14         JNZ SHORT crackme.004010C8               ;  判断EAX是否为0,不为0则跳
004010B4   .  68 33324000   PUSH crackme.00403233                    ;  right serial
004010B9   .  68 E8314000   PUSH crackme.004031E8                    ;  you've done it. write a tutorial and send it to fornix@fusionrulez.cjb.net
004010BE   .  FF75 08       PUSH DWORD PTR SS:[EBP+8]                ;  
004010C1   .  E8 A8000000   CALL crackme.0040116E                    ;  
004010C6   .  EB 12         JMP SHORT crackme.004010DA
004010C8   >  68 DB314000   PUSH crackme.004031DB                    ;  wrong serial
004010CD   .  68 B7314000   PUSH crackme.004031B7                    ;  it's not so easy boy. keep tryin...

F7跟进来到下面:

0040111C   $  55            PUSH EBP
0040111D   .  8BEC          MOV EBP,ESP
0040111F   .  E8 1D010000   CALL crackme.00401241
00401124   .  33C0          XOR EAX,EAX
00401126   .  0BC0          OR EAX,EAX
00401128   .  74 09         JE SHORT crackme.00401133
0040112A   .  7A 9B         JPE SHORT crackme.004010C7
0040112C   .  03B3 F3D3CBCB ADD ESI,DWORD PTR DS:[EBX+CBCBD3F3]
00401132      63            DB 63                                    ;  CHAR 'c'
00401133   .  6A 14         PUSH 14                                  ; /Count = 14 (20.)
00401135   .  68 84324000   PUSH crackme.00403284                    ; |Buffer = crackme.00403284
0040113A   .  68 EA030000   PUSH 3EA                                 ; |ControlID = 3EA (1002.)
0040113F   .  FF75 08       PUSH DWORD PTR SS:[EBP+8]                ; |hWnd = 06B80674 ('Fornix's Crackme 2',class='#32770')
00401142   .  E8 77010000   CALL <JMP.&user32.GetDlgItemTextA>       ; \GetDlgItemTextA
00401147   .  68 84324000   PUSH crackme.00403284
0040114C   .  E8 D0000000   CALL crackme.00401221                    ;  F7跟进

在上面代码看到GetDlgItemTextA函数,知道这段代码大体上是得到输入的Serial,所以直接F8,在最后一行F7跟进:

F7跟进来到下面:

00401221  /$  55            PUSH EBP
00401222  |.  8BEC          MOV EBP,ESP
00401224  |.  8B75 08       MOV ESI,DWORD PTR SS:[EBP+8]             ;  输入的Serial出现,偏移地址为00403284
00401227  |>  803E 00       /CMP BYTE PTR DS:[ESI],0                 ;  判断Serial是否转换完
0040122A  |.  74 0C         |JE SHORT crackme.00401238               ;  完了则跳
0040122C  |.  8A06          |MOV AL,BYTE PTR DS:[ESI]
0040122E  |.  34 15         |XOR AL,15                               ;  serial每一个字符与15异或
00401230  |.  C0C8 05       |ROR AL,5                                ;  再循环右移5位
00401233  |.  8806          |MOV BYTE PTR DS:[ESI],AL                ;  存回原来位置
00401235  |.  46            |INC ESI                                 ;  偏移地址减1,移到下一个字符
00401236  |.^ EB EF         \JMP SHORT crackme.00401227

然后一路F8,来到下面:

00401151   .  BF 20114000   MOV EDI,crackme.00401120                 ;  把地址00401120赋给EDI
00401156   .  83C7 0B       ADD EDI,0B                               ;  EDI变为0040112B
00401159   .  BE 84324000   MOV ESI,crackme.00403284                 ;  把地址00403284赋给ESI
0040115E   .  B9 09000000   MOV ECX,9                                ;  计数器置9
00401163   >  49            DEC ECX
00401164   .  AC            LODS BYTE PTR DS:[ESI]                   ;  循环比较地址00403284中的数据
00401165   .  AE            SCAS BYTE PTR ES:[EDI]                   ;  是否与地址0040112B中的数据相同
00401166   .^ 74 FB         JE SHORT crackme.00401163                ;  相同则跳回去接着比较下一位
00401168   .  8BC1          MOV EAX,ECX                              ;  不相同则把计数器值赋给EAX
0040116A   .  C9            LEAVE
0040116B   .  C2 0400       RETN 4                                   ;  返回

返回以后来到:

004010B0   .  0BC0          OR EAX,EAX                               ;  判断EAX是否为0,也就判断两个地址中的数据是否一样
004010B2      75 14         JNZ SHORT crackme.004010C8               ;  判断EAX是否为0,不为0则跳
004010B4   .  68 33324000   PUSH crackme.00403233                    ;  right serial
004010B9   .  68 E8314000   PUSH crackme.004031E8                    ;  you've done it. write a tutorial and send it to fornix@fusionrulez.cjb.net
004010BE   .  FF75 08       PUSH DWORD PTR SS:[EBP+8]
004010C1   .  E8 A8000000   CALL crackme.0040116E                    ;  
004010C6   .  EB 12         JMP SHORT crackme.004010DA
004010C8   >  68 DB314000   PUSH crackme.004031DB                    ;  wrong serial
004010CD   .  68 B7314000   PUSH crackme.004031B7                    ;  it's not so easy boy. keep tryin...

看看起来程序到这基本上清楚了,无非就是把serial每一个字符XOR 15,然后再ROR 5,结果等于0040112B中的数据就行了。马上写算法,得到serial是"fuckolly"(怎么看起来不妙..),运行原exe程序,输入"fuckolly",错误!!怎么会这样...继续跟下去吧:
再看看上面的代码,在正确提示与错误提示之间还有一个CALL crackme.0040116E,跟进去:

0040116E   $  55            PUSH EBP
0040116F   .  8BEC          MOV EBP,ESP
00401171   .  68 68324000   PUSH crackme.00403268                    ; /lParam = 403268
00401176   .  6A 14         PUSH 14                                  ; |wParam = 14
00401178   .  6A 0D         PUSH 0D                                  ; |Message = WM_GETTEXT
0040117A   .  68 EA030000   PUSH 3EA                                 ; |ControlID = 3EA (1002.)
0040117F   .  FF75 08       PUSH DWORD PTR SS:[EBP+8]                ; |hWnd = 06B90674 ('Fornix's Crackme 2',class='#32770')
00401182   .  E8 49010000   CALL <JMP.&user32.SendDlgItemMessageA>   ; \SendDlgItemMessageA
00401187   .  EB 08         JMP SHORT crackme.00401191
00401189   .  91            XCHG EAX,ECX
0040118A   .  71 39         JNO SHORT crackme.004011C5
0040118C   .  A9 41112103   TEST EAX,3211141
00401191   >  68 68324000   PUSH crackme.00403268
00401196   .  E8 66000000   CALL crackme.00401201                    ;  F7跟进

在上面代码看到SendDlgItemMessageA函数,知道这段代码大体上是得到显示消息对话框,所以直接F8,在最后一行F7跟进:

00401204  |.  8B75 08       MOV ESI,DWORD PTR SS:[EBP+8]             ;  输入的Serial出现
00401207  |>  803E 00       /CMP BYTE PTR DS:[ESI],0                 ;  判断Serial是否转换完
0040120A  |.  74 0C         |JE SHORT crackme.00401218               ;  完了则跳
0040120C  |.  8A06          |MOV AL,BYTE PTR DS:[ESI]
0040120E  |.  34 41         |XOR AL,41                               ;  serial每一个字符与41异或
00401210  |.  C0C0 03       |ROL AL,3                                ;  再循环左移3位
00401213  |.  8806          |MOV BYTE PTR DS:[ESI],AL                ;  存回原来位置
00401215  |.  46            |INC ESI                                 ;  偏移地址减1,移到下一个字符
00401216  |.^ EB EF         \JMP SHORT crackme.00401207

这一段代码似曾相识吧,没错,就是转换输入的Serial。一路F8,直到返回,来到下面:

0040119B   .  B8 05000000   MOV EAX,5
004011A0   .  F625 40324000 MUL BYTE PTR DS:[403240]
004011A6   .  BE 68324000   MOV ESI,crackme.00403268                 ;  把地址00403268赋给ESI
004011AB   .  BF 75114000   MOV EDI,crackme.00401175                 ;  把地址00401175赋给EDI
004011B0   .  03F8          ADD EDI,EAX
004011B2   .  B9 09000000   MOV ECX,9                                ;  计数器置9
004011B7   >  49            DEC ECX
004011B8   .  AC            LODS BYTE PTR DS:[ESI]                   ;  循环比较地址00403284中的数据
004011B9   .  AE            SCAS BYTE PTR ES:[EDI]                   ;  是否与地址00401175中的数据相同
004011BA   .^ 74 FB         JE SHORT crackme.004011B7                ;  相同则跳回去接着比较下一位
004011BC   .  0BC9          OR ECX,ECX                               ;  判断ECX是否为0
004011BE      74 21         JE SHORT crackme.004011E1                ;  不为0则跳,

分析到这,后面的代码基本上没有Call,应该这下程序应该清楚了吧。根据程序的逆过程,计算了一下注册码,竟然不是可输入的ASCII字符!用OllyDbg再跟一次,勉强用KeyGen生成的乱码复制到exe对话框中,点check,到了最后一步循环比较时,会发生ECX已经跳到0时,由于越界的字符比较依然相同,ECX会继续DEC,导致紧接着发生跳转,依然提示Serial错误!!这是怎么回事呢?难道程序有问题?这几乎是不可能的。。但是这程序用汇编写的,总共就那么长,代码差不多都看到了,会是哪里有问题呢?

其实在跟踪的时候,有一段程序一直很可疑,一开始我没太想清楚这段程序的作用(菜鸟就是菜鸟),只知道可能是用来效验程序是否修改。后来我就重点盯着这段程序看了,注释是后来才加的:

00401241  /$  8D3D 00104000    LEA EDI,DWORD PTR DS:[<模块入口点>]      ;  程序入口点
00401247  |.  B9 97020000      MOV ECX,297                              ;  297正好是用户代码长度
0040124C  |.  E8 45000000      CALL crackme.00401296                    ;  把0CC放到AL里
00401251      F2:AE            REPNE SCAS BYTE PTR ES:[EDI]             ;  循环搜索用户代码中是否有0CC
00401253  |.  85C9             TEST ECX,ECX                             ;  判断ECX是否为0,也就是是否搜索完毕
00401255  |.  75 3E            JNZ SHORT crackme.00401295               ;  不为0则跳
00401257  |.  8D05 9A124000    LEA EAX,DWORD PTR DS:[40129A]
0040125D  |.  A3 7D324000      MOV DWORD PTR DS:[40327D],EAX
00401262  |>  A1 7D324000      /MOV EAX,DWORD PTR DS:[40327D]
00401267  |.  8B15 7D324000    |MOV EDX,DWORD PTR DS:[40327D]
0040126D  |.  833A 00          |CMP DWORD PTR DS:[EDX],0
00401270  |.  74 14            |JE SHORT crackme.00401286
00401272  |.  8BC8             |MOV ECX,EAX
00401274  |.  E8 1D000000      |CALL crackme.00401296                   ;  又是0CC
00401279  |.  3801             |CMP BYTE PTR DS:[ECX],AL
0040127B  |.  74 18            |JE SHORT crackme.00401295
0040127D  |.  8305 7D324000 06 |ADD DWORD PTR DS:[40327D],6
00401284  |.^ EB DC            \JMP SHORT crackme.00401262              ;  循环检查是否有0CC
00401286  |>  8A1D 40324000    MOV BL,BYTE PTR DS:[403240]              ;  [403240]中的值放入BL
0040128C  |.  FEC3             INC BL                                   ;  BL加1
0040128E  |.  881D 40324000    MOV BYTE PTR DS:[403240],BL              ;  再存入[403240],其实就是403240中的值加1
00401294  |.  C3               RETN
00401295  \>  C3               RETN
00401296  /$  B0 CC            MOV AL,0CC
00401298  \.  C3               RETN

我在这段程序上调了很多次。开始我一直没搞懂这段代码到底想干嘛,后来我发现F7调试这句代码REPNE SCAS BYTE PTR ES:[EDI] 时,EDI跳到1EB时,DS:[EDI]=[004010AC]=06C,AL=0CC,然后再按F7就跳倒下一行了!!明明不等怎么会跳呢??再一看我在004010AC下了中断!猛地想起OllyDbg调试时之所以可以下断点,实际上是用INT3中断替换下断点的字符!再一查,果然就是0CC(菜鸟就是菜鸟,这时才发现,唉...)
其实在前面跟踪的时候,还有一处疑点:

0040119B   .  B8 05000000   MOV EAX,5
004011A0   .  F625 40324000 MUL BYTE PTR DS:[403240]

在调试时[403240]中的值始终为0,这样就没什么意义了。在看看反调试那段代码,如果将00401255处的代码替换为空指令,将00401270处的JE修改为JMP,便会一路执行下来,并使([403240])加1。好的,这样应该差不多了。载入exe用ollydbg一跟,可以看到([403240])最终的值是4,也就是比较的地址将是:00401175+4*5=00401189。根据前面的分析,只要把00401189处的值,以字节为单位,先ROR 3,再XOR 41就可以得到Serial了。Serial固定,就不写注册机了。终于搞定,有点累,但收获很大。

总结:0CC,可不能大意了。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (2)
雪    币: 47147
活跃值: (20450)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
欢迎你的参加。
有点可惜,和CrackMe的这篇重复了:

标 题: fornix'2nd crackme,利用INT3混乱算法
作 者: laomms
时 间: 2006-08-27 07:52
附 件: 2nd crackme.rar
链 接: http://bbs.pediy.com/showthread.php?threadid=31093
2008-8-2 18:52
0
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
郁闷
多谢老大提醒呵呵。
2008-8-2 19:00
0
游客
登录 | 注册 方可回帖
返回
//