In chiusura al mio post di ieri accennavo al fatto che la realizzazione di un virus per una determinata piattaforma non dipende (o dipende relativamente) dalla diffusione di tale piattaforma (in generale e su Internet). A titolo di curiosità pubblico qui ampi stralci di un articolo di Peter Ferrie (Symantec Security Response) apparso su Virus Bulletin di gennaio 2005, che parla di uno stealth virus ingegnoso e ben scritto, creato nel 1986 e che prendeva di mira… il Commodore 64. Il sito Virus Bulletin è un altro che consiglierei di mettere nei bookmark a chi interessa rimanere costantemente aggiornato in materia di malware.
Per chiarezza: uno stealth virus è un virus in parte residente in memoria, che nasconde i cambiamenti che apporta a livello di sistema operativo, strutture di directory e dimensioni di file, facendo credere al programma che effettua la scansione che tutto stia procedendo normalmente. (Definizione presa da questa pagina).
E ora l’articolo:
Normalmente si dice che il primo stealth virus conosciuto che infettava file fosse Frodo, del 1989. E questo è vero, ma solo per quanto concerne la piattaforma IBM PC. La piattaforma Commodore 64 venne infettata tre anni prima da quel che probabilmente fu il primo vero stealth virus che infettasse file di sistema: C64/BHP.A (da non confonderlo con il boot-sector virus che colpiva gli Atari, anch’esso conosciuto con il nome di BHP).
Tutte le descrizioni di BHP pubblicate a quel tempo erano imprecise; alcune di esse davano persino informazioni errate su come funzionava il processo di infezione. Il presente articolo mostrerà le reali operazioni di quel virus.
Come per tutti i programmi per il Commodore 64, BHP iniziava con del codice scritto in Basic. Il codice era composto da un’unica riga, una chiamata di sistema (SYS) al codice assembler, dove risiedeva il resto del virus. A differenza di molti programmi, il codice del virus costruiva l’indirizzo da chiamare dinamicamente. È possibile che sia stato scritto da un programmatore molto attento, ma questa procedura si rivelò superflua perché l’indirizzo non cambiò nelle versioni successive della macchina.
Una volta che il codice assembler otteneva il controllo, si inseriva nel blocco di memoria solitamente occupato dai dispositivi I/O [input/output] quando la ROM veniva allocata in banchi.
A questo punto è necessario descrivere più dettagliatamente parte dell’architettura del Commodore 64.
Il Commodore 64 utilizzava un processore MOS 6510, una versione più recente del chip MOS 6502 presente su molti computer della concorrenza a quei tempi, fra cui la famiglia degli Apple II e gli Atari 400 e 800. Dato che il bus dati del 6502 (e quindi del 6510) era a 16 bit soltanto, l’intervallo di memoria massimo indirizzabile direttamente era di 64 KB. Per aumentare la memoria disponibile venne implementata un’architettura ‘a banchi’, che permetteva di mappare aree diverse di memoria sotto il controllo dell’utente, semplicemente scrivendo il valore appropriato in una determinata porta mappata.
[…] Dato che le tutte le regioni mappate dovevano rientrare nell’intervallo dei 64 KB, un gruppo di intervalli di memoria forniva la base per tutta la memoria in banchi, così da offrire il quantitativo massimo di memoria sempre disponibile. Ciò riduceva di gran lunga la complessità del programma medio. D’altro canto, tuttavia, erano necessari svariati passaggi affinché un programma in esecuzione all’interno di un banco di memoria potesse accedere ai dati contenuti in un altro banco di memoria. Il primo passaggio era quello di inserire codice in una parte di memoria non allocata in banchi ed eseguirlo. In seguito, quel codice doveva togliere il programma dal banco, inserire nel banco i dati richiesti, accedere a quei dati e salvarli, poi togliere i dati dal banco, rimettere nuovamente il programma nel banco, ripristinare i dati e restituire il controllo al programma.
Un effetto collaterale dell’allocazione in banchi della memoria: si trattava di un ottimo sistema per nascondere un programma, dato che il programma non risultava visibile se la sua memoria non veniva allocata in un banco. Ecco perché BHP piazzava il suo codice nella memoria allocata.
Dopo essersi copiato nella memoria in banchi, il virus ripristinava il programma host alla sua posizione in memoria originaria e ripristinava la dimensione del programma ai valori originali. Questo permetteva al programma host di eseguirsi come se non fosse infettato. Tuttavia in questa fase il virus verificava il checksum del codice Basic del virus e se il checksum non corrispondeva, il virus sovrascriveva la memoria dell’host.
Una nota interessante sulla routine di verifica del checksum è che ignorava i primi tre byte del codice (ossia il numero di riga e il comando SYS): ciò facilitava il lavoro alla persona che avrebbe prodotto la variante successiva del virus. Malgrado la variante successiva differiva solamente per il numero di riga, questo fu sufficiente a battere il programma BHP-Killer, poiché BHP-Killer controllava l’intero codice Basic, compreso il numero di riga.
Il virus controllava se era già in esecuzione leggendo un byte di una specifica posizione in memoria. Se i valori corrispondevano, il virus assumeva che un’altra sua copia era in esecuzione. […] Se non vi era una copia del virus già in esecuzione, il virus copiava una porzione di codice in un indirizzo basso in un’area di memoria non allocata, agganciando svariati vettori e puntandoli verso il codice copiato.
I vettori agganciati erano ILOAD, ISAVE, MAIN, NMI, CBINV e RESET. L’aggancio di MAIN, NMI, CBINV e RESET rendeva il virus immune dai comandi di interruzione Break, Reset e dalla combinazione Run/Stop-Restore. Tali agganci garantivano al virus di non perdere il controllo durante il riavvio della macchina. Una simile tecnica venne poi impiegata negli agganci Ctrl-Alt-Delete impiegati da virus successivi nella piattaforma IBM PC, e negli agganci Ctrl-Amiga-Amiga nella piattaforma Commodore Amiga.
Una volta effettuati gli agganci, il virus eseguiva il codice dell’host. Il codice principale del virus sarebbe stato invocato a ogni richiesta di caricare o salvare un file.
L’aggancio ILOAD veniva raggiunto quando occorreva effettuare ricerche su un disco. Ciò avveniva ogni volta che si richiedeva l’elenco di una directory, e poteva accadere quando si effettuava una ricerca utilizzando un nome file con caratteri jolly, oppure la prima volta che si accedeva a un file. Altrimenti, l’hardware del lettore salvava in cache fino a 2 KB di dati e li restituiva direttamente.
Il virus chiamava il gestore ILOAD originale, poi controllava se era stato caricato un programma infettato. In caso positivo, il virus ripristinava il programma host alla sua posizione in memoria originaria e ripristinava la dimensione del programma ai valori originali. Altrimenti, anche se non era stato caricato alcun file, il virus invocava la routine di infezione.
L’aggancio ISAVE veniva raggiunto a ogni salvataggio di un file. Il virus chiamava il gestore ISAVE originale per salvare il file, quindi invocava la routine di infezione.
La routine di infezione cominciava controllando che il dispositivo richiesto fosse un lettore di dischetti. In caso positivo, il virus apriva il primo file nella cache. Il primo file in cache sarebbe stato il file salvato se questo codice veniva raggiunto attraverso l’aggancio ISAVE, altrimenti sarebbe stato il primo file nell’elenco della directory. Se il file era un programma Basic, il virus effettuava un veloce controllo di infezione leggendo il primo byte del programma e confrontandolo con il comando SYS. Inizialmente il virus leggeva soltanto un byte poiché i lettori di dischetti erano dispositivi seriali nella piattaforma Commodore 64, e quindi molto lenti. Tuttavia, se il comando SYS era presente, il virus verificava l’infezione leggendo e confrontando fino a 27 byte successivi. Un file veniva considerato infetto se tutti i 27 byte corrispondevano.
Se un file non era infetto, il virus passava a leggere dati dalla cache hardware. Il primo controllo verificava la presenza del classico layout del disco: la directory doveva trovarsi sulla traccia 18, settore 0, e il file da infettare non doveva risiedere in quella traccia.
Se tutti questi controlli erano positivi, il virus controllava l’elenco di tracce alla ricerca di settori liberi. Iniziava con la traccia contenente il file da infettare per poi muoversi all’esterno in direzioni alternate. Questo riduceva gli spostamenti che doveva effettuare il lettore per leggere il file in un secondo momento, e si trattava di un’ottimizzazione molto interessante, dato che alcuni virus multi-sector boot sull’IBM PC inserivano il loro codice a fine disco, e ciò causava spostamenti del lettore facilmente identificabili (da un punto di vista acustico: il lettore iniziava a produrre rumori sospetti).
Se vi erano almeno otto settori liberi sulla medesima traccia, il virus allocava otto settori per sé e aggiornava la bitmap del settore per quella traccia. Il codice per aggiornare la bitmap del settore era bellissimo: allocava i settori e creava l’elenco dei numeri di settore allo stesso tempo. […]
Il virus scriveva se stesso su disco in questo modo: il primo settore dell’host veniva copiato sull’ultimo settore allocato dal virus, poi quel primo settore veniva sostituito dal primo settore del virus. Dopodiché, il codice rimanente del virus veniva scritto sui settori allocati restanti. Qui era presente la directory stealth, ed era realizzata senza alcuno sforzo da parte dell’autore o degli autori del virus. Era un effetto collaterale del fatto che il virus non aggiornasse il conteggio dei blocchi nel settore della directory. Il conteggio dei blocchi non veniva usato dal DOS [l’autore si riferisce al Disk Operating System del Commodore 64, non al DOS dei PC IBM] per caricare i file — il suo scopo era puramente informativo.
Infatti lo stesso problema si presentava sul DOS della famiglia degli Apple II, e un tale virus sarebbe stato più facile da realizzare per quella piattaforma, dato che la comunicazione con l’hardware è molto più semplice negli Apple II. L’unico effetto evidente nel caso di BHP era che il numero di blocchi liberi su disco era visibilmente inferiore, dato che il valore veniva calcolato utilizzando la bitmap del settore, non l’elenco della directory.
Dopo qualsiasi chiamata a ILOAD o ISAVE, il virus verificava le condizioni di attivazione del payload, che erano le seguenti: a) la macchina doveva trovarsi in modalità ‘diretta’ (prompt dei comandi); b) il campo dei secondi del jiffy clock in tempo reale doveva essere un valore di 2–4 secondi; e c) la riga di scansione attuale del vertical retrace del monitor doveva essere almeno 128. Ciò rendeva l’attivazione un processo piuttosto casuale. Il payload doveva visualizzare un certo testo sul monitor, un carattere alla volta, cambiando in continuazione i colori del bordo.
Il numero seriale visualizzato era il numero di volte che veniva invocato il controllo del payload. A ogni chiamata, veniva incrementato di una unità, e si resettava al valore zero solo dopo 65.536 chiamate.
Adesso è chiaro: BHP era un virus molto avanti rispetto all’epoca.
Il virus BHP era grande soltanto 2.030 byte.