Principi di reverse engineering
Il reverse engineering è il processo di analisi di un programma compilato, ovvero di un binario eseguibile, per comprendere la sua logica di funzionamento e le sue caratteristiche interne. L'obiettivo può essere variabile: dalla ricerca di vulnerabilità di sicurezza, alla comprensione di algoritmi proprietari, fino alla modifica del software per aggiungere nuove funzionalità o per garantire la compatibilità con altri sistemi.
I due principali approcci al reverse engineering sono
- approccio statico, spesso con l'ausilio di un disassemblatore o un decompilatore, si tratta di interpretare il codice macchina del programma e provare a capire staticamente il funzionamento,
- approccio dinamico, ovvero eseguire il programma in un ambiente controllato, con l'ausilio di un debugger, un analizzatore di rete oppure altri strumenti per analizzarne il comportamento.
Vediamo adesso alcuni strumenti di base che ci permettono di fare delle analisi preliminari sui file eseguibili.
Trovare le informazioni di base di un binario
Con lo strumento file
possiamo identificare delle proprietà di base di un eseguibile, ad esempio sul binario ls
restituisce questo output.
$ file /usr/bin/ls
/usr/bin/ls: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=7aa8f52223e371fb77314d9a363129574096093b, for GNU/Linux 4.4.0, stripped
Ci restituisce delle informazioni come il fatto che è un eseguibile x86 a 64 bit, il linking è dinamico e che è stripped, ovvero non contiene informazioni di debug.
Possiamo visualizzare quali librerie sono caricate dinamicamente tramite il comando ldd
.
$ ldd /usr/bin/ls
linux-vdso.so.1 (0x00007022fc02e000)
libcap.so.2 => /usr/lib/libcap.so.2 (0x00007022fbfc8000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007022fbddc000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007022fc030000)
Trovare le stringhe nei binari
Spesso le costanti stringhe sono inserite nei binari a certi indirizzi, e possono essere trovate tramite lo strumento strings
.
Ad esempio queste sono alcune stringhe interessanti che si possono trovare nel binario ls
.
$ strings /usr/bin/ls
/lib64/ld-linux-x86-64.so.2
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
__libc_start_main
__cxa_finalize
__cxa_atexit
strcmp
stdout
__overflow
fwrite_unlocked
[...]
cannot access %s
OWNER@
GROUP@
EVERYONE@
cannot read symbolic link %s
cannot open directory %s
reading directory %s
closing directory %s
[...]
Nel primo blocco si possono notare delle stringhe probabilmente usate per la risoluzione dinamica delle funzioni di libreria, mentre nel secondo blocco ci sono delle stringhe di formattazione, probabilmente usate in funzioni printf
o simili.
Tracciare le system call
Una system call, o chiamata di sistema, è un meccanismo che permette ai programmi in esecuzione di interagire con il sistema operativo. Quando un'applicazione deve eseguire operazioni che richiedono l'accesso alle risorse hardware o ai servizi di sistema, come la gestione dei file, la comunicazione di rete o la gestione della memoria, utilizza le system call per richiedere tali servizi al kernel del sistema operativo. Sapere quali system call vengono chiamate da un processo può quindi darci delle informazioni su cosa sta facendo un programma.
Tracciare quali system call vengono eseguite da un processo è possibile in ambiente linux tramite lo strumento strace
.
Ad esempio possiamo provare a lanciarlo sul programma ls
.
strace ls
L'output sarà composto da tutte le system call invocate dal programma ls
.
$ strace ls
execve("/usr/bin/ls", ["ls"], 0x7ffc85430f10 /* 52 vars */) = 0
[...]
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
write(1, "main main.c\n", 13main main.c
) = 13
close(1) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++
In particolare possiamo notare la prima chiamata execve
per eseguire il processo ls
, la chiamata a fstat
per ottenere informazioni sui file relativi alla cartella corrente, e write
per stampare in output i nomi dei file.
strace
ci mostra anche i parametri delle chiamate e i valori di ritorno.
Questo può essere uno strumento molto utile per ottenere una informazione ad alto livello del comportamento del programma.
Esiste un programma molto simile che traccia però le chiamate a funzione a librerie caricate dinamicamente, chiamato ltrace
.