Analisi statica avanzata di un Malware - Parte prima: Assembly

Tempo di lettura: 8 minuti
Data pubblicazione: August 25, 2016

Attenzione: per questo articolo consiglio delle basi di programmazione ad alto livello (C o Java ad esempio), o per lo meno una conoscenza generica di come funziona un programma di base (funzioni, oggetti, if, while, for, etc.).

Cercherò come sempre di inserire approfondimenti o link per comprendere al meglio.

Come detto negli scorsi articoli sull’analisi dei malware, l’analisi statica e dinamica di base sono ottimi metodi per ottenere informazioni semplici, ma sono solo la punta dell’iceberg del processo.

Per esempio, se utilizziamo l’analisi di base su di un malware, possiamo osservare una particolare funzione importata, ma non potremo sapere quando e in che modo viene utilizzata. È qui che ci viene in aiuto il disassemblaggio ed è questo che verrà introdotto in questo articolo, cercando come sempre di poter dare un’infarinatura di base e link vari di approfondimento.

Astrazione

Nell’architettura dei computer, un sistema è suddiviso in diversi livelli di astrazione, i quali nascondono i dettagli di implementazione all’utente finale. Nel nostro caso, gli autori di Malware creano programmi con linguaggi ad alto livello e utilizzano un compilatore per generare il codice che verrà poi utilizzato; noi, per analizzarli, utilizziamo un dissassemblatore per generare codice assembly (il livello più vicino al linguaggio macchina) che potremo leggere e analizzare in modo da capire come il malware funziona.

analisiavanzata-astrazione
analisiavanzata-astrazione

In modo molto generale, i livelli principali sono:

  1. Harware: è il livello fisico, il quale consiste di circuiti elettrici che implementano operazioni logiche, come XOR, AND, OR e NOT (conosciuto anche come logica digitale);
  2. Microcodice: conosciuto come firmware,_ _contiene microistruzioni che traducono il livello successivo (codice macchina) nel livello inferiore. nell’analisi di un malware è solitamente ignorato, in quanto specifico per ogni macchina;
  3. Codice macchina: consiste di opcodes, ossia codici esadecimali che dicono al processore cosa fare. Questo livello è creato quando un programma viene compilato con linguaggi ad alto livello;
  4. Linguaggi a basso livello: il codice è ora leggibile e il più comune è il linguaggio assembly. L’analisi di malware opera a questo livello, visto che il codice macchina è troppo complesso per essere compreso a fondo.
  5. Linguaggi ad alto livello: quasi tutti i programmatori operano a questo livello. Compilando questi linguaggi si ottiene codice macchina che eseguirà le nostre istruzioni. Ad esempio sono C e C++;
  6. Linguaggi interpreti: sono l’ultimo livello, ad esempio C#, Perl e Java.

Ogni istruzione del linguaggio assembly corrisponde ad un opcodes**_, _**il quale “dice” alla CPU quali operazioni effettuare. Gli operatori sono utilizzati per identificare i dati usati da un istruzione. Ci sono tre tipi:

  • Operatori immediati: sono valori come 0x42;
  • Operatori di regitro: si riferiscono ai registri, come ecx;
  • Operatori degli indirizzi di memoria: si riferiscono agli indirizzi di memoria contenenti i valori di interesse;

I registri sono piccoli porzioni di dati disponibili nella CPU, il cui contenuto può essere trasferito e salvato in altre parti. I registri principali sono:

  • Registri generici, utilizzati dalla CPU durante l’esecuzione;
  • Registri dei segmenti: utilizzati per tracciare sezioni di memoria;
  • Bandiere di stato: utilizzate per prendere decisioni;
  • Puntatori di istruzioni: utilizzati per tenere traccia della prossima istruzione da eseguire.
analisiavanzata-registri
analisiavanzata-registri

L’istruzione più comune e semplice è mov, la quale è utilizzata per spostare dati da una locazione ad un’altra. In altre parole per leggere e scrivere nella memoria. Il formato in cui è scritta è

mov destinazione, sorgente

Ad esempio possiamo trovare:

  • _mov eax, ebx**: **_copia il contenuto del registro EBX nel EAX;
  • mov eax, 0x42_: _copia il valore 0x42 nel registro EAX;
  • mov eax, [ebx]: copia 4 byte specificati nel registro EBX nel registro EBX;
  • mov eax, [ebx+esi*4]: copia 4 byte specificati nel risultato dell’equazione ebx+esi*4 nel registro EAX.

L’aritmetica nel codice assembly è la stessa di sempre, per cui gli operatori sono:

  • sub eax, 0x10: sottrae 0x10 da EAX;
  • add eax, ebx aggiunge EBX a EAX e salva il risultato in EAX;
  • inc edx: incrementa EDX di 1;
  • dec ecx: decrementa ECX di 1.
  • mul 0x50: moltiplica EAX per 0x50 e salva il risultato in EDX o EAX;
  • div 0x75: divide EDX o EAX di 0x75 e salta il risultato in EAX e il resto in EDX.

Altre operazioni possono essere xor, _or, shl _e_ ror. _Una guida scritta davvero bene sull’architettura x86 la potete trovare qui.

Un branch è una sequenza di codice eseguita condizionalmente, ossia in base al valore di una determinata variabile (in sostanza come se fosse un for o un while in linguaggio ad alto livello).

Il più popolare è l’istruzione jump _locazione, _composto da più di 30 tipi diversi. Alcuni sono:

  • je locazione: salta alla locazione specificata se ZF=0 (bandiera flag zero). Solitamente viene usato dopo un’istruzione cmp (comparo un valore con un altro);
  • jg locazione: effettua una comparazione del segno e salta alla destinazione specificata;
  • jle locazione: effettua una comparazione del segno dopo un cmp se l’operando di destinazione è minore o uguale dell’operando sorgente;
  • jecxz locazione: salta alla posizione se ECX=0.

Nel corso dell’articolo cercherò comunque di introdurre al meglio le modalità di utilizzo di ogni istruzione, in modo che possa essere capita. Come vedere sono molte, perciò non posso ovviamente fare un elenco di ognuna (che risulterebbe poco comprensibile senza esempi reali).

Poichè i Malware sono solitamente scritti in C, è importante sapere come un programma è tradotto dal C all’assembly. Questa conoscenza aiuterà anche a capire la differenze che intercorre tra i due linguaggi.

codicectoassembly
codicectoassembly

Questo è un semplicissimo programma C, convertito in assembly (gentilmente preso da C to Assembly). Come potete notare, anche con il più semplice dei programmi, decompilato in assembly,  non si inizia a capire più nulla (provate, sempre tramite il sito, a convertire l’esempio Bubble sort).

Software per l’analisi di base

Il software più potente e completo è indubbiamente IDA Pro. Purtroppo, essendo un software commerciale, costa e non poco (il pacchetto Starter 529 euro). È disponibile la versione gratuita a questo indirizzo, la quale ha alcune funzioni limitate (non è infatti da utilizzare in ambito professionale), ma per usi didattici può andare benissimo.

Altri software da poter utilizzare sono:

Nel prossimo articolo andrò ad analizzare un malware e cercherò di spiegare passo per passo tutte le funzioni e (quasi) tutte le informazioni da sapere per poter effettuare un’analisi statica avanzata.

esempioanalisi1
esempioanalisi1