Πώς μια σειρά δυσδιάκριτων γεγονότων οδηγεί σε ένα ταξίδι βαθιά στη δυαδική κωδικοποίηση των αρχείων RIFF και σε αυτό το εργαλείο ανάκτησης δεδομένων
Εάν βιάζεστε, κατευθυνθείτε απευθείας στο αποθετήριο GitHub .
Αν και έχω την τύχη να αναπτύξω λογισμικό για εταιρείες τελευταίας τεχνολογίας, ήμουν πάντα ο «τυπογράφος», ένα σήμα που φοράω με περηφάνια.
Και ίσως αυτός είναι ο λόγος που, για περισσότερο από ένα τέταρτο του αιώνα, οι άνθρωποι εμφανίστηκαν με μέσα αποθήκευσης για να σώσουν. Δεν το έκανα ποτέ επαγγελματικά, είναι απλώς κάτι που μου αρέσει και, τις περισσότερες φορές, μπορώ να βοηθήσω.
Η κύρια προσέγγισή μου είναι διπλή:
συλλάβετε ό,τι μπορείτε από το ετοιμοθάνατο μέσο
προσπαθήστε να αναδημιουργήσετε τα δεδομένα
Μερικές φορές, απλώς προσαρμόζοντας την ανοχή ανάγνωσης, μπορείτε να καταγράψετε τα πάντα, από έναν δίσκο που απορρίπτεται από Windows ή Mac, καθώς το λειτουργικό σύστημα τελειώνει ταχύτερα από ό,τι μπορεί να ανταποκριθεί το ετοιμοθάνατο μέσο. Έχω έναν δίσκο γαντζωμένο σε ένα κουτί για 7 μήνες. 100% επιτυχία ανάκτησης!
Μερικές φορές, θα λείπουν κομμάτια δεδομένων, αλλά ένα από το δέντρο B Πίνακας Εκχώρησης Αρχείων/Κύριο Πίνακας Αρχείων/Κοντέινερ/Σούπερμπλοκ/ριζα είναι ακόμα εκεί, επομένως τα περισσότερα δεδομένα μπορούν να διασωθούν τόσο με ονόματα αρχείων όσο και με τη θέση δέντρου.
Στο άκρο, δεν έχετε τίποτα παρά μόνο μερικά από τα ακατέργαστα δεδομένα. Το μόνο που μπορείτε να κάνετε είναι να το χαράξετε - ουσιαστικά διαβάζοντας κάθε byte από το μέσο, προσδιορίζοντας γνωστές κεφαλίδες όπως JPG ( FF D8
) ή MKV ( 1A 45 DF A3
) και να προχωρήσετε στην καταγραφή όλων των διαδοχικών δεδομένων μέχρι το τέλος του αρχείου. Εάν για οποιονδήποτε λόγο, το αρχείο είναι κατακερματισμένο, το carving προφανώς θα αποτύχει.
Φράνκι, αυτό δεν έχει ξαναγίνει, γενικά είμαι επιφυλακτικός... Δεν ξέρω τι σκεφτόμουν - κατά λάθος μορφοποίησα μια κάρτα SD με ήχο για μια ολόκληρη περίοδο λειτουργίας. Ακόμα χειρότερα, έχω αποθηκεύσει μερικά αρχεία σε αυτό!
Ο Pierre είναι ένας απίστευτα ταλαντούχος κωμικός και ένας εξαιρετικός ηθοποιός. Ενώ το έργο του είναι πολύπλευρο, έγινε ευρέως γνωστός για τα σκίτσα του δρόμου, μερικά από αυτά απολύτως καθολικά. Απλώς ελέγξτε το παρακάτω όπου λέει απλά «Συγγνώμη».
Η απλότητα και η κωμική γοητεία αυτού του σκίτσου δημιουργούν ένα ανάλαφρο αλλά καθολικά ελκυστικό έργο τέχνης.
Με αυτή την απλή ενέργεια, ο Πιερ είχε ενταχθεί στο κλαμπ. Όλοι μπερδεύονται. Ακόμα και η Pixar . Μην ανησυχείτε, είπα, ενώ παίρνετε την κάρτα, τα αντικατασταθέντα περιεχόμενα έχουν φύγει, αλλά δεδομένου ότι πρόκειται για κάρτα με μορφοποίηση μικροφώνου, ακόμη και χωρίς πίνακες FAT, θα μπορούμε να χαράξουμε το μεγαλύτερο μέρος ό,τι έχετε καταγράψει καθώς τα δεδομένα θα είναι πιθανώς διαδοχικά.
Δεν ήξερα, ότι αυτό θα αποδεικνυόταν ένα από τα πιο ενδιαφέροντα παιχνίδια παιχνιδιών που έχω δουλέψει εδώ και καιρό. Η τελευταία φορά που διασκέδασα τόσο πολύ ήταν η ανοικοδόμηση ενός hardware RAID-0 που περιείχε τα masters για ένα άλμπουμ του ' Os Azeitonas .
Όπως ήταν αναμενόμενο, ο μόνος τρόπος για να ανακτήσετε δεδομένα ήταν να τα αφαιρέσετε από την ένδειξη εικόνας. Υπάρχει μια πληθώρα εργαλείων για να διαλέξετε, Photorec (ανοιχτού κώδικα), Recuva (προσέξτε το bundleware), ReclaiMe (με πληρωμή) κ.λπ.… αν και είμαι μερικός στο R-Studio (με πληρωμή). τα αποτελέσματά τους ξεπερνούν σταθερά τον ανταγωνισμό.
Σε αυτήν την περίπτωση, ωστόσο, τα πράγματα δεν θα ήταν τόσο απλά. Κάθε λογισμικό που δοκιμάστηκε ήταν σε θέση να εξαγάγει τα αρχεία wav
, αλλά υπήρχε κάτι μάλλον λάθος με τα δεδομένα καθώς όλα είχαν αυτή την επανάληψη, μια ηχώ του είδους.
Είμαι λίγο βαρετή στην ακρόαση, οπότε τα άνοιξα στο Audacity για να ελέγξω τι συμβαίνει. Μπορείτε να δείτε ξεκάθαρα ένα μοτίβο εδώ:
Ενώ τα κομμάτια έχουν παρόμοια κυματομορφή - δεν υπάρχει κανένα είδος δυαδικής συσχέτισης. Εκτός αυτού, υπήρχε και κάτι θολό, κάθε αρχείο wav
είχε κάτι που φαινόταν να είναι 2 συνεχόμενες κεφαλίδες.
Λάβετε υπόψη σας, σε αυτό το σημείο, οι γνώσεις μου για τα αρχεία wav
ήταν ότι η κεφαλίδα είναι 52 49 46 46
. Εκτός από τη χρήση μικροφώνου, δεν ρώτησα τον Pierre για το πώς είχε καταγράψει πραγματικά τα δεδομένα. Ωστόσο, όταν είδα την ετικέτα "ZOOM M3" στην κεφαλίδα, κάλεσα την αρχή για οτιδήποτε ήχο.
Ο Ed ήταν σε γρήγορη κλήση για το μεγαλύτερο μέρος αυτής της ζωής. Είμαι τυχερός έτσι. Πέρα από το ότι είναι απίστευτος συνθέτης - απλά ακούστε το συναρπαστικό Best Youth - είναι επίσης ένας τέλειος, ζωντανός και αναπνέοντας, εγκυκλοπαίδεια στον ήχο.
Ανίπταμαι διαγωνίως; Ναι, ναι. Έχω ένα. Ηχογραφούν και wav και raw. Τα δεδομένα είναι παραποιημένα; Αχ. Σίγουρος. Ανακτώ; Συνεστραμμένα αρχεία; Αυτό θα είναι ένα αδύνατο έργο και, ακόμα κι αν δεν είναι, θα είναι πιο εύκολο να το ξαναγράψετε.
Και εκεί με είχε. Θα ήταν σίγουρα πιο εύκολο να ηχογραφήσετε ξανά, ακόμη και ο Pierre κάποια στιγμή πρότεινε να κάνετε μια φωνή, αλλά πού θα ήταν η διασκέδαση σε αυτό;
Δεν εκτίμησα καν την έννοια του raw
κύματος μέχρι να το εξήγησε ο Ed, αλλά τώρα ήξερα ότι το μικρόφωνο αποθηκεύει δύο ταυτόχρονα αρχεία, όλα μπήκαν στη θέση τους.
Είναι σύνηθες οι ψηφιακές φωτογραφικές μηχανές να αποθηκεύουν και τις δύο μορφές, αλλά ένα μικρόφωνο είναι διαφορετικό θηρίο. Δεν υπάρχει τρόπος να προσδιορίσετε εκ των προτέρων πόσο χρόνο θα ηχογραφήσει ο χρήστης, κάτι που δεν θα ήταν πρόβλημα εάν αυτή ήταν μια ενιαία ροή. Καθώς αποθηκεύει και τα δύο κομμάτια, υπάρχει πολύ περισσότερο παιχνίδι.
Βλέπετε, μόλις πατήσετε εγγραφή, το μικρόφωνο δημιουργεί δύο αρχεία και ξεπλένει συνεχώς τα δεδομένα που έχουν καταγραφεί και από τα δύο στην κάρτα. Αυτό γίνεται σε κομμάτια δεδομένων. Ένα για το RAW, ένα άλλο για το WAV, επαναλάβετε.
Εφόσον πρέπει να απομονώσουμε αυτές τις φέτες δεδομένων για να τις ξεμπερδέψουμε, πρέπει να γνωρίζουμε το ακριβές μέγεθός τους . Και εκεί βρίσκεται ο μαγικός μας αριθμός !
Η πρώτη μου σκέψη ήταν ότι τα κομμάτια μπορεί να ευθυγραμμιστούν με το μέγεθος της μονάδας εκχώρησης exFAT
. Σε αυτήν την περίπτωση, 128 KBytes
. Ας το δοκιμάσουμε.
Η κεφαλίδα, δηλώνει ξεκάθαρα ότι πρόκειται για στερεοφωνικό αρχείο (2 κανάλια), εγγεγραμμένο στα 32 bit ανά κανάλι, δειγματοληψία 48 χιλιάδες φορές ανά δευτερόλεπτο. Αν θυμάστε από την παραπάνω εικόνα, τα κομμάτια επαναλαμβάνονται σε περίπου 0,7 δευτερόλεπτα.
Ας πάρουμε μια χονδρική προσέγγιση για τα κομμάτια byte ώστε να γνωρίζουμε το ballpark.
1 second of data = 2 channels * 32 bits * 48000 samples 1 second of data = 384000 bytes 0.7 seconds ~ 268800 bytes
Εξετάζουμε κομμάτια περίπου 268 KByte.
Και ακριβώς έτσι, η ιδέα ότι τα δεδομένα μπορεί να τεμαχιστούν σε exFAT AUS των 128 Kbyte διαψεύδεται αμέσως.
Το επόμενο προφανές βήμα θα ήταν να ταξιδέψετε προς τα πάνω στη βάση 2. Δεδομένου ότι το 4096 είναι μια καλή ισορροπία για buffer, ας αξιολογήσουμε από εκεί:
4096 * 32 = 131072 (falls short by about 1/2) 4096 * 64 = 262144 (is in the ballpark of what we're expecting) 262144/384000 ~ 0.682 seconds of data
Τα 0,682 δευτερόλεπτα ταίριαζαν τόσο τέλεια με την εκτίμησή μας για 0,7 δευτερόλεπτα που κατάλαβα αμέσως ότι το 262144 ήταν η σταθερά που αναζητούσαμε.
Εννοιολογικά, το πρόβλημα λύθηκε. Τώρα, ήταν απλώς θέμα κατασκευής του εργαλείου. Για αυτό θα ήταν απαραίτητο:
Και είμαι σίγουρος ότι θα νιώσεις περισσότερο σαν στο σπίτι σου εκεί.
Ωστόσο, για χάρη της ευρετηρίασης της Google, αφήνω εδώ τις μεθόδους που δημιουργούν τις κεφαλίδες RIFF και BEXT, κάτι που δεν μπορούσα να βρω, κάτι που δυστυχώς έκανε τη διαδικασία να διαρκέσει περισσότερο από ό,τι θα ήθελα να παραδεχτώ.
public class RiffFile { /** * Creates a RIFF header with BEXT and fmt chunks * * @param sampleRate the sample rate of the audio (8000Hz, 44100Hz, 48000Hz, etc) times per second the audio is sampled * @param bitsPerSample the bits per sample (8bits, 16bits, 32bits, etc) * @param channels the number of channels (1 mono, 2 stereo, etc) * @param audioDataSize the size of the audio data in bytes * @return the RIFF header * @throws IOException if an I/O error occurs */ public static byte[] createRiffHeader(int sampleRate, short bitsPerSample, short channels, int audioDataSize) throws IOException { // calculate the byte rate, block align and file size int byteRate = sampleRate * channels * bitsPerSample / 8; short blockAlign = (short) (bitsPerSample * channels / 8); // stream that will carry the new RIFF file ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(byteArrayOutputStream); // riff header out.writeBytes("RIFF"); out.writeInt(Integer.reverseBytes(0)); out.writeBytes("WAVE"); // 9-12 Format always WAVE // bext chunk writeBextChunk(out); // fmt chunk out.writeBytes("fmt "); // 13-16 chunkID is "fmt " with trailing whitespace out.writeInt(Integer.reverseBytes(16)); // 17-20 size of this chunk, is 16 byts out.writeShort(Short.reverseBytes((short) 3)); // 21-22 (2 bytes) audioFormat (1 PCM integer, 3 IEEE 754 float) out.writeShort(Short.reverseBytes(channels)); // 23-24 (2 bytes) numChannels (1 mono, 2 stereo, 4, etc) out.writeInt(Integer.reverseBytes(sampleRate)); // 25-28 (4 bytes) sampleRate (8000, 44100, 48000, etc) out.writeInt(Integer.reverseBytes(byteRate)); // 29-32 (4 bytes) byteRate (sampleRate * numChannels * bitsPerSample/8) out.writeShort(Short.reverseBytes(blockAlign)); // 33-34 (2 bytes) blockAlign (numChannels * bitsPerSample/8) out.writeShort(Short.reverseBytes(bitsPerSample)); // 35-36 (2 bytes) bitsPerSample (8bits, 16bits, 32bits, etc) // data chunk out.writeBytes("data"); // 37-40 chunkID ID is "data" out.writeInt(Integer.reverseBytes(audioDataSize)); // 41-44 size of this chunk varies out.close(); // write the full size of the file on the 4-8 bytes byte[] outArr = byteArrayOutputStream.toByteArray(); int size = outArr.length - 8; ByteBuffer.wrap(outArr, 4, 4).order(ByteOrder.LITTLE_ENDIAN).putInt(size); return outArr; } private static void writeBextChunk(DataOutputStream out) throws IOException { // bext chunk out.writeBytes("bext"); out.writeInt(Integer.reverseBytes(256 + 32 + 32 + 10 + 8 + 8 + 8 + 2 + 180 + 4 + 4 + 4 + 4 + 4 + 180)); // bext chunk size (fixed size for BWF) // description 256 bytes writeToArray(out, 256, ""); // 256 bytes description writeToArray(out, 32, "ZOOM M3"); // 32 bytes originator writeToArray(out, 32, ""); // 32 bytes originator reference writeToArray(out, 10, "2023-10-01"); // 10 bytes origination date writeToArray(out, 8, "12:00:00"); // 8 bytes origination time writeToArray(out, 8, "12:00:00"); // 8 bytes time reference out.writeLong(Long.reverseBytes(0L)); // 8 bytes time reference out.writeShort(Short.reverseBytes((short) 0)); // 2 bytes version out.write(new byte[180]); // 180 bytes UMID out.writeFloat(0.0f); // 4 bytes loudness value out.writeFloat(0.0f); // 4 bytes loudness range out.writeFloat(0.0f); // 4 bytes max true peak level out.writeFloat(0.0f); // 4 bytes max momentary loudness out.writeFloat(0.0f); // 4 bytes max short term loudness // zoom m3 needs this bit to allow file to be read from "zoom m3 edit & play" writeToArray(out, 180, "A=PCM,F=48000,W=32,M=stereo,T=M3;VERSION=1.00;MSRAW=ON ;"); } }
Όπως μπορείτε να δείτε, δεν καταβλήθηκε μεγάλη προσπάθεια στο κομμάτι BEXT. Απλώς το δημιούργησα για να διασφαλίσω ότι το "Zoom M3 Edit & Play" θα είναι συμβατό.
Ελπίζω να είχατε μια ενδιαφέρουσα ανάγνωση. Προσπάθησα να σηκώσω την αυλαία της διαδικασίας σκέψης, διατηρώντας ταυτόχρονα αυτό το ενδιαφέρον. Ας ελπίσουμε ότι ο πραγματικός κώδικας είναι αυτονόητος και θα τον βρείτε εδώ:
https://github.com/wasteofserver/zoom_m3_mic_wav_data_recover/
Η πρόκληση δεν αφορούσε μόνο την ανάκτηση χαμένων ηχογραφήσεων - ήταν η κατανόηση του γιατί απέτυχαν τα παραδοσιακά εργαλεία και η ανάπτυξη μιας μεθόδου που λειτούργησε.
Αν και μπορεί να ήταν ευκολότερο να ηχογραφηθεί ξανά, η συγκίνηση της επίλυσης του παζλ έκανε την προσπάθεια να αξίζει τον κόπο. Στο τέλος, πήραμε μια προσαρμοσμένη λύση που επαναφέρει με επιτυχία τις εγγραφές Zoom M3 MicTrak.
Εάν βρεθείτε ποτέ σε παρόμοια κατάσταση, ελπίζουμε ότι αυτή η ανάλυση θα σας βοηθήσει. Και αν όχι, τουλάχιστον πρέπει να απολαύσετε μια μικρή περιπέτεια στον κόσμο της ανάκτησης δεδομένων.
Αυτή η ιστορία δημοσιεύτηκε για πρώτη φορά στη διεύθυνση https://wasteofserver.com/zoom-m3-mictrak-file-recovery/ . Μπορεί να θέλετε να το ελέγξετε εκεί για πιο πρόσφατες ενημερώσεις και σχόλια.