RSS

Virus e antivirus: la ricerca dei virus TSR in memoria (3)

11 Dicembre 2011

Varie

antivirus-64

Dopo aver visto la ricerca dei files infetti da virus usando il linguaggio SVDL (ricerca algoritmica), oggi vedremo l’analogo per la memoria: MVDL (Memory Virus Detect Language).

Come nel caso dei  files, un virus che deve prendere il controllo del sistema per rimanervi attivo in memoria necessita di avere un sistema che gli permetta di evitare di installare più e più volte se stesso, pena un abbattimento delle performance del pc ad ogni operazione eseguita.

Per comprendere come questo meccanismo viene implementato, dobbiamo però conoscere come funziona un microprocessore e come il bios e il sistema operativo lo utilizzino per offrire le chiamate al sistema operativo. Ovviamente quello che vediamo è solo a livello molto semplice, giusto per avere una infarinatura, ed in questo siamo anche agevolati dal fatto che stiamo analizzando virus degli anni 90 che funzionavano su un sistema operativo molto semplice.

Il microprocessore gestisce una sequenza di istruzioni il cui flusso è determinato dal programmatore con un programma che ha un “ciclo” di operazioni prestabilito. Per far si che possano essere gestiti degli eventi esterni in real-time, il processore può essere notificato tramite un segnale esterno (l’interrupt) che deve sospendere le attuali operazioni e dedicarsi a risolvere la chiamata esterna perchè urgente.

Ad esempio, se il microprocessore controlla una fresa di una macchina a controllo numerico e l’operatore preme il pulsante di emergenza per bloccarla, il microprocessore viene notificato dell’evento non previsto e viene attivata una routine che blocca tutte le operazioni, in attesa che venga ripristinato il tutto (questo esempio è preso in prestito dai microcontrollori, per i processori normali il segnale può essere la notifica di un dispositivo collegato al pc, come un processore grafico, l’hardisk, la scheda seriale, ecc.).

Questo tipo di eventi a cui si richiede una risposta immediata sono anche chiamati “Non mascherabili” (NMI) perchè devono sempre bloccare il processore. Altri tipi di eventi, normalmente chiamati semplicemente Interrupt (IRQ), possono essere disabilitati o attivati a richiesta del programma.

Quando il processore viene interrotto, esso salva il suo stato attuale e salta ad eseguire un programma in una posizione dipendente dal tipo di processore e dal tipo di interrupt (possono essere in locazioni fisse o dipendenti da una tabella compilata). La routine chiamata si occupa di gestire la fonte dell’interrupt e prima di uscire notifica che l’interrut è stato gestito alla periferica che lo ha emesso (in modo che non arrivi nuovamente la stessa richiesta di interrupt nel momento in cui si ritorna ad eseguire il flusso originario del programma che era stato interrotto)

Questo meccanismo, oltre che avvenire per segnali hardware, può essere invocato anche tramite istruzioni software (nei processori x86 sono chiamate INT) e possono accettare come parametro (sempre nei processori x86) un numero che specifica quale locazione attivare tra 255 disponibili.

L’istruzione è utile quindi per invocare le funzioni di sistema operativo o del bios, dato che il programma viene interrotto nel punto in cui compare l’istruzione di interrupt e il controllo passa alla relativa funzione gestita dal sistema operativo che ne esplica la funzione richiesta.

Un virus che pertanto vuole assumere il controllo del sistema operativo può sostituire la chiamata di un interrupt (magari quelle che gestiscono l’accesso ai files) per farla puntare a se stesso in memoria e bypassare il normale funzionamento del sistema operativo a suo comando.

Se una persona apre un file dovrebbe essere invocato il servizio del sistema operativo dedicato alla lettura dei files, ma invece si attiva la parte del virus che infetta il file che si vuole aprire e poi ne passa il controllo al sistema operativo come se nulla fosse (e se è di tipo stealth, magari fa vedere al sistema operativo il file non infetto…).

Torniamo ora alla possibilità di verificare se un virus è attivo in memoria o meno.

Quando un programma infetto viene eseguito, una parte del codice del virus invoca una chiamata a sistema operativo (tramite interrupt) che normalmente dovrebbe dare un certo esito, tranne che non ci sia una copia del virus in memoria, dato che in questo caso è il virus stesso che governa il pc e ne può variare il risultato!

Con questo semplice meccanismo un virus sa rivelare la sua presenza e se noi ne imitiamo le mosse, possiamo avere un antivirus che scansiona la memoria in un attimo: dai semplici calcoli 10.000 virus verrebbero scansionati in meno di 1 secondo, indipendentemente da quanta memoria sia installata sul sistema.

Oggigiorno la scansione di 4GB di Ram per trovare dei virus è una attività che richiede invece molto tempo, considerando poi che la velocità di lettura della memoria RAM è considerevolmente più veloce di quella dei dischi.

L’MVDL (Memory Virus Detect Language) era quindi il passo successivo: un linguaggio di programmazione per rilevare i virus in memoria, tramite un compilatore ed un interprete.

A quel tempo però non implementai il compilatore/interprete dell’MVDL, ma ne scrissi delle specifiche parziali (da cui posso fare degli esempi), mentre creai la versione in linguaggio macchina per testarne la funzionalità (cosa che avvenne con esito positivo)

Vediamo un esempio:

CODE 01;
SEARCH FOR 'SLAYER';
ASSUME AX=0F1FAh;
INT 21h;
COMPARE AX WITH 0AAAAh BY =
ON TRUE DO '%#^& in $'

Il codice in MVDL qui sopra ricerca il virus Slayer in memoria; la corrispondente versione in assembler è la seguente:

MOV  AX,0F1FAh
INT  21h
CMP  AX,0AAAAh
JE   @VirusActiveInMemory

Analizzando il codice assembler vediamo che:

  • Il virus effettua una chiamata all’interrupt 21h (servizi del Dos) tramite l’istruzione INT 21h
  • Alla routine viene passato un parametro all’interno del registro AX. Questo parametro non corrisponde a nessun servizio fornito dal sistema DOS.
  • Dopo aver eseguito la routine associata al gestore di interrupt, l’istruzione eseguita è un confronto (CMP) in cui si verifica che ci sia il valore 0AAAAh nel registro AX
  • In caso di esito positivo, il virus è attivo in memoria, dato che il DOS non avrebbe mai restituito quel risultato.

In termini del linguaggio MVDL, la prima istruzione è l’ancora di salvezza che permette di estendere il linguaggio a piacimento per stare al passo coi tempi (così come abbiamo visto per il linguaggio SVDL), mentre:

  • SEARCH FOR è una dichiarazione che server per documentare per che virus abbiamo scritto questo codice di ricerca.
  • ASSUME è una istruzione con cui possiamo assegnare ai registri del processore un valore
  • INT è l’istruzione che richiama l’interrupt
  • COMPARE è l’istruzione che confronta una variabile con un valore tramite l’operazione specificata da BY
  • ON TRUE DO è l’istruzione che se il risultato del confronto è positivo, stampa le informazioni sull’avvenuta identificazione del virus in memoria tramite quello che è racchiuso negli ”.

L’output dell’MVDL (“SLAYER virus found active in memory”) si basa su queste macro:

  • %   stringa contenuta in SEARCH FOR ‘…’
  • #   virus
  • $   memory
  • @   MVDL
  • &   active
  • ^   found

Vediamo un altro esempio che rintraccia il virus Flip (virus polimorfico):

CODE 01;
SEARCH  FOR 'FLIP';
ASSUME  AX = 0FE01h;
INT     21h;
COMPARE AX WITH 01FEh BY =;
ON TRUE DO '?'
 
{
Assembler instructions:
MOV  AX,0FE01h
INT  21h
CMP  AX,01FEh
JE   virus_in_memory
}

Come possiamo vedere concettualmente non c’è nulla di differente rispetto al caso precedente, perchè il metodo è sempre lo stesso, cambiando solo i parametri di chiamata e il valore di confronto atteso.

Alcune note curiose: il virus Yankee Doddle utilizza una chiamata a INT 21 funzione C603 per verificare la sua presenza, ma è più sicuro utilizzare la chiamata a C600 (in quanto la precedente modifica anche dei registri in caso che il virus sia presente). Si potrebbe utilizzare anche la chiamata a C601 (che restituisce AH=0 ma è meno sicura), mentre sono poco utili le chiamate a C602 e C5xx.

Il virus Bebe non è un TSR, ma quando attiva il suo effetto “maligno”, copia il suo gestore dell’interrupt 1Ch nella memoria bassa (segmento 0000), e pone 2 byte prima della routine per sapere che c’è il suo gestore attivo. Pertanto questo virus è rintracciabile in memoria nel momento in cui è attivo.

Dato che non avevo fatto l’implementazione dell’MVDL visto che ne stavo ancora creando le specifiche, posto, per chi ha un background di programmazione, l’antivirus in versione nativa per fare la scansione della memoria.

L’antivirus rintraccia 21 virus ed è capace di identificare la famiglia a cui appartiene quel virus. Fa uso dell’oggetto MMV  (Menager of Virus in Memory) in cui viene usato il codice in linguaggio assembler estratto dai virus per eseguire la scansione, e di un semplice file Pascal che richiama l’oggetto:

program Find_virus;
uses Find_Virus;
var Vir:MMV;
begin
Vir.Init(Find);
If Vir.ValidStatus
then if Vir.Allert
then begin
write('Found ',Vir.WhatVirus);
if Vir.WhatFamily<>'none'
then write(' (family: ',Vir.WhatFamily,')');
writeln(' in memory');
end
else writeln('no virus in memory');
Vir.Done
end.

Ecco il codice della libreria:

unit find_virus;
interface
const
NVirus  = 21;
NFamily = 2;
 
{vn: Virus Name}
vnNone         =  0;
vnDataLock     =  1;
vn1701         =  2;
vnFlip         =  3;
vnJerusalem    =  4; {jerusalem, jerusalem-A, 1244}
vnSlayer       =  5;
vnTequila      =  6;
vnTopo         =  7;
vnDarkAvenger  =  8;
vn584          =  9;
vn706_768      =  10;
vn855_880      =  11;
vnSunday       =  12;
vnKeyPressed   =  13;
vn1030         =  14;
vnAlabama      =  15;
vn1554         =  16; {tenbyte}
vnDoodle       =  17; {Yankee Doodle}
vnBadBoy       =  18;
vnDemolition   =  19;
vnCansu        =  20;
vnBebe         =  21;
 
{vf: Virus Family}
vfNone         =  0;
vfJerusalem    =  1;
vf855          =  2;
 
{ss: Status of Scan}
ssOK           =  0;
ssScanOutRange =  1;
 
const VirusFamily:Array[1..NFamily] of string[11]=
('Jerusalem',
'855');
 
type VirusRec=record
Name:string[16];
Family:byte;
end;
VirusTable=Array[1..NVirus] of VirusRec;
const VirusInfo:VirusTable=(
(Name:  'DataLock';           Family: vfNone),
(Name:  '1701';               Family: vfNone),
(Name:  'Flip';               Family: vfNone),
(Name:  'Jerusalem o 1244';   Family: vfJerusalem),
(Name:  'Slayer';             Family: vfNone),
(Name:  'Tequila';            Family: vfNone),
(Name:  'Topo';               Family: vfNone),
(Name:  'DarkAvenger';        Family: vfNone),
(Name:  '584';                Family: vf855),
(Name:  '706 o 768';          Family: vf855),
(Name:  '855 o 880';          Family: vf855),
(Name:  'Sunday';             Family: vfJerusalem),
(Name:  'KeyPressed';         Family: vfNone),
(Name:  '1030';               Family: vfNone),
(Name:  'Alabama';            Family: vfNone),
(Name:  '1554';               Family: vfNone),
(Name:  'Yankee Doodle';      Family: vfNone),
(Name:  'BadBoy';             Family: vfNone),
(Name:  'Demolition';         Family: vfNone),
(Name:  'Cansu';              Family: vfNone),
(Name:  'Bebe';               Family: vfNone));
 
{MMV:Menager of Virus in Memory}
Type MMV=object
ScanReport:byte;
Status:byte;
Constructor Init(Find:byte);
Destructor  Done;
Procedure ReSearch; Virtual;
Function Allert:Boolean; Virtual;
Function WhatVirus:String; Virtual;
Function WhatFamily:String; Virtual;
Function ValidStatus:Boolean; Virtual;
Function Version:word; Virtual;
end;
 
var Find:byte;
 
implementation
 
procedure Search; forward;
 
Constructor MMV.Init(Find:byte);
begin
ScanReport:=Find;
if ScanReport > NVirus
then begin
Status:=ssScanOutRange;
ScanReport:=vnNone
end
else Status:=ssOK
end;
 
Destructor MMV.Done;
begin
end;
 
Procedure MMV.ReSearch;
begin
Search;
ScanReport:=Find;
Status:=ssOk;
end;
 
Function MMV.Allert:Boolean;
begin
Allert:=ScanReport<>0;
end;
 
Function MMV.WhatVirus:String;
begin
if ScanReport=vnNone
then WhatVirus:='No known Virus found in memory'
else WhatVirus:=VirusInfo[ScanReport].Name;
end;
 
Function MMV.WhatFamily:String;
begin
if ScanReport=vnNone then WhatFamily:=''
else if VirusInfo[ScanReport].Family=vfNone
then WhatFamily:='none'
else WhatFamily:=VirusFamily[VirusInfo[ScanReport].Family];
end;
 
Function MMV.ValidStatus:boolean;
begin
ValidStatus:=Status=0;
end;
 
Function MMV.Version:word;
begin
Version:=NVirus;
end;
 
procedure search;assembler;
const BabBoyTable:array[1..12] of byte=
($2e,$0ff,$36,$27,$01,$0E,$1F,$2E,$FF,$26,$25,$01);
asm
{DataLock}
MOV  Find,vnDataLock
MOV  AH,0BEh
INT  21h
CMP  AX,1234h
JZ   @@virus_in_memory
{1701}
MOV  Find,vn1701
MOV  AX,4BFFh
INT  21h
CMP  DI,55AAh
JE   @@virus_in_memory
{Flip}
MOV  Find,vnFlip
MOV  AX,0FE01h
INT  21h
CMP  AX,01FEh
JE   @@virus_in_memory
{Jerusalem / 1244 / jerusalem-A}
MOV  Find,vnJerusalem
MOV  AH,0E0h
INT  21h
CMP  AH,0E0h
JAE  @@not_jeru_in_mem
CMP  AH,03h
JB   @@not_jeru_in_mem
JMP  @@virus_in_memory
@@not_jeru_in_mem:
{Sunday}
MOV  Find,vnSunday
MOV  AH,0FFh
INT  21h
CMP  AH,0FFh
JAE  @@not_Sun_in_mem
CMP  AH,04h
JB   @@not_Sun_in_mem
JMP  @@virus_in_memory
@@not_sun_in_mem:
 
{Slayer}
MOV  Find,vnSlayer
MOV  AX,0F1FAh
INT  21h
CMP  AX,0AAAAh
JE   @@virus_in_memory
 
{Tequila}
MOV  Find,vnTequila
MOV  AX,0FE02h
INT  21h
CMP  AX,01FDh
JE   @@virus_in_memory
{1030}
MOV  Find,vn1030
MOV  AX,3521h
INT  21h
CMP  BX,200h
JNE  @@not_1030_in_mem
{   MOV  AX,3524h
INT  21h
CMP  BX,484h
JE   @@virus_in_memory}
JMP  @@virus_in_memory
@@not_1030_in_mem:
{Yankee Doodle}
MOV  Find,vnDoodle
MOV  AX,0C600h
CLC
INT  21h
JNC  @@not_Doodle_in_mem
CMP  AX,002Ch
JE   @@Virus_in_memory
@@not_Doodle_in_mem:
{Demolition}
MOV  Find,vnDemolition
MOV  AX,4B7Fh
INT  21h
JNC  @@Virus_in_memory
 
{BadBoy}
MOV  Find,vnBadBoy
MOV  ES,PrefixSeg
MOV  ES,ES:[0002]
MOV  SI,Offset BabBoyTable
MOV  DI,100
MOV  CX,000Bh
REP  CMPSB
JZ   @@Virus_in_memory
{Bebe}
MOV  Find,vnBebe
MOV  AX,351Ch
INT  21h
MOV  AX,ES:[BX-2]
CMP  AX,00FFh
JNE  @@Not_Bebe_in_mem
PUSH ES
POP  AX
OR   AX,AX
JZ   @@Virus_in_memory
@@Not_Bebe_In_mem:
 
{Cansu}
MOV  Find,vnCansu
PUSH DS
XOR  AX,AX
MOV  DS,AX
MOV  ES,DS:[004eh]
MOV  BX,00D6h
CMP  Word ptr ES:[BX],9876h
JNE  @@not_Cansu_in_mem
POP  DS
JMP  @@Virus_in_memory
@@not_Cansu_in_mem:
POP  DS
 
{Topo}
MOV  Find,vnTopo
XOR  BX,BX
MOV  ES,BX
MOV  AX,11h
CMP  ES:[3FEh],AX     {0000:03fe}
JE   @@virus_in_memory
{DarkAvenger}
MOV  Find,vnDarkAvenger
PUSH DS
{ES=0}
LDS  AX,ES:[0080h]
CMP  AX,2EEh
JNE  @@not_Dark_in_mem
LDS  AX,ES:[009Ch]
CMP  AX,2A9h
JNE  @@not_Dark_in_mem
LDS  AX,ES:[004Ch]
CMP  AX,6E4h
JNE  @@not_Dark_in_mem
POP  DS
JMP  @@virus_in_memory
@@not_Dark_in_mem:
POP  DS
 
{584 (della famiglia dell'855 -novembre 17-)}
MOV  Find,vn584
{ES=0}
CMP  ES:[020Ch].byte,4Dh
JE   @@virus_in_memory
{855 e 880}
MOV  Find,vn855_880
{ES=0}
CMP  ES:[020Ch].word,5856h
JE   @@virus_in_memory
{706 e 768 (della famiglia dell'855)}
MOV  Find,vn706_768
{ES=0}
CMP  ES:[020Ch].byte,56
JE   @@virus_in_memory
{KeyPressed}
MOV  Find,vnKeyPressed
{ES=0}
CMP  ES:[0600h].byte,1
JE   @@virus_in_memory
 
MOV  Find,vnAlabama
{ES=0}
MOV  AX,ES:[0]
OR   AX,ES:[2]
JZ   @@virus_in_memory
{1554}
MOV  Find,vn1554
{ES=0}
CMP  ES:[0084].word,0413h
JNE  @@not_1554_in_mem
CMP  ES:[0086].word,9A00h
JE   @@virus_in_memory
@@not_1554_in_mem:
 
MOV  Find,vnNONE
 
@@virus_in_memory:
 
end;
 
begin
search;
end.

A questo punto un piccolo riassunto finale.
I concetti presenti in SVDL sulla ricerca algoritmica dei virus sono apparsi negli antivirus solo in alcuni casi proprio per l’identificazione dei virus polimorfici, anche se l’orientamento verso cui la ricerca si è spostata era quello della criptoanalisi per identificare i virus polimorfici (dato che le routine di mascheramento operano con poche istruzione, come XOR, alcune sequenze di codice anche crittografato del virus poteva lasciare una traccia comunque visibile sotto forma di pattern).

Riguardo a MVDL e alla scansione algoritmica della memoria, non mi è noto (ma potrei sbagliare) nessun prodotto che ne faccia o abbia fatto uso (e questo è un vero peccato, perchè vista l’estrema velocità di scansione, un antivirus potrebbe schedulare la scansione della memoria ogni tot minuti senza che questo intacchi le performance del computer).

One Response to “Virus e antivirus: la ricerca dei virus TSR in memoria (3)”

  1. max Says:

    Stavo cercando un buon antivirus e ho scoperto Kaspersky con uno sconto di 10€ http://www.kaspersky.com/it/kaspersky_internet_security Poi ho scoperto che c’era anche Kaspersky Mobile Security in Regalo per il mio SmartPhone. Lo consiglio a tutti!

Leave a Reply