|
Appunti informatica |
|
Visite: 1543 | Gradito: | [ Picolo appunti ] |
Leggi anche appunti:Gli interrupt: gestioneGli interrupt: gestione A pag. 119 e seguenti abbiamo visto come un programma C può Disinstallare i tsrDisinstallare i TSR Sappiamo che, per un programma TSR, la capacità di disinstallarsi (liberando L'accessibilitÀ e la durata delle variabiliL'accessibilità e la durata delle variabili In C le variabili possono essere |
Chi non si è ancora imbattuto in qualche mutante di virus informatico? A titolo di esempio riportiamo e commentiamo il listato C di una semplice utility in grado di individuare nei programmi il virus Vienna B e di 'risanarli'.
Un programma colpito dal Vienna B è riconoscibile in base alle seguenti caratteristiche:
|
è un file .COM |
|
la sua dimensione è maggiore per 648 byte (codice del virus) rispetto al normale |
|
in questa 'appendice' vi è la stringa '*.COM' preceduta da una sequenza di 3 byte identici ai primi 3 byte del file |
|
i 3 byte che precedono tale sequenza erano, prima del contagio, i primi 3 byte del file stesso |
L'anti‑Vienna deve pertanto aprire il file sospetto, leggerne i primi 3 byte e concatenare a questi la stringa '*.COM' per ricostruire l'identificatore del virus. Esso deve poi leggere gli ultimi 648 byte del file in un buffer appositamente allocato e scandirli alla ricerca della stringa identificatrice composta in precedenza. Se essa viene localizzata, allora il file analizzato ha subìto il contagio. Per risanarlo è sufficiente sovrascrivere i suoi primi 3 byte con i 3 byte che, nel buffer, precedono la stringa identificatrice e troncarlo ad una dimensione pari alla lunghezza attuale diminuita di 648.
Vediamone il listato.
BARNINGA_Z! - 1990
DISINFES.C - Elimina il virus Vienna (versione B) dai file contagiati
COMPILABILE CON TURBO C++ 1.0
tcc -O -d disinfes.c wildargs.obj
#pragma warn -pia /* no warnings per assegnazioni all'interno di if */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#define _VIRUSDIM_ ((size_t)648) /* dimensione del virus */
#define _S_STRING_ ((unsigned char *)' *.COM') /* da cercare */
#define _S_BYTES_ 3 /* n. bytes da sostituire */
#define _O_BYTES_ -3 /* offset dei bytes rispetto alla stringa */
unsigned char *sstring = _S_STRING_;
char *msg[] = ;
unsigned char *srchstr(unsigned char *bufptr,unsigned char *sstring)
return(NULL);
int tronca(FILE *file,unsigned char *bufptr,int flen)
return(0);
int disinfesta(char *fname,unsigned char *bufptr)
void main(int argc,char **argv)
La funzione main() provvede a visualizzare un opportuno messaggio qualora il programma sia lanciato senza specificare sulla command line alcun nome di file. Se DISINFES è consolidato con WILDARGS.OBJ è possibile utilizzare le wildcard ' ' e ' ' nei nomi di file (vedere pag. main() provvede inoltre ad allocare il buffer necessario ai 648 byte letti dal file analizzato e a gestire il ciclo di chiamate (una per ogni file specificato sulla command line) alla funzione disinfesta(). Tale ciclo può, a prima vista, apparire di difficile interpretazione; l'algoritmo risulta tuttavia banale se si tiene conto di alcune particolarità. In primo luogo, è noto che la variabile argc contiene il numero di elementi presenti nell'array argv: per ottenere l'indice massimo di argv occorre dunque, inizialmente, decrementare di uno argc. Inoltre, dal momento che argv[0] è, per default, il pathname completo di DISINFES, è necessario escludere detto elemento dal ciclo, il quale si arresta quando argc assume valore nullo ; al compimento di ogni iterazione argc è decrementata, dunque l'analisi procede dall'ultimo file specificato sulla command line di DISINFES sino al primo . All'interno del ciclo vi è solamente una chiamata a printf(): per ogni file analizzato viene dunque stampato un messaggio individuato, nell'array msg, dal valore restituito dalla funzione disinfesta()
Questa apre il file e ne calcola la lunghezza , decrementandola di (se esso è contaminato, la variabile flen contiene dunque la sua dimensione originaria) e, dopo avere invocato tronca(), restituisce un valore atto a individuare l'opportuno messaggio d'errore nell'array msg qualora non fosse possibile chiudere il file al ritorno da tronca(); in caso contrario, il valore restituito da quest'ultima viene a sua volta restituito a main()
La funzione tronca() provvede a leggere i primi 3 byte del file nel buffer sstring, che è una variabile (puntatore) globale inizializzata a _S_STRING_. L'esame della direttiva
#define _S_STRING_ ' *.COM'
rivela che sstring contiene, dopo l'operazione di lettura, i primi 3 byte del file seguiti immediatamente da '*.COM': essa può dunque essere utilizzata per identificare il virus. Vengono poi letti nel buffer allocato in main() gli ultimi 648 byte del file analizzato: la funzione srchstr() li scandisce alla ricerca della stringa di identificazione.
L'algoritmo utilizzato da srchstr() è di carattere generale: all'interno di un primo ciclo, che incrementa ad ogni iterazione il puntatore al buffer e valuta, mediante un confronto con un altro puntatore, se il buffer è stato interamente scandito, si trova un secondo ciclo, il quale confronta tra loro i caratteri di buffer e stringa che si trovano all'offset corrispondente all'iterazione attuale e si interrompe se essi sono differenti. Se all'uscita da questo ciclo il contatore è uguale alla lunghezza della stringa ricercata, essa è stata individuata (perché il ciclo non è stato interrotto prima del termine), e il puntatore al buffer referenzia ora, in realtà, la stringa all'interno del buffer medesimo: tale puntatore è restituito a tronca()
Un valore non nullo restituito da srchstr() è altresì, per tronca(), il segnale che il virus è presente nel file analizzato: basta sommare al puntatore bufptr l'offset, rispetto alla stringa, della sequenza di byte che dovrebbe trovarsi in testa al file (attenzione: _O_BYTES_ è definito pari a , perciò i conti tornano), riscriverla 'al suo posto' e troncare la dimensione del file per completare il ripristino delle sue caratteristiche originarie; l'elaborazione descritta è ripetuta sul successivo file eventualmente specificato sulla riga di comando.
DISINFES.C, il cui impianto logico e tecnico è, come si vede, assai semplice, appare suscettibile di perfezionamenti . Un esame più attento del comportamento del Vienna B, effettuato disassemblando un file 'infetto', rivela che i 3 byte da esso scritti in testa ad ogni programma contagiato rappresentano una istruzione di salto JMP (1s byte) seguita (2s e 3s byte) dall'offset al quale viene in effetti trasferita l'elaborazione. In tutti i casi esaminati tale offset è risultato pari alla lunghezza originaria del programma diminuita di : se ne trae che il programma contagiato, non appena è eseguito, salta all'indirizzo immediatamente successivo alla fine del proprio codice, cioè all'inizio del codice del virus, il quale, in tal modo, ha la garanzia di poter assumere per primo il controllo della situazione. L'indirizzo ricavato dai 3 byte che originariamente erano in testa al file (JMP e near offset) sono utilizzati per riprendere la normale esecuzione del programma quando Vienna B termina le proprie operazioni.
L'affidabilità di DISINFES potrebbe essere allora accresciuta dotandolo di una funzione che confronti l'integer costituito dal 2s e 3s byte del file con la sua presunta lunghezza originale diminuita di
int jmp_test(char *sstring,int flen)
Si consideri l'espressione (*((int *)(sstring+1))): sommando al puntatore alla stringa utilizzata per identificare il virus se ne 'scavalca' il primo byte (l'istruzione JMP); l'operazione di cast forza il compilatore C a considerare puntatore a int l'espressione (sstring+1); l'indirezione restituisce quindi l'intero rappresentato dai byte di cui si è detto. Pertanto, se la funzione jmp_test() restituisce un valore non nullo si può concludere che il file esaminato non contiene il virus, pur contenendo la stringa sospetta.
Concludiamo il presente paragrafo presentando la modifica da apportare alla funzione tronca() per inserire jmp_test() in DISINFES.C
.
if((bufptr = srchstr(bufptr,sstring)) && !jmp_test(sstring,flen)) {
bufptr += _O_BYTES_;
.
Il codice di jmp_test(), qualora non si intenda dichiararne il prototipo, deve essere inserito nel listato prima della definizione di tronca()
I nomi di file sono posti in ordine alfabetico crescente dalle routine contenute in WILDARGS.OBJ. L'utilizzo, un po' particolare, di argc elimina la necessità di definire una variabile intera con la funzione di contatore.
Dal momento che la dimensione dei files .COM non può superare i 64Kb, la variabile flen è dichiarata int, con il vantaggio di rendere più efficiente la gestione dello stack: tuttavia ciò ha reso necessaria l'operazione di cast nelle chiamate alle funzioni fseek() e chsize()
E' stato necessario implementare una funzione apposita, rinunciando ad utilizzare una delle funzioni di libreria per la scansione di stringhe, ad es. strstr(), in quanto queste interrompono l'operazione al primo byte nullo incontrato (terminatore di stringa): si ricordi che il codice del virus non è, di per sé, una stringa, ma una sequenza di byte derivata dall'assemblaggio di un sorgente; è possibile che esso contenga byte nulli, i quali potrebbero interrompere anzitempo la ricerca.
Appunti su: |
|