Προς το περιεχόμενο

self-deleting in C


yincyun

Προτεινόμενες αναρτήσεις

Ναι, αλλά όχι με cross-platform μέθοδο.

 

Σε linux μπορείς απ' ότι ξέρω απλά να κάνεις unlink() τον εαυτό σου ακόμα και όσο τρέχεις, no problem there.

 

Σε Windows υπάρχουν διάφορες τεχνικές, λιγότερο ή περισσότερο περίπλοκες -- οι καλές όμως είναι οι περισσότερο περίπλοκες. Μία σίγουρη που γνωρίζω έχει να κάνει με την εκμετάλλευση της FILE_FLAG_DELETE_ON_CLOSE στην CreateFile, αλλά θέλει δουλειά. Η ιδέα είναι πως το .exe σου θα περιέχει ένα άλλο μικρό .exe του οποίου η δουλειά είναι να σβήσει το πρώτο. Το δεύτερο .exe με τη σειρά του θα σβηστεί αυτόματα από τα Windows, κάτι που δε μπορεί να γίνει με το πρώτο γιατί εκεί δεν ελέγχεις το πώς θα ανοιχτεί το εκτελέσιμο (το κάνει ο Windows Explorer ας πούμε). Μπορείς να δεις λεπτομέρειες αλλά και εναλλακτικές εδώ.

  • Like 4
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

2001

http://www.catch22.net/tuts/flicker-free-drawing

 

double buffering

 

 

This method is a little slow, because the offscreen memory-DC is created from scratch every time the window needs to be drawn. A more efficient method would be to create the memory DC only once, big enough so that the entire window can be painted at any time. When the application terminates, the memory DC would then be destroyed. Both these methods are potentially quite memory-intensive, especially if the memory DC needs to be the size of a screen (1024 * 768 * 32 bytes=2.5 Mb).

 

 

Προβληματα της εποχης χεχεχε

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Σε linux μπορείς απ' ότι ξέρω απλά να κάνεις unlink() τον εαυτό σου ακόμα και όσο τρέχεις, no problem there.

Ενδιαφέρουσα λεπτομέρεια, ο κόσμος του LINUX δεν παύει να με εκπλήσσει :-D

Σε Windows υπάρχουν διάφορες τεχνικές[..]

Αν εξαιρέσουμε μερικά βρόμικα (αλλά εξίσου ενδιαφέροντα) κόλπα των παλαιοτέρων εκδόσεων, υπάρχει πάντα η ανάγκη να αναθέσουμε (έμμεσα ή άμεσα) την δουλειά σε κάποιον άλλο (όταν έγραψα την αρχική απάντηση το μυαλό μου ήταν στην RUNDLL32) - καλύτερη λύση εκείνη του LINUX..
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Μετά από - πολύ - ψάξιμο έφτασα εδώ και μπορώ να πω είναι λειτουργικός κώδικας του Napalm. Δεν καταλαβαίνω ακριβώς τι κάνει αλλά δουλεύει.. :)

Με μια γρήγορη ματιά, φορτώνει μια νέα διεργασία του EXPLORER.EXE (που υπάρχει σε όλες τις εκδόσεις των Windows) και δημιουργεί σε αυτή ένα νέο "εξωτερικό" Thread το οποίο ανήκει μεν στο EXPLORER.EXE αλλά έχει δημιουργηθεί από το προς διαγραφή πρόγραμμα (ανήκει και σε αυτό ας πούμε), από εκεί και πέρα, στο νέο Thread που ανήκει τεχνικά (και) στον EXPLORER.EXE, περνά την εντολή WaitForSingleObject η οποία περιμένει πότε θα ολοκληρωθεί το προς διαγραφή πρόγραμμα, ως τότε το νέο Thread απλά περιμένει, ύστερα μόλις το πρόγραμμα τελειώσει την εκτέλεση του, το Thread απελευθερώνεται και διαγράφει το αποφορτωμένο πλέον εκτελέσιμο.. (φυσικά κάνει και άλλα πράγματα - φορτώνει την διεύθυνση των εντολών που χρειάζεται να γράψει στο Thread από το KERNEL32.DLL, δεσμεύει μια σελίδα με δικαιώματα εκτέλεσης εντολών για το remote-Thread, περιλαμβάνει εντολές για να κλείσει ο EXPLORER.EXE κλπ).

 

Γενικά, ωραίο hack (-τα έδωσε πριν ο Defacer) που με έμμεσο τρόπο αναθέτει την διαγραφή σε ένα άλλο "αθώο" λογισμικό.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Ενδιαφέρουσα λεπτομέρεια, ο κόσμος του LINUX δεν παύει να με εκπλήσσει :-DΑν εξαιρέσουμε μερικά βρόμικα (αλλά εξίσου ενδιαφέροντα) κόλπα των παλαιοτέρων εκδόσεων, υπάρχει πάντα η ανάγκη να αναθέσουμε (έμμεσα ή άμεσα) την δουλειά σε κάποιον άλλο (όταν έγραψα την αρχική απάντηση το μυαλό μου ήταν στην RUNDLL32) - καλύτερη λύση εκείνη του LINUX..

 

Η πλάκα είναι πως δεν ξέρω τι ακριβώς κάνει το linux σ' αυτή την περίπτωση για να μπορεί να το επιτρέψει αυτό. Τι εννοώ: ο λόγος που δε μπορείς να διαγράψεις loaded executable image είναι πως ο loader των Windows κάνει map τα περιεχόμενα του image στο address space της εφαρμογής αλλά δεν τα φορτώνει βέβαια επιτόπου από το δίσκο. Ο κανονικός μηχανισμός της διαχείρισης virtual memory απο κει και πέρα επαρκεί (πας να κάνεις access μια page που δεν έχει φορτωθεί => page fault => το λειτουργικό φορτώνει από το δίσκο στη μνήμη => η εφαρμογή συνεχίζει σα να μην τρέχει τίποτα).

 

Το πρόβλημα λοιπόν είναι πως αν διαγραφεί το image τότε σε περίπτωση τέτοιου page fault δεν μπορείς να κάνεις τίποτα παρα μόνο ένα "η εφαρμογή παρουσίασε σφάλμα μπλα μπλα και τη σκότωσα" (αντιθέτως δεν υπάρχει κανένα πρόβλημα να μετονομάσεις ή να μετακινήσεις το αρχείο, αυτά είναι λεπτομέρειες του filesystem που δεν έχουν σημασία).

 

Άρα το Linux τι κάνει σ' αυτή την περίπτωση; Απλά segfault ή κάτι ανάλογο; Δεν ξέρω, αλλά ακούγεται μέσα στο πνεύμα.

 

Μετά από - πολύ - ψάξιμο έφτασα εδώ και μπορώ να πω είναι λειτουργικός κώδικας του Napalm. Δεν καταλαβαίνω ακριβώς τι κάνει αλλά δουλεύει.. :)

 

Πάνω κάτω αυτό που λέει ο DX κάνει.

 

Πιο συγκεκριμένα, πρώτα (γραμμές 44-51) δημιουργεί ένα νέο process (επιλέγει τον explorer.exe, αλλά θα μπορούσε να επιλέξει και οποιοδήποτε άλλο) χωρίς παράθυρα και suspended (δηλαδή δεν τρέχει ο κώδικάς του, απλά το δημιουργεί παγωμένο -- και στη συνέχεια δεν το ξεπαγώνει ποτέ βέβαια, γιατί δε θέλουμε να τρέξει ο κώδικάς του).

 

Στη συνέχεια (52-55) κάνει allocate μνήμη στο address space του νέου process στην οποία υπάρχουν δικαιώματα write (γιατί στη συνέχεια θα γράψουμε στη μνήμη αυτή) και read/execute (γιατί η νέα process στην ακόμα πιο συνέχεια θα εκτελέσει αυτό που γράψαμε).

 

Μετά (57-72) μαζεύει στη μνήμη του δικού σου process τα δεδομένα που θα μας χρειαστούν για να τα γράψουμε στη μνήμη του άλλου process. Αυτά περιλαμβάνουν τις διευθύνσεις κάποιων WinAPI functions που θα κληθούν καθώς και κάποια handles). Όλα πακετάρονται μέσα στη μεταβλητή local.

 

Μετά (75-76) γράφει στη μνήμη του νέου process τον κώδικα που θέλουμε να εκτελέσουμε (αυτός βρίσκεται στη function RemoteThread, η οποία δεν τρέχει ποτέ στο δικό μας process, μόνο στο νέο) και 128 bytes (γιατί "είναι αρκετά™" και είναι μια ωραία στρόγγυλη δύναμη του 2, για να μην έχουμε alignment issues) παραπέρα τα περιεχόμενα της local που λέγαμε.

 

Τέλος, καλώντας την CreateRemoteThread ξεκινά ένα νέο thread στο νέο process δίνοντάς του τη διεύθυνση μνήμης του κώδικα που πρέπει να τρέξει και τη διεύθυνση μνήμης της παραμέτρου της function που υλοποιεί το thread αυτό (η παράμετρος είναι ακριβώς μία και είναι pointer, δηλαδή διεύθυνση μνήμης, επειδή όπως βλέπουμε στην CreateRemoteThread είναι τύπου LPTHREAD_START_ROUTINE, δηλαδή έχει αυτό το signature -- καθόλου τυχαία σχεδόν το ίδιο με τη RemoteThread στον κώδικα του Napalm).

 

Δεν αναφέρθηκα καθόλου, αλλά είναι σημαντικό οπότε το λέω τώρα, στο #pragma check_stack. Είναι απαραίτητο γιατί αν δεν απενεργοποιηθεί μ' αυτό τον τρόπο το stack probe που γράφει ο compiler για τη συνάρτηση RemoteThread υπάρχει περίπτωση (αν και δεν το βλέπω να γίνεται από τον κώδικα) το stack probe να σου προκαλέσει ανεπανόρθωτο page fault με το που θα πάει να τρέξει η συνάρτηση (στο νέο process). Extra bonus ειρωνίας επειδή το stack probe έχει σα μοναδικό σκοπό στη ζωή του... το να μη φας ανεπανόρθωτο page fault όταν πάει να τρέξει η συνάρτηση.

 

 

 

Stack probe = ο compiler δημιουργεί κώδικα που πάει και "ακουμπάει" μία μία με τη σειρά όλες τις memory pages στις οποίες εκτείνεται το stack της συνάρτησης που ετοιμάζεται να τρέξει. Το κάνει αυτό γιατί συνήθως η μνήμη στο stack αποτελείται από μια σειρά committed pages (που χρησιμοποιούνται ήδη), μία "guard page" και ακολουθούν unallocated pages. Όποτε ακουμπάς την guard το λειτουργικό την κάνει commit και κάνει allocate την επόμενη (που τώρα γίνεται guard), επομένως αν τις ακουμπάς με τη σειρά φτάνεις μέχρι stack overflow. Αν όμως ακουμπήσεις κατευθείαν "μια page μετά", τότε unrecoverable page fault και μπουμ.

 

Με άλλα λόγια, το stack probe εμποδίζει το crash and burn εδώ:

 

function foo() {
    // assume page size == 4096 bytes, sizeof(char) == 1
    char c[10000];
    c[9000] = 0; // crash and burn
}

 

Δοκιμάστε να βάλετε #pragma check_stack off γύρω από αυτή και καλέστε την να δούμε τι θα γίνει. Δεν το δοκίμασα αλλά η θεωρία λέει πυροτεχνήματα.

 

 

 

Γενικά μπορώ να πω ότι ο κώδικας του Napalm είναι προσεγμένος και φαίνεται πως προέρχεται από κάποιον που ξέρει τι κάνει.

 

Αλλά δεν είναι όλα όμορφα στο στρουμφοχωριό.

 

Προσωπικά δε θα επέλεγα κάτι τέτοιο γιατί

  1. Δεν υπάρχει λόγος. Αντί να ανέβεις λαθρεπιβάτης στο explorer.exe απλά ξεκίνα ένα νέο δικό σου process, δεν είναι τόσο δύσκολο. Βασικά είναι πολύ ευκολότερο. Αλλά δεν είναι αρκετά ποζέρικο και δεν είναι κάτι που μπορείς απλά να κάνεις copy paste σε source code.
  2. Όποιος ανακατεύεται με τα πίτουρα τον τρώνε eventually οι κότες.

 

Αυτά γενικά. Στην προκειμένη περίπτωση οι κότες είναι ανησυχητικά κοντά, ονομάζονται ASLR και είναι το τέλειο παράδειγμα για να κάνω το point μου μιας και όταν ο Napalm έγραψε τον κώδικα δεν υπήρχε ASLR στα Windows. Δηλαδή δεν υπήρχαν κότες. Αλλά φίλε, αν πας στα πίτουρα να ξέρεις ότι κάποια μέρα μπορεί να έρθουν ξαφνικά.

 

Όποιος εξηγήσει ποιό είναι το εν δυνάμει πρόβλημα λόγω ASLR κερδίζει τα σέβη μου. :-)

  • Like 2
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Πιστεύω πως προσωπικά με κάλυψε σε μεγάλο βαθμό με το απλά να το δω να γίνετε..! Και προφανώς μου έδωσε νέα πράγματα να ψαχουλέψω γιατί πλέον έχω ένα πάτημα σε αυτό που θέλω να κάνω..! Εννοείται πως θα τσεκάρω κάθε εντολή και θενξ παιδιά για τα τιπς! Σιγά σιγά μαθαίνουμε :)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

[..]Άρα το Linux τι κάνει σ' αυτή την περίπτωση;[..]

Γενικά στο LINUX υπάρχουν διάφορα ιδιόρρυθμα θέματα και καθώς δεν έχω ασχοληθεί ιδιαίτερα μαζί του απλά εντυπωσιάζομαι άλλοτε θετικά άλλοτε αρνητικά, για παράδειγμα το γνωστό πρόγραμμα συμπίεσης UPX σε LINUX δεν μπορεί να κάνει αποσυμπίεση στην μνήμη οπότε αποσυμπιέζει στον κατάλογο TMP και εκτελεί από εκεί το αποσυμπιεσμένο πρόγραμμα..

[..]Όποιος εξηγήσει ποιό είναι το εν δυνάμει πρόβλημα λόγω ASLR[..]

Η απευθείας ανάθεση της διεύθυνσης που προκύπτει από κάθε GetProcAddress στο EXPLORER.EXE remote-Thread καθώς λόγο ASLR κάθε διεύθυνση διαφέρει μεταξύ των δυο αυτών διαφορετικών διεργασιών.
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Η απευθείας ανάθεση της διεύθυνσης που προκύπτει από κάθε GetProcAddress στο EXPLORER.EXE remote-Thread καθώς λόγο ASLR κάθε διεύθυνση διαφέρει μεταξύ των δυο αυτών διαφορετικών διεργασιών.

 

Τα σέβη μου αν και το προόριζα για τους λιγότερο έμπειρους. ^_^

 

Συγκεκριμένα, αν το ASLR ήταν ενεργοποιημένο για τον explorer και τα libraries του (που είναι) και λειτουργούσε "στο maximum" (που δεν συμβαίνει) αυτός ο κώδικας θα κράσαρε το νέο process επιτόπου. Η kernel32.dll θα ήταν φορτωμένη σε άλλο base address στο νέο και στο παλιό process, οπότε μεταφέροντας τη (σωστή) επιστρεφόμενη τιμή της GetProcAddress στο άλλο process έχεις έναν pointer που πλέον δείχνει όπου να 'ναι. Με το που ξεκινήσει το νέο thread να εκτελείται, crash.

 

Αυτό ισχύει γενικότερα (εφόσον τα 2 address spaces είναι τελείως ξεχωριστά, θεωρητικά οποιαδήποτε μεταφορά τιμής pointer από το ένα στο άλλο δεν μπορεί ποτέ να λειτουργήσει), αλλά παραδοσιακά τα διάφορα images φορτώνονται στο ίδιο memory address κάθε φορά οπότε τα πράγματα "δουλεύουν κατα λάθος" που λέω καμιά φορά. Επιπλέον, παρόλο που υπάρχει και παλιότερος μηχανισμός σε λειτουργία που μπορεί να οδηγήσει σε τέτοιου είδους crash, εν τούτοις το kernel32.dll συγκεκριμένα είναι "πρώτο μεταξύ ίσων" οπότε και πάλι "κατα λάθος" δεν έχουμε crash (δοκιμάζοντας το ίδιο με συναρτήσεις σ' ένα δικό μας dll τα πράγματα δε θα είναι τόσο ρόδινα).

 

Παρόλα αυτά, απ' ότι ξέρω το ASLR στα Windows είναι λίγο ευνουχισμένο (φαντάζομαι γιατί υπάρχουν πολλά προγράμματα που κάνουν ακριβώς τέτοια πράγματα και βέβαια η MS δεν είναι δυνατόν να βγάλει νέα Windows όπου όλα αυτά μόνο κρασάρουν) και επιλέγει διαφορετικές διευθύνσεις για τα dll μόνο σε κάθε boot οι οποίες ισχύουν για όλα τα processes (ενώ θα μπορούσε να επιλέγει διαφορετικά επιτόπου για κάθε νέο process που δημιουργείται). Αυτός είναι και ο λόγος που δεν κρασάρει ο κώδικας παραπάνω -- και πάλι κατά τύχη δηλαδή.

 

Αν κάποιος θέλει μπορεί να δοκιμάσει να τρέξει το παραπάνω σε δύο δόσεις: αντί να κάνουμε inject κατευθείαν στη νέα process πρώτα γράφουμε τα ίδια περιεχόμενα μνήμης σε ένα αρχείο, και στη δεύτερη δόση τα διαβάζουμε από το αρχείο και τα γράφουμε στη νέα process. Αν τώρα ανάμεσα στο πρώτο και στο δεύτερο βήμα μεσολαβήσει reboot του υπολογιστή => crash.

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Γενικά στο LINUX υπάρχουν διάφορα ιδιόρρυθμα θέματα και καθώς δεν έχω ασχοληθεί ιδιαίτερα μαζί του απλά εντυπωσιάζομαι άλλοτε θετικά άλλοτε αρνητικά, για παράδειγμα το γνωστό πρόγραμμα συμπίεσης UPX σε LINUX δεν μπορεί να κάνει αποσυμπίεση στην μνήμη οπότε αποσυμπιέζει στον κατάλογο TMP και εκτελεί από εκεί το αποσυμπιεσμένο πρόγραμμα..

Βέβαια μπορείς να φορτώσεις ολόκληρο το /tmp στη RAM που εκ του αποτελέσματος τουλάχιστον, λογικά, είναι το ίδιο πράγμα. Από default το κάνουν οι ArchLinux και Fedora. Πιθανά και άλλες.

https://wiki.archlinux.org/index.php/Fstab#tmpfs

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Η πλάκα είναι πως δεν ξέρω τι ακριβώς κάνει το linux σ' αυτή την περίπτωση για να μπορεί να το επιτρέψει αυτό. Τι εννοώ: ο λόγος που δε μπορείς να διαγράψεις loaded executable image είναι πως ο loader των Windows κάνει map τα περιεχόμενα του image στο address space της εφαρμογής αλλά δεν τα φορτώνει βέβαια επιτόπου από το δίσκο. Ο κανονικός μηχανισμός της διαχείρισης virtual memory απο κει και πέρα επαρκεί (πας να κάνεις access μια page που δεν έχει φορτωθεί => page fault => το λειτουργικό φορτώνει από το δίσκο στη μνήμη => η εφαρμογή συνεχίζει σα να μην τρέχει τίποτα).

 

Το πρόβλημα λοιπόν είναι πως αν διαγραφεί το image τότε σε περίπτωση τέτοιου page fault δεν μπορείς να κάνεις τίποτα παρα μόνο ένα "η εφαρμογή παρουσίασε σφάλμα μπλα μπλα και τη σκότωσα" (αντιθέτως δεν υπάρχει κανένα πρόβλημα να μετονομάσεις ή να μετακινήσεις το αρχείο, αυτά είναι λεπτομέρειες του filesystem που δεν έχουν σημασία).

 

Άρα το Linux τι κάνει σ' αυτή την περίπτωση; Απλά segfault ή κάτι ανάλογο; Δεν ξέρω, αλλά ακούγεται μέσα στο πνεύμα.

Ανάλογα με τον τρόπο που δουλεύει η εφαρμογή υπάρχουν και διαφορετικές συμπεριφορές αλλά γενικά αν έχει γίνει μια εφαρμογή mmap δεν σε αφήνει να την κάνεις overwrite ακριβώς για να μην υπάρχουν προβλήματα. Κάνει όμως ορισμένες εξυπνάδες για να σου παρέχει κάποιες ευκολίες.
# cd /bin
# cat yes > bash
zsh: Αρχείο κειμένου σε χρήση: bash
# cp yes bash
cp: cannot create regular file «bash»: Αρχείο κειμένου σε χρήση
# mv yes2 bash
Όπως βλέπουμε, το overwrite, είτε γράφοντας το αρχείο με cp είτε γράφοντας τα δεδομένα με cat, αποτυγχάνει γιατί το κέλυφος bash τρέχει αυτή τη στιγμή και έχει γίνει mmap οπότε θα ήταν καταστροφικό να γίνει overwrite. Όταν όμως κάνω μετακίνηση-μετονομασία ένα αρχείο (από το ίδιο filesystem) σε bash, τότε απλά αλλάζουν τα μεταδεδομένα του fs ώστε να δείχνουν στην τάδε περιοχή του δίσκου που υπήρχε το yes2 οπότε δεν υπάρχει πρόβλημα γιατί δεν αλλάζει η περιοχή του σκληρού που έχει γίνει mmap.

 

Για αυτό το λόγο σε πολλές διανομές τα πακέτα για τις "κρίσιμες" εφαρμογές έχουν διαφορετικά ονόματα (πχ bash2) και ένα script που κάνει την μετακίνηση.

 

Όμοια μπορείς να κάνεις unlink όπως είπες και αυτό θα γίνει μόνο αφού κλείσουν όλα τα handles που κάνουν reference το αρχείο. Για παράδειγμα μπορείς να βάλεις να παίζει ένα mp3 ή να βάλεις τον firefox να κατεβάζει ένα μεγάλο αρχείο και μετά από άλλο κέλυφος το σβήνεις. Στη περίπτωση του mp3 θα το ακούς κανονικά και στη περίπτωση του firefox θα δεις ότι θα συνεχίζει να γράφει δεδομένα και το σβήσιμο θα γίνει αφού τελειώσει η αναπαραγωγή και το κατέβασμα αντίστοιχα.

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...