Επόμενο Προηγούμενο Περιεχόμενα
Γλώσσες όπως η Java και η C# που χρησιμοποιούν ενδιάμεσες
μορφές κώδικα ως το βασικό μέσο μεταφοράς τους,
εμφανίζουν μια νέα πρόκληση για το RCE. Οι ενδιάμεσες
μορφές μεταφέρουν αναπόφευκτα πολλές πληροφορίες για τον
πηγαίο κώδικα και έτσι κάποιος θα μπορούσε να θεωρήσει
πως είναι πιο εύκολο να τον ανασυνθέσουμε. Και αυτό
είναι, όντως, αλήθεια.
Έπρεπε, λοιπόν, να βρεθεί ένας διαφορετικός τρόπος για
προστασία του κώδικα από τα αδιάκριτα μάτια. Αυτό που
έγινε, τελικά, είναι να δοθεί περισσότερο βάρος στην
παλιά τεχνική του code obfuscation. Οι ίδιες βασικές
αρχές παρέμειναν αλλά προσαρμόστηκαν στη νέα
πραγματικότητα του αντικειμενοστρεφούς μοντέλου.
Σκοπός του obfuscation είναι να μετασχηματίσει ένα
πρόγραμμα σε ένα άλλο, ισοδύναμο του, τέτοιο ώστε να
είναι πιο δύσκολο να κατανοηθεί από ανθρώπους. Επιπλέον
χρειάζεται ο μετασχηματισμός αυτός να είναι δύσκολα
αντιστρεπτός από κάποιο άλλο αυτόματο εργαλείο
(deobfuscator). Στο παιχνίδι παίζουν ρόλο πολλοί
αντικρουόμενοι παράγοντες και έτσι πρέπει να βρεθεί μια
ικανοποιητική λύση, ανάλογα με τις ανάγκες του χρήστη.
Για παράδειγμα, αύξηση της πολυπλοκότητας του κώδικα
μπορεί να επιφέρει δραματική αλλαγή στην ταχύτητα
εκτέλεσης, οπότε πρέπει να αποφασιστεί τι έχει μεγαλύτερη
σημασία, η απόδοση ή η προστασία.
Ο πιο απλός τρόπος obfuscation ενός προγράμματος είναι η
μετονομασία των συμβόλων του (μεταβλητές, συναρτήσεις
κτλ) σε ακατανόητες συμβολοσειρές. Άλλο είναι να βλέπεις
μια συνάρτηση "CheckUser" και άλλο αυτή να λέγεται
"mvkof89". Πάντως, αν και έτσι δυσχεραίνονται οι RCE
προσπάθειες, η κατάσταση δεν είναι τόσο άσχημη.
Το επόμενο βήμα είναι το λεγόμενο control-flow
obfuscation. Σκοπός αυτής της τεχνικής είναι να
μπερδευτεί τόσο πολύ η ροή του προγράμματος ώστε να είναι
δύσκολο να την ακολουθήσει κάποιος. Για παράδειγμα το
απλό κομμάτι κώδικα:
printf("OK");
μπορεί να μετασχηματιστεί στο ισοδύναμο:
y=72;
...
x=random();
if ( (x*13) % 5 < 3) {
doit:
if (y > 61)
printf("OK");
else
printf("KUKU");
}
else {
if (y * 2 - 6 == 138)
goto doit;
printf("KUKU");
}
Φανταστείτε τι σύγχυση μπορεί να προκληθεί σε επίπεδο bytecodes (η και γλώσσα
μηχανής)! Από τη μεριά τους, οι deobfuscators προσπαθούν να αντιμετωπίσουν τη μέθοδο
αυτή χρησιμοποιώντας αυτόματες τεχνικές για την απόδειξη θεωρημάτων. Για παράδειγμα,
βλέποντας πως το y είναι 72 ξέρουν πως πάντα ισχύει y>61 και επομένως το
printf("KUKU") δεν πρόκειται να εκτελεστεί ποτέ.
Για επιπλέον αύξηση του χάους σε ένα πρόγραμμα, μπορεί να
εφαρμοστεί η τεχνική του data obfuscation. Εδώ στο
στόχαστρο βρίσκονται πλέον τα δεδομένα του προγράμματος
τα οποία σπάνε, συγχωνεύονται, ψευδο-κρυπτογραφούνται και
γενικώς υπόκεινται σε ένα σωρό μετασχηματισμούς. Ένα απλό
παράδειγμα:
;; Έστω a ένας πίνακας 20 στοιχείων
x = 0;
for(i = 0; i < 20; i++) {
x += a[i];
}
if (x == 10)
printf("Ok!");
else
printf("Kuku!");
;; Έστω a ένας πίνακας 20 στοιχείων
x = 100;
for(i = 0; i <20; i++) {
x += 2 * a[i] + i;
}
if (x == 310)
printf("Ok!");
else
printf("Kuku!");
Υπάρχουν πολλές ακόμη κατηγορίες obfuscating μετασχηματισμών και ένας σωστός
συνδυασμός τους καθιστά την κατάσταση πολύ δύσκολη για τον reverse engineer.
Στο μέλλον προβλέπω (κοιτώντας τη κρυστάλλινη σφαίρα :) )
πως το παιχνίδι του RCE σε ένα μεγάλο βαθμό θα έχει
μετατραπεί σε ένα παιχνίδι obfuscation/deobfuscation. Εδώ
και καιρό υπάρχουν εργαλεία για obfuscation ενώ τον
τελευταίο καιρό εμφανίζονται πρωτότυποι deobfuscators
βασισμένοι σε σχετικές δημοσιεύσεις. Ας αρχίσουν οι
χοροί...
Επόμενο Προηγούμενο Περιεχόμενα