• 标 题:对Crunch v1.1加壳程序的手动脱壳及反跟踪代码的一点分析 (15千字)
  • 作 者:ljttt
  • 时 间:2000-10-2 19:01:05
  • 链 接:http://bbs.pediy.com

对Crunch v1.1加壳程序的手动脱壳及反跟踪代码的一点分析

【声明】
我写文章以交流为主,希望大家在转载时能保持文章的完整性。

【前言】
由于我现在还没有破解版的Crunch v1.1,所以就拿未注册版的Crunch来测试。好象未注册版的Crunch加壳的程序的名字有限制,其他的我就不清楚了。由于我DOWN的这个文件是根据看雪论坛的一个贴子提供的地址下载的。连帮助文件也没有。:-( 
而且目前我手头也没有注册版的Crunch,所以只好以未注册版的Crunch加壳Notepad.EXE程序来进行说明。

样例文件:    bitarts_evaluation.exe    (把Notepad.EXE加壳后的文件)
            (未注册版Crunch加壳的程序文件名都是这个,不过可以在跟踪中发现文件名比较的地方)
加壳方式:    未注册版Crunch v1.1加壳
目标:        手动脱壳
作者:        ljttt
写作日期:    2000-10-02

1、这次我们用ProcDump提供的Bhrama Server的功能来手动脱壳,这样可以简化一点手动脱壳的麻烦。所以首先我们启动ProcDump,选择Option/Rebuild new import table。点击Bhrama Server,这时就启动了服务器,将等待客户命令。

2、Ctrl-D进入SoftICE,设断点
bpint 3

3、启动PEditor,打开bitarts_evaluation.exe,点击break'n'enter。单击Run。这时将中断进入SoftICE。可以看到如下提示。
?You have to enter "eb eip 55" before you continue ! ? 

为了继续跟踪,我们根据提示下指令
eb eip 55

现在将显示如下:
015F:0040D000  55                  PUSH      EBP        <--这就是我们修改后的结果
015F:0040D001  E800000000          CALL      0040D006
015F:0040D006  5D                  POP      EBP

4、继续跟踪到如下处:
015F:0040D0AC  FF95B1320000        CALL      [EBP+000032B1]
015F:0040D0B2  83C408              ADD      ESP,08
015F:0040D0B5  8BB5AD320000        MOV      ESI,[EBP+000032AD]
015F:0040D0BB  BFC6000000          MOV      EDI,000000C6
015F:0040D0C0  03FD                ADD      EDI,EBP
015F:0040D0C2  8BC8                MOV      ECX,EAX
015F:0040D0C4  F3A4                REPZ MOVSB                <--在此处停下,此指令执行后,以下的指令将改变,这就是SMC的技巧,以后还有多处,就不一一列举了。
015F:0040D0C6  8B07                MOV      EAX,[EDI]    <--现在我们看到的是加密的代码,只有上一指令执行后才会显示正确的指令代码
015F:0040D0C8  D581                AAD
015F:0040D0CA  C23813              RET      1338

5、在REPZ MOVSB指令执行后我们将看到如下的正确代码:
015F:0040D0C6  8BD5                MOV      EDX,EBP
015F:0040D0C8  81C238130000        ADD      EDX,00001338
015F:0040D0CE  52                  PUSH      EDX
015F:0040D0CF  33C0                XOR      EAX,EAX
015F:0040D0D1  8CD8                MOV      AX,DS
015F:0040D0D3  A804                TEST      AL,04
015F:0040D0D5  7408                JZ        0040D0DF
015F:0040D0D7  B402                MOV      AH,02
015F:0040D0D9  CD1A                INT      1A
015F:0040D0DB  8BC2                MOV      EAX,EDX
015F:0040D0DD  EB02                JMP      0040D0E1
015F:0040D0DF  0F31                RDTSC
015F:0040D0E1  33D2                XOR      EDX,EDX
015F:0040D0E3  69C00D661900        IMUL      EAX,EAX,0019660D
015F:0040D0E9  05CD0D0100          ADD      EAX,00010DCD
015F:0040D0EE  8985383A0000        MOV      [EBP+00003A38],EAX
015F:0040D0F4  BB56340200          MOV      EBX,00023456
015F:0040D0F9  43                  INC      EBX
015F:0040D0FA  F7F3                DIV      EBX
015F:0040D0FC  8BC2                MOV      EAX,EDX
015F:0040D0FE  5A                  POP      EDX
015F:0040D0FF  90                  NOP
015F:0040D100  FFD2                CALL      EDX        <--在此处我们要停下,如果你想了解反跟踪代码,可以进入看看
015F:0040D102  CC                  INT      3
015F:0040D103  CC                  INT      3
015F:0040D104  E80E130000          CALL      0040E417
015F:0040D109  E838100000          CALL      0040E146    <--反跟踪

6、在执行到 CALL EDX 指令处时,我们下指令
r eip eip+E
r esi 10
这样就可以跳过这一段反跟踪的代码。

7、这里我们来了解一下 CALL EDX 中的一段反跟踪代码
015F:0040E369  0F010B              SIDT      FWORD PTR [EBX]    <--取IDTR的内容
015F:0040E36C  8B7302              MOV      ESI,[EBX+02]        <--取IDT表的基地址
015F:0040E36F  83C608              ADD      ESI,08
015F:0040E372  8B1E                MOV      EBX,[ESI]            <--取int 1的低位偏移
015F:0040E374  53                  PUSH      EBX
015F:0040E375  56                  PUSH      ESI
015F:0040E376  83C610              ADD      ESI,10
015F:0040E379  8B36                MOV      ESI,[ESI]            <--取int 3的低位偏移
015F:0040E37B  81E6FFFF0000        AND      ESI,0000FFFF
015F:0040E381  81E3FFFF0000        AND      EBX,0000FFFF
015F:0040E387  2BF3                SUB      ESI,EBX
015F:0040E389  83FE1E              CMP      ESI,1E                <--比较低位偏移
015F:0040E38C  B090                MOV      AL,90
015F:0040E38E  7427                JZ        0040E3B7            <--关键了!
015F:0040E390  8BDD                MOV      EBX,EBP
015F:0040E392  81C311140000        ADD      EBX,00001411
015F:0040E398  0F010B              SIDT      FWORD PTR [EBX]    <--取IDTR的内容
015F:0040E39B  8B7B02              MOV      EDI,[EBX+02]        <--取IDT表的基地址
015F:0040E39E  83C708              ADD      EDI,08
015F:0040E3A1  8B4704              MOV      EAX,[EDI+04]        <--取int 1的高位偏移
015F:0040E3A4  668B07              MOV      AX,[EDI]            <--取int 1的低位偏移
015F:0040E3A7  8038E9              CMP      BYTE PTR [EAX],E9    <--判断int 1中断处理程序第一个字节是否为JMP指令
015F:0040E3AA  B090                MOV      AL,90
015F:0040E3AC  7409                JZ        0040E3B7            <--关键了!
015F:0040E3AE  83FE1E              CMP      ESI,1E
015F:0040E3B1  B090                MOV      AL,90
015F:0040E3B3  7402                JZ        0040E3B7            <--关键了!
015F:0040E3B5  045B                ADD      AL,5B

8、这里我们来了解一下 CALL 0040E146 中的反跟踪代码
015F:0040E146  8B8587390000        MOV      EAX,[EBP+00003987]
015F:0040E14C  8B8DC5300000        MOV      ECX,[EBP+000030C5]
015F:0040E152  C1E908              SHR      ECX,08
015F:0040E155  33D2                XOR      EDX,EDX
015F:0040E157  B902000000          MOV      ECX,00000002
015F:0040E15C  F7F1                DIV      ECX
015F:0040E15E  0BD2                OR        EDX,EDX
015F:0040E160  7502                JNZ      0040E164            <--关键了!
015F:0040E162  EB27                JMP      0040E18B
==> 0040E164  8BDD                MOV      EBX,EBP
015F:0040E166  81C38B390000        ADD      EBX,0000398B
015F:0040E16C  0F010B              SIDT      FWORD PTR [EBX]    <--取IDTR的内容
015F:0040E16F  8B7302              MOV      ESI,[EBX+02]        <--取IDT表的基地址
015F:0040E172  83C608              ADD      ESI,08
015F:0040E175  8B1E                MOV      EBX,[ESI]            <--取int 1的低位偏移
015F:0040E177  83C610              ADD      ESI,10
015F:0040E17A  8B36                MOV      ESI,[ESI]            <--取int 3的低位偏移
015F:0040E17C  81E6FFFF0000        AND      ESI,0000FFFF
015F:0040E182  81E3FFFF0000        AND      EBX,0000FFFF
015F:0040E188  2BF3                SUB      ESI,EBX            <--ESI返回低位偏移之差
015F:0040E18A  C3                  RET
015F:0040E18B  BE10000000          MOV      ESI,00000010        <--ESI返回0x10
015F:0040E190  C3                  RET

9、我们继续跟踪来到如下,(这也是由SMC得来的)
015F:0040D1F7  49                  DEC      ECX
015F:0040D1F8  83F900              CMP      ECX,00
015F:0040D1FB  75EB                JNZ      0040D1E8
015F:0040D1FD  59                  POP      ECX
015F:0040D1FE  81E942000000        SUB      ECX,00000042
015F:0040D204  51                  PUSH      ECX
015F:0040D205  E873100000          CALL      0040E27D            <--内有反跟踪代码
015F:0040D20A  33C0                XOR      EAX,EAX
015F:0040D20C  59                  POP      ECX
015F:0040D20D  8AC1                MOV      AL,CL
015F:0040D20F  8BDD                MOV      EBX,EBP
015F:0040D211  81C3A5320000        ADD      EBX,000032A5
015F:0040D217  8BB57D390000        MOV      ESI,[EBP+0000397D]
015F:0040D21D  50                  PUSH      EAX
015F:0040D21E  51                  PUSH      ECX
015F:0040D21F  53                  PUSH      EBX
015F:0040D220  E8D10E0000          CALL      0040E0F6
015F:0040D225  5B                  POP      EBX

10、进入 CALL 0040E27D 我们来看看其中的反跟踪代码。
==> 0040E29D  8BDD                MOV      EBX,EBP
015F:0040E29F  81C311140000        ADD      EBX,00001411
015F:0040E2A5  0F010B              SIDT      FWORD PTR [EBX]    <--取IDTR内容
015F:0040E2A8  8B7B02              MOV      EDI,[EBX+02]        <--取IDT表基地址
015F:0040E2AB  83C708              ADD      EDI,08
015F:0040E2AE  8B4704              MOV      EAX,[EDI+04]        <--取int 1的高位偏移
015F:0040E2B1  668B07              MOV      AX,[EDI]            <--取int 1的低位偏移
015F:0040E2B4  8038E9              CMP      BYTE PTR [EAX],E9    <--判断int 1中断处理程序第一个字节是否为JMP指令
015F:0040E2B7  750B                JNZ      0040E2C4            <--关键了!
015F:0040E2B9  BE1E000000          MOV      ESI,0000001E        <--发现跟踪ESI=1E
015F:0040E2BE  8BDD                MOV      EBX,EBP
015F:0040E2C0  52                  PUSH      EDX
015F:0040E2C1  53                  PUSH      EBX
015F:0040E2C2  EB28                JMP      0040E2EC
015F:0040E2C4  8BDD                MOV      EBX,EBP
015F:0040E2C6  52                  PUSH      EDX
015F:0040E2C7  53                  PUSH      EBX
015F:0040E2C8  81C311140000        ADD      EBX,00001411
015F:0040E2CE  0F010B              SIDT      FWORD PTR [EBX]    <--取IDTR内容
015F:0040E2D1  8B7302              MOV      ESI,[EBX+02]        <--取IDT表基地址
015F:0040E2D4  83C608              ADD      ESI,08
015F:0040E2D7  8B1E                MOV      EBX,[ESI]            <--取int 1的低位偏移
015F:0040E2D9  83C610              ADD      ESI,10
015F:0040E2DC  8B36                MOV      ESI,[ESI]            <--取int 3的低位偏移
015F:0040E2DE  81E6FFFF0000        AND      ESI,0000FFFF
015F:0040E2E4  81E3FFFF0000        AND      EBX,0000FFFF
015F:0040E2EA  2BF3                SUB      ESI,EBX            <--计算低位偏移之差
015F:0040E2EC  8BDE                MOV      EBX,ESI
015F:0040E2EE  8BD3                MOV      EDX,EBX            <--发现跟踪EDX=ESI=1E
015F:0040E2F0  5B                  POP      EBX
015F:0040E2F1  81C3DB110000        ADD      EBX,000011DB        <--以后用不正确的EDX和ESI来SMC自身的代码,你说会发生什么现象!
015F:0040E2F7  33C0                XOR      EAX,EAX
015F:0040E2F9  8A03                MOV      AL,[EBX]
015F:0040E2FB  03C6                ADD      EAX,ESI
015F:0040E2FD  8803                MOV      [EBX],AL
015F:0040E2FF  83C304              ADD      EBX,04
015F:0040E302  8A03                MOV      AL,[EBX]
015F:0040E304  8BF2                MOV      ESI,EDX
015F:0040E306  C1E602              SHL      ESI,02
015F:0040E309  03C6                ADD      EAX,ESI
015F:0040E30B  8803                MOV      [EBX],AL
015F:0040E30D  5A                  POP      EDX
015F:0040E30E  C3                  RET
015F:0040E30F  8BDD                MOV      EBX,EBP
015F:0040E311  BE10000000          MOV      ESI,00000010
015F:0040E316  BA10000000          MOV      EDX,00000010        <--未发现跟踪时ESI=10,EDX=10
015F:0040E31B  81C3DB110000        ADD      EBX,000011DB        <--以下是用SMC的技巧来还原以后的代码
015F:0040E321  33C0                XOR      EAX,EAX
015F:0040E323  8A03                MOV      AL,[EBX]
015F:0040E325  03C6                ADD      EAX,ESI
015F:0040E327  8803                MOV      [EBX],AL
015F:0040E329  83C304              ADD      EBX,04
015F:0040E32C  8A03                MOV      AL,[EBX]
015F:0040E32E  8BF2                MOV      ESI,EDX  
015F:0040E330  C1E602              SHL      ESI,02
015F:0040E333  03C6                ADD      EAX,ESI
015F:0040E335  8803                MOV      [EBX],AL
015F:0040E337  C3                  RET

11、看完反跟踪代码,我们回到主程序继续跟踪到如下代码处
015F:0040D9C2  FFB54C210000        PUSH      DWORD PTR [EBP+0000214C]
015F:0040D9C8  FF956E1E0000        CALL      [EBP+00001E6E]
015F:0040D9CE  83854021000014      ADD      DWORD PTR [EBP+00002140],14
015F:0040D9D5  FF8D94300000        DEC      DWORD PTR [EBP+00003094]
015F:0040D9DB  6683BD9430000000    CMP      WORD PTR [EBP+00003094],00
015F:0040D9E3  0F85FAFDFFFF        JNZ      0040D7E3
015F:0040D9E9  B8BB140000          MOV      EAX,000014BB
015F:0040D9EE  03C5                ADD      EAX,EBP
015F:0040D9F0  FFE0                JMP      EAX            <--此处将跳转到文件名比较的地方
015F:0040D9F2  FFA5AB390000        JMP      [EBP+000039AB]    <--跳到以下代码处
我们分别先在 JMP EAX 和 JMP [EBP+000039AB] 处设下断点。先我们来看看加壳程序是如何进行文件名比较来防止你修改文件名的。

12、在 JMP EAX 跳转后,我们将来到如下代码处
.....之前的代码将调用GetModuleName得到文件名称,以下的代码是分析比较的部分
015F:0040E503  740F                JZ        0040E514
015F:0040E505  8A1E                MOV      BL,[ESI]    <--[ESI]指向文件名称字符串
015F:0040E507  80FB5C              CMP      BL,5C        <--判断是否为"\"字符
015F:0040E50A  7415                JZ        0040E521
015F:0040E50C  80C320              ADD      BL,20        <--转换成小写字母
015F:0040E50F  881E                MOV      [ESI],BL
015F:0040E511  4E                  DEC      ESI
015F:0040E512  EBF1                JMP      0040E505
015F:0040E514  90                  NOP
015F:0040E515  8A1E                MOV      BL,[ESI]
015F:0040E517  80FB5C              CMP      BL,5C
015F:0040E51A  7405                JZ        0040E521
015F:0040E51C  881E                MOV      [ESI],BL
015F:0040E51E  4E                  DEC      ESI
015F:0040E51F  EBF4                JMP      0040E515
015F:0040E521  46                  INC      ESI
015F:0040E522  BF66160000          MOV      EDI,00001666
015F:0040E527  03FD                ADD      EDI,EBP    <--EDI指向字符串"bitarts"
015F:0040E529  33C9                XOR      ECX,ECX
015F:0040E52B  33C0                XOR      EAX,EAX
015F:0040E52D  8A06                MOV      AL,[ESI]
015F:0040E52F  3C00                CMP      AL,00
015F:0040E531  7438                JZ        0040E56B    <--判断字符串是否比较结束
015F:0040E533  83F905              CMP      ECX,05
015F:0040E536  7433                JZ        0040E56B    <--是否匹配完全相同,是则跳走
015F:0040E538  3807                CMP      [EDI],AL    <--判断文件名称是否相等
015F:0040E53A  7505                JNZ      0040E541    <--不等,则跳走
015F:0040E53C  46                  INC      ESI
015F:0040E53D  47                  INC      EDI
015F:0040E53E  41                  INC      ECX
015F:0040E53F                      JMP      0040E52D    <--循环

13、知道了程序如何比较文件名称,按F5继续,将中断在我们设好的断点 JMP [EBP+000039AB] 处。我们继续跟踪到如下代码
015F:00410D99  BBCD150000          MOV      EBX,000015CD
015F:00410D9E  03DD                ADD      EBX,EBP
015F:00410DA0  53                  PUSH      EBX
015F:00410DA1  50                  PUSH      EAX
015F:00410DA2  FF955E1E0000        CALL      [EBP+00001E5E]
015F:00410DA8  FFD0                CALL      EAX
015F:00410DAA  8B8554210000        MOV      EAX,[EBP+00002154]
015F:00410DB0  0185C1300000        ADD      [EBP+000030C1],EAX
015F:00410DB6  FFA59B390000        JMP      [EBP+0000399B]            <--此处跳转到最后的一段代码
015F:00410DBC  C3                  RET

14、在 JMP [EBP+0000399B] 处跳转到如下最后一处代码。这里我们可以DUMP得到完整的文件了。
015F:0040D0C6  61                  POPAD
015F:0040D0C7  5D                  POP      EBP
015F:0040D0C8  8B85C1300000        MOV      EAX,[EBP+000030C1]
015F:0040D0CE  5D                  POP      EBP
015F:0040D0CF  FFE0                JMP      EAX                <--此时,EAX就是OEP。在此处我们DUMP的时候到了
在 JMP EAX 处我们下指令(这里我使用的是ICEDUMP 6.0168的命令使用方法)
/bhrama ProcDump32 - Dumper Server
这时程序将回到WINDOWS,过一段时间,如果成功,将出现文件保存对话框让你选择想保存的文件名。存完将回到SoftICE,好!我们按F5结束工作。

【后记】
应该说以上我介绍的并不是以脱壳为主,而是介绍一些Crunch加壳程序的特征,也希望大家以此能进行更多的分析来相互交流。
如果你按此法脱壳,可能你要花费大把大把的时间。如果你想更快的脱壳,可以参考我写的ProcDump的Script,或者根据其中脱壳的方法在加密代码还原后搜索特征代码,那样来得更快。