DedSec Project

Δυαδικά αρχεία και σειριοποίηση — DedSec Project

Δυαδικές μορφές — ασφαλή μήκη, εκδόσεις και έλεγχος φθοράς Ορισμός μορφής δεδομένων Δημιούργησε μία εγγραφή που περιέχει κείμενο UTF-8. Η κεφαλίδα έχει έντεκα byte: τέσσερα byte μαγικής…

Δυαδικές μορφές — ασφαλή μήκη, εκδόσεις και έλεγχος φθοράς

Ορισμός μορφής δεδομένων

Δημιούργησε μία εγγραφή που περιέχει κείμενο UTF-8. Η κεφαλίδα έχει έντεκα byte: τέσσερα byte μαγικής υπογραφής DSK1, ένα byte έκδοσης, δύο byte για το μήκος του περιεχομένου σε σειρά big-endian και τέσσερα byte CRC32. Αμέσως μετά ακολουθεί το πραγματικό περιεχόμενο. Η ξεκάθαρη σειρά των byte κάνει τη μορφή μεταφέρσιμη ανάμεσα σε διαφορετικές αρχιτεκτονικές. Ορίζουμε μέγιστο περιεχόμενο 64 byte και απορρίπτουμε άγνωστες εκδόσεις, επιπλέον δεδομένα, κομμένα μηνύματα και λανθασμένο άθροισμα ελέγχου.

Εκτελέσιμο εργαστήριο Python 3

Αποθήκευσε ως binary_lab.py και εκτέλεσε python3 binary_lab.py. Ο κώδικας χρησιμοποιεί τεχνητά byte στη μνήμη, χωρίς πρόσθετα πακέτα ή πρόσβαση δικτύου.

import struct
import zlib

MAGIC, LIMIT = b"DSK1", 64

def encode(content):
    raw = content.encode("utf-8")
    if len(raw) > LIMIT:
        raise ValueError("payload too large")
    return struct.pack(">4sBHI", MAGIC, 1, len(raw), zlib.crc32(raw)) + raw

def decode(frame):
    if len(frame) < 11:
        raise ValueError("short header")
    magic, version, size, checksum = struct.unpack(">4sBHI", frame[:11])
    if magic != MAGIC or version != 1 or size > LIMIT or len(frame) != 11 + size:
        raise ValueError("invalid header or length")
    raw = frame[11:]
    if zlib.crc32(raw) != checksum:
        raise ValueError("corrupted bytes")
    return raw.decode("utf-8")

good = encode("γειά")
assert decode(good) == "γειά"
for bad in (good[:10], good[:-1], good[:5] + b"\xff" + good[6:],
            good[:11] + b"x" + good[12:], good + b"x"):
    try:
        decode(bad)
    except (ValueError, UnicodeError):
        pass
    else:
        raise AssertionError("corruption accepted")
print("PASS: length/version/checksum and UTF-8 checks")

Γιατί ελέγχουμε πριν διαβάσουμε

Πριν από τη struct.unpack πρέπει να βεβαιωθούμε ότι υπάρχει ολόκληρη η κεφαλίδα. Πριν θεωρήσουμε έγκυρο το περιεχόμενο πρέπει να επιβεβαιώσουμε ότι το δηλωμένο μήκος βρίσκεται κάτω από το επιτρεπτό όριο και συμφωνεί ακριβώς με τα πραγματικά byte. Διαφορετικά, μία σωστή αρχή θα μπορούσε να κρύβει επιπλέον άγνωστα δεδομένα. Το CRC32 εντοπίζει τυχαία φθορά, αλλά δεν πιστοποιεί προέλευση και δεν προστατεύει από επιτιθέμενο που το υπολογίζει ξανά. Ακόμη και σωστό άθροισμα ελέγχου δεν εγγυάται έγκυρο κείμενο UTF-8. Μη χρησιμοποιείς pickle για αποσειριοποίηση μη έμπιστων δεδομένων, καθώς μπορεί να προκαλέσει εκτέλεση κώδικα.

Ασκήσεις και διάγνωση

  1. Άλλαξε το byte της έκδοσης και μετά ένα byte κειμένου. Σημείωσε ποιος έλεγχος αποτυγχάνει κάθε φορά.
  2. Δοκίμασε ελληνικό κείμενο και emoji. Σύγκρινε μήκος χαρακτήρων και byte· η κεφαλίδα καταγράφει byte.
  3. Άλλαξε το δηλωμένο μήκος σε 65535. Ο αναλυτής πρέπει να απορρίψει το μήνυμα πριν δεσμεύσει μνήμη.
  4. Σχεδίασε νέα έκδοση 2 ως διαφορετική σύμβαση και εξήγησε πώς θα ελέγξεις συμβατότητα με τα παλιά αρχεία.

Παραδοτέα: PASS, διάγραμμα της κεφαλίδας, πέντε άκυρα μηνύματα και σαφής διάκριση ανίχνευσης φθοράς από πιστοποίηση αυθεντικότητας.