Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια
σε

 

Αρχική σελίδα Ιστολόγια Συζητήσεις Εκθέσεις Φωτογραφιών Αρχειοθήκες

Τεχνικές Anti-Cracking (part II)

Îåêßíçóå áðü ôï ìÝëïò Thiseas. Τελευταία δημοσίευση από το μέλος Thiseas στις 09-03-2010, 23:44. Υπάρχουν 0 απαντήσεις.
Ταξινόμηση Δημοσιεύσεων: Προηγούμενο Επόμενο
  •  09-03-2010, 23:44 57586

    Τεχνικές Anti-Cracking (part II)

    Σύνδεση με τα προηγούμενα... ή αλλιώς «Previously in... Lost» ;-)
    Στο προηγούμενο part I - άρθρο, είχαμε ξεκινήσει μια αναφορά στους τρόπους με τους οποίους μπορούμε να κάνουμε το πρόγραμμα μας δύσκολο στο να παραβιαστεί από κάποιον cracker. Όταν λέμε να «παραβιαστεί» εννοούμε να μπορέσει κάποιος, π.χ. κάνοντας disassembly με κάποιον debugger, να διαβάσει, να καταλάβει και να προσπεράσει τις όποιες δικλείδες ασφαλείας βάλαμε για να μην χρησιμοποιηθεί το πρόγραμμα μας χωρίς την απαραίτητη δικαιοδοσία.
    Τα βήματα που υποσχεθήκαμε να σας παρουσιάσουμε, για την υλοποίηση του παραπάνω στόχου, είναι τα παρακάτω:
    1.    Τεχνικές Anti-Debugging.
    2.    Μπέρδεμα του κώδικα ώστε να μην μπορεί να κατανοηθεί εύκολα (code obfuscation).
    3.    Packing του εκτελέσιμου και self-encryption.
    4.    Δημιουργία αλγόριθμου για την απόδειξη της γνησιότητας ενός serial number (serial number authentication).
    Στο προηγούμενο άρθρο, αναφερθήκαμε στο βήμα 1, παρουσιάζοντας και αρκετό κώδικα για να μπορέσετε να τον χρησιμοποιήσετε κι εσείς. Σε αυτό το άρθρο θα «μιλήσουμε» για τα εξίσου σημαντικά βήματα 2,3 και 4. Ας ξεκινήσουμε...

    Μπέρδεμα του κώδικα (code obfuscation)
    Το μπέρδεμα του κώδικα (http://is.gd/7IxS) είναι μια αρκετά παλιά τεχνική η οποία τον τελευταίο καιρό είναι της «μόδας» λόγω των VM-Executables (Virtual Machines) όπως είναι ο εκτελέσιμος κώδικας που «γεννάει» η Java ή η C# (βλέπε .Net Application Framework). Ο βασικός στόχος είναι να δημιουργηθεί ένα πρόγραμμα τόσο δύσκολο στην κατανόηση ώστε να αποθαρρύνει και τον πιο υπομονετικό cracker. Σε αυτό το άρθρο, θα επικεντρωθούμε στην δημιουργία ενός εκτελέσιμου του οποίου ο disassembly κώδικας θα οδηγεί σε ένα λαβύρινθο, που ακόμα κι ο ...Θησέας  θα τα εύρισκε «σκούρα»! Μπορείτε κάλιστα να ακολουθήσετε τις εμπορικές λύσεις που υπάρχουν πολλές ή μπορείτε να δείτε μια πολύ απλή προσέγγιση εδώ για να γίνει κατανοητή η βασική μεθοδολογία τους, οπότε συνεχίστε να διαβάζετε... ;-)
    Πριν ξεκινήσουμε, θα πρέπει να έχουμε στο μυαλό μας μερικούς κανόνες που καλό θα ήτανε να προσπαθούμε να τηρούμε όταν θέλουμε να κρύψουμε πράγματα, από τους crackers.

    1.    Να μην υποτιμάμε τον «αντίπαλο». Οι crackers είναι κι αυτοί άνθρωποι, σαν κι εμάς!! Οπότε κι αυτοί διαβάζουν ότι κι εμείς και γνωρίζουν καλά τις τεχνικές code obfuscation. Έτσι, το κρύψιμο του κώδικα δεν θα πρέπει να γίνεται με... φανερό τρόπο!! Δηλαδή, θα πρέπει να προσέχουμε να μην κάνουμε το πρόγραμμα μας τέτοιο ώστε να «φωνάζει» και να λέει: «Ε! Εδώ, αυτό το κομμάτι που διαβάζεις το έχω κάνει επίτηδες δύσκολο, αλλά στην πραγματικότητα δεν κάνει τίποτε το ουσιαστικό, μπορείς να το κάνεις skip!!». Έτσι το μπέρδεμα του κώδικα καλό θα είναι να είναι διακριτικό, χωρίς «χυδαιότητες» (όπως συνήθιζε να γράφει κάποιος παλιός δάσκαλος - [1]).
    2.    Να μην κρύβουμε μόνο το κομμάτι που έχει τον κώδικα που (ας πούμε) εξετάζει την γνησιότητα ενός serial-number, διότι είναι σαν να λέμε στον cracker, που να επικεντρωθεί. Το κρύψιμο θα πρέπει να γίνεται σε όλο τον κώδικα του προγράμματος μας και ομοιόμορφα.
    3.    Φυσικά, να έχουμε πάντα μια καθαρή έκδοση του κώδικα του προγράμματος μας (clear sources), διότι μετά το κρύψιμο, η συντήρηση του θα είναι από πάρα πολύ δύσκολη έως αδύνατη.
    4.    Στο κρύψιμο, να χρησιμοποιούμε οτιδήποτε θα μπορούσε να μπερδέψει τον cracker. Ένας καλός κανόνας είναι να βρούμε μια λίστα με οδηγίες για το πως γράφουμε ευανάγνωστο κώδικα, και να κάνουμε ακριβώς τα αντίθετα.
    5.    Ο μπερδεμένος κώδικας να μην είναι εντελώς άχρηστος. Δηλαδή να μην μπορεί κάποιος να παραβλέψει τις εντολές του κάνοντας (ας πούμε ένα JMP - jump) σε μιά άλλη περιοχή, χωρίς συνέπειες. Θα πρέπει να χρησιμοποιούμε global μεταβλητές στις οποίες θα δίνουμε συγκεκριμένες τιμές μέσα σε κάποια συνάρτηση και αργότερα, πολύ αργότερα, θα εξετάζουμε αν αυτές οι μεταβλητές έχουνε τις τιμές που δώσαμε. Αν όχι, σημαίνει οτι κάποιος έχει... «πηδήξει» (με την «καλή» έννοια??) ένα μέρος του κώδικά μας.
    6.    Πολλοί είναι αυτοί που λένε να μην γράφετε σχόλια, ή να μην στοιχίζετε σωστά τις εντολές έτσι ο κώδικας θα είναι πολύ δύσκολο να κατανοηθεί. Αυτό δεν είναι εντελώς λάθος, αλλά μόνο αν δίνετε και τον πηγαίο κώδικα και δεν θέλετε να κατανοηθεί πλήρως. Στην περίπτωση μας όμως, δεν ισχύει κάτι τέτοιο, μιας και τα σχόλια ή η στοίχιση δεν εισάγονται στο τελικό εκτελέσιμο αρχείο.

    Ας δούμε, λοιπόν, ένα παράδειγμα στην πράξη:
    Έστω οτι έχουμε το ακόλουθο πρόγραμμα:
    Ζητάμε από τον χρήστη ένα κωδικό και απαντάμε αν αυτός ο κωδικός είναι σωστός, συγκρίνοντας τον με κάποιον σταθερό, που έχουμε μέσα στο πρόγραμμά μας:
    #include<stdio.h>
    
    int checkCode(char sCode[500]);
    
    int main(int argc, char *argv[])
    {
        char sCode[500];
        
        printf("Enter Code: ");
        scanf("%s", sCode);    
        
        if (checkCode(sCode)){
            printf("Correct!!\n");
        }
        else{
            printf("Fail...!\n");
        }    
        return 0;
    }
    
    int checkCode(char sCode[500])
    {
        return (!strcmp(sCode,"aygoulaki"));
    }

    Απ’ οτι βλέπουμε ο σωστός κωδικός είναι "aygoulaki"! Ας δούμε τώρα πως λειτουργεί ένας cracker όταν θα θελήσει να σπάσει των κωδικό μας, έχοντας μόνο το executable πρόγραμμα:
    Τρέχει το πρόγραμμα με τον Olly Debugger και διαβάζοντας τον κώδικα ή ψάχνοντας με κάποιο τρόπο οδηγείται στην εντολή που κάνει τον έλεγχο για τον κωδικό. Εκεί, βάζει ένα break point και εκτελεί το πρόγραμμα δίνοτας για κωδικό κάτι άσχετο (πχ. «xeimwnas»). Στην είκόνα 1 φαίνονται τα... αποτελέσματα! Ο κωδικός μας είναι πιο ορατός κι από τον... ήλιο!


    Εικόνα 1: Ο κωδικός φαίνεται στους καταχωρητές (επάνω δεξιά) αλλά και στην μνήμη (κάτω).

    Ας κρύψουμε όμως, λιγάκι των κώδικα για να δυσκολέψουμε το έργο του. Δείτε το παρακάτω πρόγραμμα που κάνει το ίδιο ακριβώς πράγμα:
    #include<stdio.h>
    
    #define K "abcdefghijklmnopqrstuvwxyz"
    int checkCode(char sCode[500]);
    
    int main(int argc, char *argv[])
    {
        char sCode[500];
        
        printf("Enter Code: ");
        scanf("%s", sCode);
        
        if (checkCode(sCode)){
            printf("Correct!\n");
        }
        else{
            printf("Fail...!\n");
        }
        
        return 0;
    }
    
    
    int checkCode(char sCode[500])
    {
        return ( sCode[ 0 ]==K[ 0 ]  && sCode[ 1 ]==K[ 24 ] &&  
                 sCode[ 2 ]==K[ 6 ]  && sCode[ 3 ]==K[ 14 ] &&  
                 sCode[ 4 ]==K[ 20 ] && sCode[ 5 ]==K[ 11 ] &&  
                 sCode[ 6 ]==K[ 0 ]  && sCode[ 7 ]==K[ 10 ] &&  
                 sCode[ 8 ]==K[ 8 ] );
    }


    Το κολπάκι που κάναμε εδώ είναι να μην χρησιμοποιήσουμε σταθερά για τον κωδικό, αλλά να τον κρύψουμε μέσα σε μια μετβλητή φαινομενικά άσχετων χαρακτήρων (την Κ) και να εξάγουμε (εμέσως) μέσα από αυτήν τα γράμματα που αποτελούν τον κωδικό, δηλαδή τα K[ 0 ], K[ 24 ], K[ 6 ], K[ 14 ], K[ 20 ], K[ 11 ], K[ 0 ], K[ 10 ], K[ 8 ], που είναι η λέξη "aygoulaki". Η τελική σύγκριση για τον κωδικό θα πραγματοποιείτε συγκρίνοντας χαρακτήρες (character by character) και όχι character arrays.  To αποτέλεσμα του εκτελέσιμου φαίνεται στην εικόνα 2. Απ’ ότι βλέπετε, δεν υπάρχει πουθένα (ούτε στην μνήμη, ούτε στους καταχωρητές) η περίφημη λέξη "aygoulaki". Φυσικά, το να σπάσει και αυτή η κωδικοποίηση είναι κάτι απλό, εφόσων ο cracker αρχίζει να ψάχνει καλύτερα τον κώδικα. Κι εμείς όμως δεν θα τον αφήσουμε παραπονεμένο ;-) θα εμπλουτίσουμε το παραπάνω πρόγραμμα με τις τεχνικές που ήδη αναφέραμε. Δηλαδή θα κάνουμε την function checkCode να καλεί καμιά 20αριά άλλες functions άσχετες, οι οποίες θα κάνουνε ένα σωρό πράξεις και ότι άλλο σκεφτούμε. Π.χ. θα δημιουργούν ένα αρχείο με το όνομα tmp.enc και θα γράφουνε μέσα σε αυτό ένα σωρό χαρακτήρες σε δεκαεξαδική μορφή αφού τους έχουν κάνει XOR κλπ κλπ. Φυσικά η ενέργεια αυτή θα γίνει για να μπερδέψει τον cracker. Επίσης, ο έλεγχος sCode[ 0 ] == K[ 0 ]  && sCode[ 1 ] == K[ 24 ]... που είναι και ο πραγματικός έλεγχος, καλό είναι να μην βρίσκεται σε ένα μόνο σημείο και όλος μαζί. Μπορούμε να τον «διασπείρουμε» μέσα στις functions που θα καλέσουμε. Καλό είναι να βρίσκεται σε σημεία μαζί με άλλους ελέγχους (π.χ. τον έλεγχο ύπαρξης του αρχείου "tmp.enc"). Με αυτόν τον τρόπο ο cracker δεν θα μπορέσει εύκολα να καταλάβει ποιά σημεία του κώδικα είναι σημαντικά και ποιά όχι, καθώς τα σημαντικά με τα ασήματα θα «αναμιγνύονται» σε έναν επαναλαμβανόμενο κύκλο, κάνοντας το debugging... από εφιαλτικό έως αδύνατο.



    Εικόνα 2: Πού είναι ο κωδικός; Χμ, μάλλον κρύφτηκε, μόνο ο... xeimwnas μας έμεινε.


    Packing του εκτελέσιμου και self-encryption
    Η μέθοδος αυτή χωρίζεται σε δυο βασικές κατηγορίες:

    ->    Packing Executables
    Είναι η μετατροπή το εκτελέσιμου σε μια συμπιεσμένη μορφή ωστέ να μην μπορεί να διαβαστεί με ένα πρόγραμμα μετατροπής του exe σε αρχείο εντολών assembly. Κατά την εκτέλεση του προγράμματος γίνεται το unpacking στην μνήμη.
    Ένας από τους πιο γνωστούς packers είναι ο UPX Packer (http://upx.sourceforge.net/).
    Όμως, για κάθε κλειδί υπάρχει κι ένα αντικλείδι, οπότε, δεν θα λέγαμε οτι η μέθοδος αυτή από μόνη της είναι οτι καλύτερο, διότι οι περισσότεροι υποψήφιοι crackers το πρώτο πράγμα που κάνουνε όταν αποφασίσουνε να ασχοληθούνε με το "άθλημα", είναι να κατεβάσουμε όλους τους un packers... του "εμπορίου"... (π.χ. http://www.exetools.com/unpackers.htm). Μια άλλη χρήση των packers είναι και η οικονομία στον δίσκο, μιας και η συμπίεση μπορεί να φτάσει και πάνω από το 200%. Για παράδειγμα, το προγραμαματάκι που φτιάξαμε στο πρώτο μέρος του άρθρου (στο προηγούμενο τεύχος, το rock.exe - το θυμάστε;) συμπιέστηκε με τη χρήση του UPX, από τα 32kb στα 10.5kb. Καθόλου άσχημα!!

    ->    Τεχνικές self-encryption. Ίσως έχετε διαβάσει για τους λεγόμενους πολυμορφικούς ιούς που έχουν την ικανότητα να πολλαπλασιάζονται κρύβοντας κάθε φορά τον εαυτό τους μέσα στον εαυτό τους!! με μια διαφορετική μορφή κωδικοποίησης. Μπορούμε (ακολουθώντας την ίδια αρχή) να κάνουμε encrypt το πρόγραμμα ή κομμάτια αυτού και να τα κάνουμε decrypt run-time κατά βούληση και κατ' απαίτηση.
    Η τεχνική αυτή ήδη έχει εφαρμοστεί στο παραπάνω μικρό προγραμματάκι μας με την μορφή του πίνακα χαρακτήρων Κ. Ο Κωδικός μας "aygoulaki" βρίσκεται κωδικοποιημένος μέσα στην Κ και στην ουσία η συνάρτηση checkCode είναι υπεύθυνη για την αποκωδικοποίηση του. Φυσικά αυτή είναι μια στοιχειώδης κωδικοποίηση και επιλέχτηκε για καθαρά εκπαιδευτικούς λόγους. Θα μπορούσε εύκολα ο πίνακας Κ να είναι κωδικοποιημένος με κάποια γνωστή μορφή κρυπτογράφισης (π.χ. DES) και να αποκρυπτογραφείται κατά την διάρκεια εκτέλεσης του προγράμματος (run time) μέσα στην checkCode. Το θέμα αυτό είναι πραγματικά ενδιαφέρον αλλά και προκλητικό μαζί. Για μια πιο προχωρημένη και λεπτομερή προσέγγιση, μπορείτε να ρίξετε μια ματιά κι εδώ: http://is.gd/7Jei


    Serial Number Authentication
    Το λεγόμενο SNA υλοποιεί έναν αλγόριθμο πιστοποιήσης αυθεντικότητας. Συνήθως απαιτεί μια σειρά αριθμών σαν είσοδο και σαν έξοδο αναφέρει αν η σειρά είναι γνήσια ή όχι, εφαρμόζοντας μια σειρά πράξεων σε αυτούς τους αριθμούς. Μπορούμε εύκολα να δημιουργήσουμε έναν τέτοιο απλό αλγόριθμο, καθώς και να το υλοποιήσουμε με κάποια γλώσσα προγραμματισμού. Ας δούμε λοιπόν πως γίνεται κάτι τέτοιο:

    Περιγραφή Αλγορίθμου:
    Ο χρήστης θα εισάγει μια σειρά αριθμών της μορφής: A-B-C-D-E, όπου A, B, C, D, E είναι ακαίρεοι αριθμοί μεγαλύτεροι του μηδενός.
    Αν ισχύουν όλα τα παρακάτω:
    1.    A=B*C και
    2.    D=B+C και
    3.    Ε=Mod(Rv(A*B*C*D),A)
    τότε η σειρά αριθμών θεωρείτε γνήσια.
    Η συνάρτηση Rv επιστρέφει την αντίστροφη σειρά ενός αριθμού, δηλαδή Rv(512)=215.
    H συνάρτηση Mod επιστρέφει το υπόλοιπο της διαίρεσης των ορισμάτων της. Πχ. Mod(20,3)=2.
    Για παράδειγμα, η σειρά 12-4-3-7-9 είναι μια γνήσια σειρά.

    Η υλοποίηση ενός τέτοιου αλγόριθμου μέσα στον κώδικα του εκτελέσιμου δεν ακολουθείται πια στις μέρες μας. Αυτό που συχνά γίνεται είναι ο έλεγχος της γνησιότητας μέσω internet με τον κώδικα αυθεντικοποίησης να βρίσκεται σε έναν server.
    Θα σας δείξουμε πως μπορείτε να κάνετε κάτι τέτοιο, δίνοντας το πρόγραμμα τόσο του client όσο και του server. Ο client θεωρούμε οτι θα είναι σε C++ και είναι αυτός που θα ζητάει αυθεντικοποίηση. Ο server θα είναι σε php, και θα είναι αυτός που θα αυθεντικοποιεί, δηλαδή αυτός που θα εξετάζει αν μια σειρά αριθμών είναι γνήσια ή όχι υλοποιώντας τον αλγόριθμό μας.
    Θα σας δείξουμε πρώτα το php (sorry δεν το έχω σε asp...yet!) πρόγραμμα του server:
    <?php
    // Πρόγραμμα checkserial.php
    $ser = $_GET["ser"];                 // Παίρνουμε την σειρά αριθμών.
    $a = explode  ( "-"  , $ser  ,5 );   // την τοποθετούμε σε ένα πίνακα.
    $e = $a[0] * $a[1] * $a[2] * $a[3];  // Βρίσκουμε το Ε κανονικό.
    $e_rev = strrev($e);                 // Αναποδογυρίζουμε το Ε.
    if (
            ($a[0] == $a[1] * $a[2] ) &&   // Εξετάζουμε το Α.
            ($a[3] == $a[1] + $a[2] ) &&   // Εξετάζουμε το D.
            (fmod($e_rev, $a[0]) == $a[4]) // Εξετάζουμε το E.
         )
    
        echo "!@#!@#234234!@#!@";       // Στην επιτυχία επιστέφουμε αυτό!
    else
        echo "!@#!@#234234!@#!!";       // Στην αποτυχία επιστρέφουμε αυτό!! ;-)
    ?>


    Δίνοντας http://www.o_server_mou.com/checkserial.php?ser=12-4-3-7-9 θα πάρουμε σαν αποτέλεσμα το "!@#!@#234234!@#!!" (σε αποτυχία), ή το "!@#!@#234234!@#!@" σε περίπτωση επιτυχίας! Φυσικά δεν είμαστε και τόσο χαζοί να επιστρέφουμε TRUE/FALSE. Το "!@#!@#234234!@#!@" μπορούμε να το χειριστούμε με τέτοιο τρόπο ώστε να μην φαίνεται εύκολα οτι είναι αποτέλεσμα επιτυχυμένης προσπάθειας μιας και μοιάζει πάρα πολύ με το αποτέλεσμα της αποτυχημένης προσπάθειας. Στην ουσία διαφέρουν μόνο στο τελευταίο ψηφίο. Αυτό είναι ένα πρώτο και πολύ αρχικό στάδιο «κρυψίματος» κώδικα. Φυσικά και εδώ θα μπορούσαμε να στέλνουμε την απάντηση κωδικοποιημένη (κατά MD5) με την  χρήση της έτοιμης συνάρτησης της php MD5 (http://gr2.php.net/md5).
    Από την άλλη πλευρά, σας δίνουμε τον κώδικα σε Microsoft C++ για να μπορέσετε να καλέσετε το checkserial.php στον παραπάνω server. Σας τον παρουσιάζουμε χωρίς προστασία ή προφύλαξη, για να τον καταλάβετε (κι εσείς, κι εμείς!). Να σημειώσουμε οτι ο server www.o_server_mou.com είναι ένας φανταστικός server. Στην θέση του μπορείτε να βάλετε τον δικό σας.
    Ο κώδικας είναι ο ακόλουθος:
    #include "windows.h"
    #include "winhttp.h"
    #include "stdio.h"
    
    int main( int argc, char* argv[] )
    {
        HINTERNET hSession;
        HINTERNET hConnect;
        HINTERNET hRequest;
        BOOL httpResult;
        DWORD data;
        DWORD dwSize = sizeof(DWORD);
        LPSTR pszOutBuffer = 0;
        DWORD dwDownloaded;
    
        // Χρησιμoποιούμε την WinHttpOpen για να ανοίξουμε την σύνδεση.
         hSession = WinHttpOpen( L"An HTTP Call",
                              WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
                              WINHTTP_NO_PROXY_NAME,
                              WINHTTP_NO_PROXY_BYPASS, 0 );
    
        // Καλούμε τις απαραίτητες παραμέτρους για web access.
        httpResult = WinHttpQueryOption( hSession,
                                       WINHTTP_OPTION_CONNECT_TIMEOUT,
                                       &data,
                                       &dwSize );
    
        /// Εδώ εισάγουμε τον server.
        hConnect = WinHttpConnect( hSession,
                                 L"www.o_server_mou.com",
                                 INTERNET_DEFAULT_HTTP_PORT,
                                 0 );
    
        // Ορίσουμε την σειρά αριθμών.
        char code[120] = "123-456";
    
        //Ορίσουμε το πρόγραμμα που θα καλέσουμε.
        char sGet[200] = "/checkserial.php?ser=";
        
        strcat(sGet, code);
    
        int len = strlen(sGet)+1;
        wchar_t *wURL_Get = new wchar_t[len];
        if ( wURL_Get == 0 )
         return 0;
        memset(wURL_Get,0,len);
        ::MultiByteToWideChar(  CP_ACP, NULL,sGet, -1, wURL_Get,len );
    
        // Καλούμε τους HTTP Headers.
        hRequest = WinHttpOpenRequest( hConnect,
                                     L"GET",
                                     wURL_Get,
                                     NULL,
                                     WINHTTP_NO_REFERER,
                                     WINHTTP_DEFAULT_ACCEPT_TYPES,
                                     0 );
                                     
        delete []wURL_Get;
    
        httpResult = WinHttpSendRequest( hRequest,
                                       WINHTTP_NO_ADDITIONAL_HEADERS,
                                       0,
                                       WINHTTP_NO_REQUEST_DATA,
                                       0,
                                       0,
                                       0 );
    
        httpResult = WinHttpReceiveResponse( hRequest, NULL );
    
        dwSize = 0;
        if (!WinHttpQueryDataAvailable( hRequest, &dwSize)){
            printf("Error %u in WinHttpQueryDataAvailable.\n",
                    GetLastError());
        }
    
        // Δημιουργούμε τον χώρο για την απάντηση απο τον server.
        pszOutBuffer = new char[dwSize+1];
        if (pszOutBuffer)
        {
            ZeroMemory(pszOutBuffer, dwSize+1);
            if (!WinHttpReadData( hRequest,
                                  (LPVOID)pszOutBuffer,
                                  dwSize, &dwDownloaded))
                printf("Error %u in WinHttpReadData.\n",
                        GetLastError());
            else{
                // ΕΔΩ!! ΓΙΝΕΤΑΙ Ο ΕΛΕΓΧΟΣ ΓΙΑ ΤΟ ΑΝ ΑΥΤΟ ΠΟΥ ΔΙΑΒΑΣΑΜΕ
                      // ΕΙΝΑΙ ΣΩΣΤΟ ΚΑΙ... ΝΟΜΙΜΟ!!! ;-)
                if (!strcmp("!@#!@#234234!@#!@", pszOutBuffer))
                    printf("Yes!\n");
            }
    
            delete [] pszOutBuffer;
        }
    
    
      // clean up the mess, λέμε!!!
      httpResult = WinHttpCloseHandle( hRequest );
      if( !httpResult )
          printf( "Could not close the hRequest handle.\n" );
    
      httpResult = WinHttpCloseHandle( hConnect );
      if( !httpResult )
          printf( "Could not close the hConnect handle.\n" );
    
      httpResult = WinHttpCloseHandle( hSession );
      if( !httpResult )
          printf( "Could not close the hSession handle.\n" );
    
      return( 0 );
    }

    Συμπεράσματα
    Σε αυτά τα 2 μέρη, είπα να δείξω μερικές πάρα πολύ απλές και εντελώς εκπαιδευτικές μεθόδους δίνοντας και μερικά μικρά παραδείγματα σε κώδικα, για το πως μπορούμε να κάνουμε τα προγράμματα μας πιο ασφαλή και πιο δύσκολα στο να παραβιαστούν. Μην ξεχνάτε όμως, καμιά μέθοδος από μόνη της δεν είναι αρκετή. Πρέπει να συνδιαστούν και να αναμιχθούν και να... επαναληφθούν.
    Οι κανόνες που ανάφερα είναι μόνο η αρχή....


    Happy Anti-Cracking...



    Αναφορές - Βιβλιογραφία
    [ 1 ] 8088/8086 Προγραμματισμός και Εφαρμογές για τους IBM PC/XT και συμβατούς, Νίκος Νασούφης.
    [ 2 ] "AsProtected Notepad!" By The CodeBreakers-Journal, Vol. 1, No. 2 (2004) (http://is.gd/7Jei)
    [ 3 ] Obfuscation (http://is.gd/7Jkq).
    [ 4 ] Self-encrypting Code to Protect Against Analysis and Tampering (http://is.gd/7KnZ).
    [ 5 ] The Art of Unpacking by Mark Vinsent Yason (http://is.gd/7Kpe)
    [ 6 ] Setting Internet Options in WinHTTP (http://is.gd/7KpV)

    [Πρώτη Δημοσίευση: Total XakeЯ Magazine #20 (Jan.2009)]

    Nothing to declare...
Προβολή Τροφοδοσίας RSS με μορφή XML
Με χρήση του Community Server (Commercial Edition), από την Telligent Systems