6.4 Valutazione delle performance
6.4.1 Bilancio sull’overhead di una chiamata di sistema
In base all’analisi appena fatta, si pu`o calcolare l’overhead associato ad ogni chiamata di sistema come:
Cint+ Cgret+ Ctstat+ Ccrypt+ Cdecrypt
In prima approssimazione, i primi tre valori , ossia Cint, Cgret, Ctstat, possono essere considerati come fissi ed i test effettuati dimostrano che la loro somma `e di 52 + 81 + 4 = 137µsec. A questo costo fisso occorre sommare poi il costo variabile dato da Ccrypt e Cdecrypt, che dipender`a dal numero e dalle dimensioni degli unit block contenuti nello state block terminato e da eseguire, e dalle loro dimensioni.
Riprendendo l’esempio precedente, nel caso di state block composti da un unit block da 1 kbyte, i tempi sono i seguenti:
Ccrypt+ Cdecrypt = 103 + 103 = 206µsec utilizzando DES Ccrypt+ Cdecrypt = 89 + 89 = 178µsec utilizzando Blowfish
Ccrypt+ Cdecrypt= 60 + 60 = 120µsec utilizzando One Time Pad appros-simato con generatore lineare congruente
6.4 Valutazione delle performance 86
Avendo dunque dei costi totali di:
Ctot = 137 + 206 = 343µsec utilizzando DES Ctot = 137 + 178 = 315µsec utilizzando Blowfish
Ctot = 137 + 120 = 257µsec utilizzando One Time Pad approssimato con generatore lineare congruente
Ctot = 137 + 0 = 137µsec nel caso si decida di non far uso di cifratura
Questi costi dovranno essere sommati al costo base di ciascuna chiamata di sistema che, a sua volta dipende dalla chiamata utilizzata di volta in volta.
Di seguito vengono riportati i costi di tre comuni chiamate di sistema:
time : 2µsec open : 3µsec
write(1kbyte) : 7µsec
Libreria di introspezione
La libreria di introspezione viene utilizzata dal processo manager per map-pare, nel proprio spazio di memoria virtuale, indirizzi di memoria virtuali appartenenti al processo offuscato, in modo tale da poter manipolare il loro contenuto. Tuttavia allo stato attuale la libreria soffre di un problema che causa una degradazione sensibile delle prestazioni del sistema. Il problema `e il seguente: se mappo un indirizzo A e successivamente un indirizzo B, dal momento in cui ho mappato B, l’indirizzo A e gli indirizzi che fanno parte della medesima pagina non sono pi`u accessibili. Per potervi di nuovo acce-dere, devo mappare di nuovo A. Questo crea chiaramente una degradazione, poich`e gran parte delle pagine a cui si deve accedere vengono riusate e invece di poter riutilizzare il lavoro gi`a fatto, `e necessario pagare di nuovo il costo
6.4 Valutazione delle performance 87
di una map e successivamente anche quello di una unmap in modo da evitare errori alla futura rimappatura della stessa pagina.
Dai test effettuati si `e osservato che il costo di una map `e di 37µsec, e quello di una unmap `e di 4µsec.
Di seguito verranno analizzati i costi delle varie fasi viste in precedenza, in modo tale da darne una nuova valutazione nel caso il problema alla libreria non fosse presente nel caso in cui le pagine a cui si vuole accedere siano gi`a state mappate in precedenza:
1. intercettazione delle chiamate di sistema : questa fase `e carat-terizzata da una map e una unmap, che corrispondono all’indirizzo di memoria della variabile si sincronizzazione associata al canale di comu-nicazione. Dunque si avrebbe un costo Cint= 52 − (Cmap+ Cunmap) = 52 − (37 + 4) = 11µsec.
2. individuazione del return address : questa fase `e caratterizzata in media da due map, che corrispondono alle due pagine contenenti le aree del kernel stack e dello user stack da analizzare. Inoltre c’`e la unmap della pagina del kernel stack. La unmap dello user stack avverr`a nella fase successiva. Dunque si avrebbe un costo Cgret = 81 − (2 · Cmap+ Cunmap) = 81 − (2 · 37 + 4) = 81 − 78 = 4µsec.
3. transizione di stato : questa fase `e caratterizzata dalla unmap del-la pagina appartenente allo user stack mappato neldel-la fase precedente.
Dunque si avrebbe un costo Ctstat = 4 − Cunmap = 4 − 4 = 0µsec. In questo caso dunque il costo residuo `e dell’ordine dei nano-secondi.
4. fasi di cifratura e decifratura: questa fase `e caratterizzata da una map e da una unmap per quanto riguarda la variabile “s”, ossia il costo fisso di gestione per ogni unit block. Dunque, il nuovo valore della variabile
`
e s = 41 − (Cmap+ Cunmap) = 41 − (37 + 4) = 0µsec. In questo caso s ha
6.4 Valutazione delle performance 88
un valore dell’ordine dei nano-secondi. Nel caso dell’esempio in cui sia lo state block terminato che lo state block da eseguire siano composti da uno unit block di 1 kbyte, il nuovo costo di cifratura e decifratura sar`a:
Ccrypt = Cdecrypt= 62µsec per il Des Ccrypt = Cdecrypt= 48µsec per Blowfish
Ccrypt = Cdecrypt = 19µsec per One Time Pad approssimato con gene-ratore lineare congruente.
Dunque in questo caso, l’overhead fisso associato ad ogni chiamata di sistema risulterebbe:
Cint+ Cgret+ Ctstat = 11 + 4 + 0 = 15µsec
a questo occorre sommare il costo variabile della cifratura e decifratura che nel caso di state block composti da uno unit block delle dimensioni di 1 kbyte sarebbe:
Ccrypt+ Cdecrypt = 62 + 62 = 124µsec utilizzando DES Ccrypt+ Cdecrypt = 48 + 48 = 96µsec utilizzando Blowfish
Ccrypt+ Cdecrypt= 19 + 19 = 38µsec utilizzando One Time Pad approssi-mato con generatore lineare congruente
Abbiamo quindi i seguenti costi totali:
Ctot = 15 + 124 = 139µsec utilizzando DES Ctot = 15 + 96 = 111µsec utilizzando Blowfish
Ctot = 15 + 38 = 53µsec utilizzando One Time Pad approssimato con generatore lineare congruente
Ctot = 15 + 0 = 15µsec se non si applica la cifratura
6.4 Valutazione delle performance 89
Figura 6.1: (1) l’overhead attuale. (2) l’overhead se la libreria permettesse di riutilizzare gli indirizzi delle pagine gi`a mappate
Come si pu`o osservare dal grafico in Fig. 6.1, la differenza di overhead rispetto al caso attuale `e significativa.
6.4.2 Considerazioni sull’overhead globale
Non `e semplice valutare in generale le prestazioni del sistema, poich`e l’ove-rhead `e fortemente dipendente dalle caratteristiche di ciascuna applicazione che deve essere protetta con l’architettura. Tali caratteristiche riguardano:
• la distribuzione delle chiamate di sistema;
• le dimensioni (e il numero) degli unit block all’interno di ciascun state block ed il numero di istruzioni eseguite in un unit block;
6.4 Valutazione delle performance 90
La distribuzione delle chiamate di sistema `e importante, poich`e sono pro-prio le chiamate ad innescare i meccanismi di gestione, e dunque l’overhead `e direttamente dipendente dal loro numero ed anche dal livello di offuscamento.
Le dimensioni degli state block, anch’essi funzione della distribuzione delle chiamate di sistema, determinano inoltre la dimensione di dati da cifrare. Di conseguenza, minore `e il rapporto tra il codice effettivamente eseguito ad un dato istante e la dimensione dello state block d’appartenenza, maggiore sar`a l’overhead.
Un esempio di caso ottimo per minimizzare i costi `e quello di uno state block costituito da un costrutto “while” il cui corpo sia eseguito numerose volte, con una chiamata di sistema appena all’esterno del ciclo che determina la fine del blocco.
while(guard){
code;
. . code;
}
syscall();
In un caso come questo, all’aumentare del numero dei cicli, l’overhead tende a zero.
Un esempio di caso pessimo `e il seguente:
if(cond){
code;
6.4 Valutazione delle performance 91
. . code;
}
syscall();
Questo blocco di codice pu`o essere considerato un caso sfavorevole, da un punto di vista delle prestazioni, se il ramo “if” contiene un numero molto grande di istruzioni ma il predicato `e falso. Ci`o genera un overhead elevato, poich`e il codice realmente eseguito `e solo la chiamata di sistema, ma si `e dovuto pagare un costo elevato per la cifratura/decifratura degli state block.
Un metodo per stimare le prestazioni generali del sistema, `e ovviamente quello di valutare i tempi per eseguire alcune applicazioni reali. Questo al momento non `e possibile perch`e questa tesi `e focalizzata sulla progettazione dell’architettura del sistema di offuscamento e sullo sviluppo del sistema a tempo d’esecuzione. Lo sviluppo di un compilatore che possa generare binari adatti a tale architettura avver`a successivamente.
Capitolo 7 Conclusioni
Questa tesi ha presentato una nuova architettura per offuscare il programma di un processo. L’architettura `e basata su un ambiente a macchine virtua-li, in cui una macchina esegue il processo offuscato, e un’altra esegue un processo che gestisce l’offuscamento del processo precedente (il “manager”).
La virtualizzazione `e stata utilizzata per ottenere una maggiore robustezza e separazione tra l’ambiente d’esecuzione e quello di gestione. Le principali tecniche di offuscamento implementate dall’architettura riguardano la parti-zione del controllo di flusso tra due macchine virtuali e la cifratura del codice da eseguire. Entrambe le tecniche sfruttano una strutturazione ad hoc del codice del processo. Tale strutturazione prevede che il codice sia suddiviso in blocchi delimitati dalle invocazioni al sistema operativo. L’eseguibile del pro-cesso contiene il codice dei blocchi, ma non le informazioni che li collegano;
queste verranno mantenute nascoste sull’altra macchina virtuale, che si oc-cuper`a di gestire le transizioni del flusso d’esecuzione tra la fine di un blocco di codice e l’inizio del blocco successivo. La cifratura del codice riguarda sia il disco fisso che la memoria, ed `e associata alla decomposizione in blocchi del codice. In memoria, durante l’esecuzione del processo, solo il blocco di codice momentaneamente attivo `e in chiaro, mentre gli altri rimangono cifrati. Le
93
operazioni di cifratura e decifratura vengono effettuate dal processo manager eseguito dall’altra macchina virtuale, che incapsula quindi le chiavi di cifra-tura. Il processo manager viene eseguito ogni volta che viene invocata una chiamata di sistema dal processo offuscato ed implementa le sue operazioni di gestione, in particolare la manipolazione dello stato del processo sull’altra macchina, applicando la tecnica d’introspezione della memoria.
Il prototipo realizzato ha dimostrato la fattibilit`a dell’architettura propo-sta. I test effettuati hanno permesso di valutare l’overhead associato a cia-scuna chiamata di sistema e dimostrano come l’effettiva usabilit`a dell’archi-tettura in termini di efficienza, dipenda dalla futura evoluzione della libreria d’introspezione utilizzata.
Questa tesi si `e concentrata sulla progettazione dell’architettura di offu-scamento proposta e sulla realizzazione di un prototipo del sistema a tempo d’esecuzione. I possibili sviluppi futuri riguardano l’implementazione di un compilatore in grado di produrre dei binari adatti all’eseccuzione sull’archi-tettura attualmente implementata, in modo da soddisfare le specifiche sulla strutturazione del codice.
Appendice A - Codice
manager.c
#include <s t d i o . h>
#include < s t d l i b . h>
#include < s t r i n g . h>
#include <t i m e . h>
#include <x e n c t r l . h>
#include <xen / i o / xenbus . h>
#include <x s . h>
#include <l i n u x / k e r n e l . h>
#include <l i n u x / u n i s t d . h>
#include <s y s / s t a t . h>
#include <asm/ p o s i x t y p e s . h>
#include <l i n u x / t y p e s . h>
#include ” p a r s e g r a p h . h”
#include ” p a r s e c o d e . h”
#include ” t v m m i n t r o s f u n . h”
#define p a g e a d d r ( a ) ( ( ( unsigned long ) ( a ) ) & 0xFFFFF000 )
#define munmap page ( p t r ) (munmap ( ( void ∗ ) p a g e a d d r ( ( p t r ) ) , XC PAGE SIZE ) )
95
#define NO TLB 1
#define PRINT 1
typedef k e r n e l u i d 3 2 t q i d t ;
s t a t i c i n t x c h a n d l e = −1;
s t a t i c u i n t 3 2 t domid = 0 ; s t a t i c unsigned long p a g a d d r ;
void ∗ p a g e m a p p e d a d d r e s s ;
void ∗ e s p p r o c e s s l o c a t i o n ; void ∗ e b p p r o c e s s l o c a t i o n ;
FILE ∗ l o g F i l e ;
StateGraph ∗ sGraph = NULL ;
CodeSegment ∗ i nf oC od eS eg m en t = NULL ; unsigned i n t c u r r e n t s t a t e = 0 ; unsigned i n t n e x t s t a t e ;
bool inCodeSegment ( unsigned i n t a d d r e s s ) { i f ( i nf oC od eS e gm en t == NULL) return f a l s e ;
i f ( ( a d d r e s s >= infoCodeSegment−>s t a r t a d d r e s s ) && ( a d d r e s s <=
infoCodeSegment−>e n d a d d r e s s ) ) return true ;
e l s e
return f a l s e ; }
void ∗ g e t R e t u r n A d d r e s s 2 ( ) {
96
PROT READ | PROT WRITE, KERNEL PGD) ; b a s e p o i n t e r k e r n e l c o n t e n t = ∗ ( u i n t 3 2 t ∗ )
97
98 PROT READ | PROT WRITE, PROCESS PGD) ;
b a s e p o i n t e r c o n t e n t = ∗ ( u i n t 3 2 t ∗ )
99
100 PROT READ | PROT WRITE, KERNEL PGD) ;
b a s e p o i n t e r k e r n e l c o n t e n t = ∗ ( u i n t 3 2 t ∗ )
101
102 PROT READ | PROT WRITE, PROCESS PGD) ;
b a s e p o i n t e r c o n t e n t = ∗ ( u i n t 3 2 t ∗ )
103
104
void ∗ u B l o c k i n i t i a l a d d r e s s m a p p e d = tv mapmem domu ( x c h a n d l e , 0 , domid , ( void ∗ ) ( ub . i n i t i a l a d d r e s s ) , PROT READ | PROT WRITE, PROCESS PGD) ;
unsigned char e n c r y p t e d C o d e [ ub . s i z e ] ;
105 PROT READ | PROT WRITE, PROCESS PGD) ;
unsigned char d e c r y p t e d C o d e [ ub . s i z e ] ;
106 PROT READ | PROT WRITE, PROCESS PGD) ;
unsigned char e n c r y p t e d C o d e [ ub . s i z e ] ;
107 PROT READ | PROT WRITE, PROCESS PGD) ;
unsigned char d e c r y p t e d C o d e [ ub . s i z e ] ;
108
109
void ∗ u B l o c k i n i t i a l a d d r e s s m a p p e d = tv mapmem domu ( x c h a n d l e , 0 , domid , ( void ∗ ) ( ub . i n i t i a l a d d r e s s ) , PROT READ | PROT WRITE, PROCESS PGD) ;
unsigned char e n c r y p t e d C o d e [ ub . s i z e ] ;
110
void ∗ u B l o c k i n i t i a l a d d r e s s m a p p e d = tv mapmem domu ( x c h a n d l e , 0 , domid , ( void ∗ ) ( ub . i n i t i a l a d d r e s s ) , PROT READ | PROT WRITE, PROCESS PGD) ;
unsigned char d e c r y p t e d C o d e [ ub . s i z e ] ;
111
c r y p t d e c r y p t S O T P ( ) ; }
E x i t P o i n t ∗ f i n d E x i t P o i n t ( S t a t e ∗ s t , unsigned i n t r e t a d d r e s s , void ∗ r e t a d d r e s s l o c a t i o n ) {
E x i t P o i n t ∗ e p t = ( s t −>e x i t P o i n t H T ) [ r e t a d d r e s s % 1 6 ] ;
i f ( e p t == NULL) return NULL ;
i f ( ept−>r e t u r n a d d r e s s == r e t a d d r e s s ) { i f ( ept−>r o o t r e t u r n a d d r e s s == 0 ) {
return e p t ; }
e l s e { /∗ r o o t r e t u r n a d d r e s s c h e c k ∗/ } }
e l s e {
while ( ept−>n e x t != NULL) { e p t = ept−>n e x t ;
i f ( ept−>r e t u r n a d d r e s s == r e t a d d r e s s ) { i f ( ept−>r o o t r e t u r n a d d r e s s == 0 ) {
return e p t ; }
e l s e { /∗ r o o t r e t u r n a d d r e s s c h e c k ∗/ } }
}
return NULL ; }
}
i n t main ( i n t a r g c , char ∗∗ a r g v ) {
s t r u c t x s h a n d l e ∗ x s ; /∗ x e n s t o r e h a n d l e r ∗/
112
113
114
115
116
117
118
memcpy ( p a g e m a p p e d a d d r e s s , &guard , s i z e o f ( i n t ) ) ; x c d o m a i n u n p a u s e ( x c h a n d l e , domid ) ;
#i f NO TLB
munmap page ( p a g e m a p p e d a d d r e s s ) ;
#endif }
x c e v t c h n u n m a s k ( x c e h a n d l e , l o c a l p o r t ) ; x c e v t c h n c l o s e ( x c e h a n d l e ) ;
x c i n t e r f a c e c l o s e ( x c h a n d l e ) ; return 0 ;
}
util.h
#include <math . h>
#include <u n i s t d . h>
#include < s t r i n g . h>
#include <s t d i o . h>
#include < s t d l i b . h>
#include < f c n t l . h>
#include < g l i b . h>
#include <s y s / t i m e . h>
#include <t i m e . h>
char ∗ t r i m ( char ∗ ) ;
unsigned i n t s t r i n g T o 3 2 b i t X I n t ( char ∗ ) ;
unsigned long s t r i n g T o l o n g ( char ∗ ) ;
119
util.c
#include ” u t i l . h”
char ∗ t r i m ( char ∗ s t r ) {
i f ( s t r == NULL) return NULL ; i n t s t r S i z e = s t r l e n ( s t r ) ; i f ( s t r S i z e == 0 ) return NULL ; i n t i = 0 ;
i n t i n i t W h i t e S p a c e s = 0 ; i n t endWhiteSpaces = 0 ; while ( i < s t r S i z e ) {
i f ( ( s t r [ i ] == ’ ’ ) | | ( s t r [ i ] == ’ ’ ) ) i n i t W h i t e S p a c e s ++;
e l s e break ; i ++;
}
i = s t r S i z e −1;
while ( i >=0){
i f ( ( s t r [ i ] == ’ ’ ) | | ( s t r [ i ] == ’ ’ ) ) endWhiteSpaces++;
e l s e break ; i −−;
}
i n t n e w S t r S i z e = s t r S i z e − ( i n i t W h i t e S p a c e s + endWhiteSpaces ) ; i f ( n e w S t r S i z e == 0 ) return NULL ;
char ∗ trimmedStr = m a l l o c ( ( n e w S t r S i z e +1) ∗ s i z e o f ( char ) ) ; i = 0 ;
while ( i < n e w S t r S i z e ) {
trimmedStr [ i ] = s t r [ i n i t W h i t e S p a c e s + i ] ; i ++;
120
121
122
123
124
d e f a u l t : // i l l e g a l v a l u e f r e e ( s t r ) ;
return 2 ; }
totNumber += number ∗ pow ( 1 0 , c y c l e ) ; }
f r e e ( s t r ) ;
return totNumber ; }
parse graph.h
#include ” u t i l . h”
typedef s t r u c t {
unsigned i n t r e t u r n a d d r e s s ; unsigned i n t r o o t r e t u r n a d d r e s s ; unsigned i n t n e x t s t a t e i d ;
void ∗ n e x t ; // E x i t P o i n t ∗ } E x i t P o i n t ;
typedef s t r u c t { E x i t P o i n t ∗ ep ;
void ∗ n e x t ; // E x i t P o i n t R e f ∗ } E x i t P o i n t R e f ;
typedef s t r u c t { E x i t P o i n t ∗ head ; E x i t P o i n t ∗ c u r r e n t ;
125
126
typedef s t r u c t {
S t a t e ∗ s t a t e V e c t o r ; unsigned i n t l e n g t h ; } StateGraph ;
typedef s t r u c t { S t a t e L i s t ∗ s t a t e L ; g c h a r ∗ l a s t t a g ; } ParserGraph ;
void p a r s i n g g e t e l e m e n t ( GMarkupParseContext ∗ , const g c h a r ∗ , const g c h a r ∗ ∗ , const g c h a r ∗ ∗ , g p o i n t e r , GError ∗ ∗ ) ;
void p a r s i n g g e t v a l u e ( GMarkupParseContext ∗ , const g c h a r ∗ , g s i z e , g p o i n t e r , GError ∗ ∗ ) ;
void d e s t r o y p a r s e r ( g p o i n t e r ) ;
StateGraph ∗ p a r s e g r a p h ( char ∗ ) ;
StateGraph ∗ setupGraph ( S t a t e L i s t ∗ ) ;
void p r i n t S t a t e L i s t ( S t a t e L i s t ∗ ) ;
parse graph.c
#include ” p a r s e g r a p h . h”
127
128
129
130
131
132
GMarkupParser p a r s e r ;
GMarkupParseContext ∗ c o n t e x t ; ParserGraph b u f f e r ;
b u f f e r . s t a t e L = NULL ;
memset ( &p a r s e r , 0 , s i z e o f ( GMarkupParser ) ) ; p a r s e r . s t a r t e l e m e n t = p a r s i n g g e t e l e m e n t ; p a r s e r . e n d e l e m e n t = NULL ;
p a r s e r . t e x t = p a r s i n g g e t v a l u e ;
memset ( &b u f f e r , 0 , s i z e o f ( ParserGraph ) ) ;
c o n t e x t = g m a r k u p p a r s e c o n t e x t n e w ( &p a r s e r , 0 , &b u f f e r , d e s t r o y p a r s e r ) ;
i n t num = 0 ;
i n t b u f f S i z e T e m p = 1 0 0 ;
char ∗ tempBuf = m a l l o c ( b u f f S i z e T e m p ∗ s i z e o f ( char ) ) ; i n t f d = open ( f i l e , O RDONLY) ;
i f ( f d == −1) return NULL ;
while ( ( num = r e a d ( fd , tempBuf , b u f f S i z e T e m p ) ) > 0 ) {
g m a r k u p p a r s e c o n t e x t p a r s e ( c o n t e x t , tempBuf , num , NULL )
; }
g m a r k u p p a r s e c o n t e x t e n d p a r s e ( c o n t e x t , NULL ) ; g m a r k u p p a r s e c o n t e x t f r e e ( c o n t e x t ) ;
return setupGraph ( b u f f e r . s t a t e L ) ; }
void p r i n t S t a t e L i s t ( S t a t e L i s t ∗ s l ) {
133
134
135
136
137
typedef s t r u c t { unsigned i n t i d ;
unsigned i n t i n i t i a l a d d r e s s ; unsigned i n t s i z e ;
void ∗ n e x t ; // UnitBlockTemp } UnitBlockTemp ;
typedef s t r u c t {
unsigned i n t s t a r t a d d r e s s ; unsigned i n t e n d a d d r e s s ;
unsigned i n t e n c r y p t i o n m e t h o d ; UnitBlockTemp ∗ head ;
UnitBlockTemp ∗ c u r r e n t ; unsigned i n t l e n g t h ; } CodeSegmentUnitBlockList ;
typedef s t r u c t {
C o d e S e g m e n t U n i t B l o c k L i s t ∗ c o d e S e g ; g c h a r ∗ l a s t t a g ;
} P a r s e r C o S e g ;
void g e t e l e m e n t C o S e g ( GMarkupParseContext ∗ , const g c h a r ∗ , const g c h a r ∗ ∗ , const g c h a r ∗ ∗ , g p o i n t e r , GError ∗ ∗ ) ;
void g e t v a l u e C o S e g ( GMarkupParseContext ∗ , const g c h a r ∗ , g s i z e , g p o i n t e r , GError ∗ ∗ ) ;
void d e s t r o y p a r s e r C o S e g ( g p o i n t e r ) ;
138
CodeSegment ∗ p a r s e C o S e g ( char ∗ ) ;
void getKeysDES ( CodeSegment ∗ ) ;
void getKeysBF ( CodeSegment ∗ ) ;
void getKeysSOTP ( CodeSegment ∗ ) ;
CodeSegment ∗ s e t u p C o d e S e g m e n t B l o c k s ( C o d e S e g m e n t U n i t B l o c k L i s t ∗ ) ;
void p r i n t C o d e S e g m e n t B l o c k s ( CodeSegment ∗ ) ;
parse code.c
#include ” p a r s e c o d e . h”
void g e t e l e m e n t C o S e g ( GMarkupParseContext ∗ c o n t e x t , const g c h a r ∗ element name ,
const g c h a r ∗∗ a t t r i b u t e n a m e s , const g c h a r ∗∗
a t t r i b u t e v a l u e s ,
g p o i n t e r u s e r d a t a , GError ∗∗ e r r o r ) {
P a r s e r C o S e g ∗ b u f f e r ;
b u f f e r = ( P a r s e r C o S e g ∗ ) u s e r d a t a ; i f ( b u f f e r −>l a s t t a g != NULL )
f r e e ( b u f f e r −>l a s t t a g ) ;
b u f f e r −>l a s t t a g = s t r d u p ( element name ) ;
i f ( strcmp ( element name , ” c o d e s e g m e n t ” ) == 0 ) {
139
140
141
142
b u f f e r . c o d e S e g = NULL ;
memset ( &p a r s e r , 0 , s i z e o f ( GMarkupParser ) ) ; p a r s e r . s t a r t e l e m e n t = g e t e l e m e n t C o S e g ;
p a r s e r . e n d e l e m e n t = NULL ; p a r s e r . t e x t = g e t v a l u e C o S e g ;
memset ( &b u f f e r , 0 , s i z e o f ( P a r s e r C o S e g ) ) ;
c o n t e x t = g m a r k u p p a r s e c o n t e x t n e w ( &p a r s e r , 0 , &b u f f e r , d e s t r o y p a r s e r C o S e g ) ;
i n t num = 0 ;
i n t b u f f S i z e T e m p = 1 0 0 ;
char ∗ tempBuf = m a l l o c ( b u f f S i z e T e m p ∗ s i z e o f ( char ) ) ; i n t f d = open ( f i l e , O RDONLY) ;
i f ( f d == −1) return NULL ;
while ( ( num = r e a d ( fd , tempBuf , b u f f S i z e T e m p ) ) > 0 ) {
g m a r k u p p a r s e c o n t e x t p a r s e ( c o n t e x t , tempBuf , num , NULL )
; }
g m a r k u p p a r s e c o n t e x t e n d p a r s e ( c o n t e x t , NULL ) ; g m a r k u p p a r s e c o n t e x t f r e e ( c o n t e x t ) ;
f r e e ( tempBuf ) ;
return s e t u p C o d e S e g m e n t B l o c k s ( b u f f e r . c o d e S e g ) ; }
void getKeysDES ( CodeSegment ∗ c s ) { i n t f d ;
i n t keysNumber = c s −>uBlockNumber ; DES cblock key ;
143
i n t keysNumber = c s −>uBlockNumber ; unsigned char k e y d a t a [ 1 7 ] ;
BF KEY∗ key ;
144
void getKeysSOTP ( CodeSegment ∗ c s ) {
i n t f d ;
i n t keysNumber = c s −>uBlockNumber ;
145
146
147
c s −>u B l o c k V e c t o r = ub ;
f r e e ( c s u b ) ;
i f ( c s −>e n c r y p t i o n m e t h o d == DES) { getKeysDES ( c s ) ;
}
e l s e i f ( c s −>e n c r y p t i o n m e t h o d == BLOWFISH) { getKeysBF ( c s ) ;
}
e l s e i f ( c s −>e n c r y p t i o n m e t h o d == SOTP) { getKeysSOTP ( c s ) ;
}
return c s ; }
void d e s t r o y p a r s e r C o S e g ( g p o i n t e r d a t a ) { P a r s e r C o S e g ∗ b u f f e r ;
b u f f e r = ( P a r s e r C o S e g ∗ ) d a t a ; i f ( b u f f e r −>l a s t t a g != NULL )
f r e e ( b u f f e r −>l a s t t a g ) ; }
void p r i n t C o d e S e g m e n t B l o c k s ( CodeSegment ∗ c s ) { i f ( c s == NULL) return ;
p r i n t f ( ” s t a r t a d d r e s s = %x\n” , c s −>s t a r t a d d r e s s ) ; p r i n t f ( ” e n d a d d r e s s = %x\n” , c s −>e n d a d d r e s s ) ;
p r i n t f ( ” e n c r y p t i o n m e t h o d = %d\n” , c s −>e n c r y p t i o n m e t h o d ) ; p r i n t f ( ” B l o c k s number = %d\n” , c s −>uBlockNumber ) ;
U n i t B l o c k ∗ ub = c s −>u B l o c k V e c t o r ; i n t i = 0 ;
148
f o r ( i = 0 ; i < c s −>uBlockNumber ; i ++){
unsigned i n t pos = i ;
p r i n t f ( ” B l o c k i d = %d\n” , ( ub+pos )−>i d ) ;
p r i n t f ( ” i n i t i a l a d d r e s s = %x\n” , ( ub+pos )−>
i n i t i a l a d d r e s s ) ;
p r i n t f ( ” s i z e = %x\n” , ( ub+pos )−> s i z e ) ;
p r i n t f ( ” i s C r y p t e d = %d\n” , ( ub+pos )−>i s C r y p t e d ) ; }
}
keys generator.c
#include <s y s / t y p e s . h>
#include <s y s / s t a t . h>
#include < f c n t l . h>
#include <u n i s t d . h>
#include < s t r i n g . h>
#include <o p e n s s l / d e s . h>
#include <o p e n s s l / b l o w f i s h . h>
#include <o p e n s s l / rand . h>
void genDesKeys ( i n t num) { DES cblock key ;
DES cblock i v e c ;
i n t f d= open ( ” k e y s . k” , O WRONLY| O CREAT | O TRUNC, 0 6 6 6 ) ; i f ( f d == −1){
p r i n t f ( ” E r r o r c r e a t i n g k e y s . k . \ n” ) ; e x i t ( 1 ) ;
149
150
e x i t ( 1 ) ; }
i n t i = 0 ;
f o r ( i = 0 ; i < num ; i ++){
RAND bytes ( k e y d a t a , 1 2 ) ; w r i t e ( fd , k e y d a t a , 1 2 ) ; }
c l o s e ( f d ) ; }
/∗
Takes a s f i r s t argument t h e t y p e o f e n c r y p t i o n , a s s e c o n d argument t h e number o f k e y s ( and i v ) t o c r e a t e , and a s t h i r d argument a s t r i n g t o s e e d t h e random number g e n e r a t o r
∗/
i n t main ( i n t a r g c , char ∗ a r g v [ ] ) {
i n t numberOfKeys = 0 ; char ∗ e n c r y p t i o n t y p e ; char ∗ s t r ;
i f ( a r g c != 4 ) {
p r i n t f ( ”Number o f arguments n o t v a l i d ! \ n” ) ; e x i t ( 1 ) ;
}
e n c r y p t i o n t y p e = a r g v [ 1 ] ; numberOfKeys = a t o i ( a r g v [ 2 ] ) ;
i f ( numberOfKeys == 0 ) {
p r i n t f ( ” I n v a l i d number o f k e y s . ” ) ; return ( 1 ) ;
151
}
s t r = a r g v [ 3 ] ;
RAND seed ( s t r , s t r l e n ( s t r ) ) ;
i f ( strcmp ( e n c r y p t i o n t y p e , ”DES” ) == 0 ) genDesKeys ( numberOfKeys ) ;
e l s e i f ( strcmp ( e n c r y p t i o n t y p e , ”BF” ) == 0 ) genBFKeys ( numberOfKeys ) ;
e l s e i f ( strcmp ( e n c r y p t i o n t y p e , ”SOTP” ) == 0 ) genSOTPKeys ( numberOfKeys ) ;
e l s e
p r i n t f ( ”The t y p e o f e n c r y p t i o n s p e c i f i e d i s n o t v a l i d ! \ n” ) ; return 0 ;
}
elf encryptor.c
#include ” p a r s e c o d e . h”
i n t f d E l f F i l e ; char ∗ e l f F i l e ;
CodeSegment ∗ in f oC od eS eg m en t = NULL ;
i n t g e t C S O f f s e t ( char ∗ s t r ) { i n t strNumber = 0 ;
char s t r C S O f f s e t [ 1 0 ] ; i n t guard = TRUE;
i n t i = 0 ;
while ( guard ) {
152 unsigned i n t uBVecLen = infoCodeSegment−>uBlockNumber ; char f i l e C o d e E n c r y p t N a m e [ 3 0 0 ] ;
153
154
void encWithBF ( i n t t e x t S e g m e n t O f f s e t ) {
U n i t B l o c k ∗ uBlockVec = infoCodeSegment−>u B l o c k V e c t o r ; unsigned i n t uBVecLen = infoCodeSegment−>uBlockNumber ; char f i l e C o d e E n c r y p t N a m e [ 3 0 0 ] ;
155
156 unsigned i n t uBVecLen = infoCodeSegment−>uBlockNumber ; char f i l e C o d e E n c r y p t N a m e [ 3 0 0 ] ;
157
158
fdTmpFile= open ( tmpFile , O RDONLY) ; i f ( fdTmpFile == −1){
159
e x i t ( 1 ) ; }
t e x t S e g m e n t O f f s e t = g e t C S O f f s e t ( s t r ) ; s p r i n t f ( cmd , ”rm %s ” , t m p F i l e ) ;
syste m ( cmd ) ;
p r i n t f ( ” o f f s e t = %d\n” , t e x t S e g m e n t O f f s e t ) ; e n c r y p t E l f F i l e ( t e x t S e g m e n t O f f s e t ) ;
c l o s e ( f d E l f F i l e ) ; return ( 0 ) ;
}
Elenco delle figure
2.1 Compilazione vs Reverse Engineering . . . 8
2.2 (a) Codice originale. (b) Codice che utilizza una branch function. 10 2.3 (a) Codice originale. (b) Codice con trasformazioni if-then-goto. 14 2.4 Control flow appiattito sostituento i goto con uno switch. . . . 15
3.1 (a) unhosted vmm. (b) hosted vmm. . . 23
4.1 Esempio 1: rappresentazione del control flow graph . . . 42
4.2 Esempio 1: grafo degli state block . . . 44
4.3 Esempio 2: rappresentazione del control flow graph . . . 45
4.4 Esempio 2: grafo degli state block . . . 46
4.5 Esempio 3: rappresentazione del control flow graph . . . 47
4.6 Esempio 3: grafo degli state block . . . 48
5.1 Divisione del controllo di flusso . . . 50
5.2 Esempio di indeterminazione nel caso venga utilizzato il tipo di una chiamata di sistema come discriminante nel passaggio tra state block . . . 54
5.3 Architettura . . . 59
ELENCO DELLE FIGURE 161
5.4 Ricerca del return address . . . 65 6.1 (1) l’overhead attuale. (2) l’overhead se la libreria permettesse
di riutilizzare gli indirizzi delle pagine gi`a mappate . . . 89
Bibliografia
[BDF+03] Paul R. Barham, Boris Dragovic, Keir A. Fraser, Steven M.
Hand, Timothy L. Harris, Alex C. Ho, Evangelos Kotsovinos, Anil V. S. Madhavapeddy, Rolf Neugebauer, Ian A. Pratt, and Andrew K. Warfield. Xen 2002. Technical Report UCAM-CL-TR-553, University of Cambridge, Computer Laboratory, January 2003.
[BMST09] Fabrizio Baiardi, Dario Maggiari, Daniele Sgandurra, and Fran-cesco Tamberi. Psycotrace: Virtual and transparent monitoring of a process self. In Didier El Baz, Fran¸cois Spies, and Tom Gross, editors, PDP, pages 393–397. IEEE Computer Society, 2009.
[CTL97] Christian Collberg, Clark Thomborson, and Douglas Low. A taxonomy of obfuscating transformations. Technical Report 148, Department of Computer Sciences, The University of Auckland, July 1997.
[CTL98] Christian S. Collberg, Clark D. Thomborson, and Douglas Low.
Manufacturing cheap, resilient, and stealthy opaque constructs.
In POPL, pages 184–196, 1998.
BIBLIOGRAFIA 163
[KAH07] Prasad Kulkarni, Matthew Arnold, and Michael Hind. Dynamic compilation: the benefits of early investing. In Chandra Krintz, Steven Hand, and David Tarditi, editors, VEE, pages 94–104.
ACM, 2007.
[Lan92] William Landi. Undecidability of static analysis. LOPLAS, 1(4):323–337, 1992.
[LD03] Linn and Debray. Obfuscation of executable code to improve re-sistance to static disassembly. In SIGSAC: 10th ACM Conferen-ce on Computer and Communications Security. ACM SIGSAC, 2003.
[Mye81] E. W. Jr. Myers. A precise inter-procedural data flow algorithm.
In Conference record of the 8th ACM Symposium on Principles of Programming Languages (POPL), pages 219–230, 1981.
[SCK+93] R. L. Sites, A. Chernoff, M. B. Kirk, M. P. Marks, and S. G.
Robinson. Binary translation. Communications of the ACM, 36(2):69–81, February 1993.
[WHKD00] Chenxi Wang, Jonathan Hill, John Knight, and Jack Davidson.
Software tamper resistance: Obstructing static analysis of pro-grams. Technical Report CS-2000-12, Department of Computer Science, University of Virginia, May 12 2000. Tue, 30 May 2000 16:51:20 GMT.