Το HotXLS γράφει workbooks Excel 5.0/95 (BIFF5) με XOR obfuscation που η Excel 16 ανοίγει μόνο όταν τρεις λεπτομέρειες ταιριάζουν ακριβώς με το [MS-OFFCRYPTO]: το κλειδί FILEPASS πρέπει να είναι CreateXorKey_Method1(password), το XOR array 16 byte πρέπει να χτίζεται με XorRor (περιστροφή δεξιά κατά ένα bit), και κάθε byte πρέπει να χρησιμοποιεί XorArrayIndex = (stream offset + record length) mod 16. Το HotXLS έφτιαξε το index στο v2.384.47 και το κλειδί και την περιστροφή στο v2.384.54. Πριν από αυτό, κάθε BIFF5 αρχείο με κωδικό που παρήγαγε άνοιγε μια χαρά στο HotXLS και απέτυχε στην Excel
Η τελευταία εκείνη πρόταση είναι όλη η ιστορία σε μικρότυπο. Ένας reader και ένας writer που μοιράζονται την ίδια λάθος ιδέα συμφωνούν μεταξύ τους τέλεια, οπότε τα round-trip tests μένουν πράσινα ενώ ο μόνος καταναλωτής που μετράει λέει όχι. Η Excel 16 είπε όχι δύο φορές, με δύο διαφορετικά μηνύματα, και κάθε μήνυμα έδειχνε σε διαφορετικό στρώμα του σχήματος. Αυτό το άρθρο διασχίζει εκείνα τα στρώματα με τη σειρά που τα ελέγχει η Excel, με λεπτομέρεια σε επίπεδο byte που σας χρησιμεύει είτε καλείτε το HotXLS είτε γράφετε δικό σας BIFF reader
Τι αποθηκεύει στην πραγματικότητα το BIFF XOR obfuscation;
Το BIFF XOR obfuscation αποθηκεύει μόνο δύο λέξεις 16 bit στο αρχείο, και όλα τα άλλα ξαναϋπολογίζονται από τον κωδικό. Η εγγραφή FILEPASS ($002F) κάθεται αμέσως μετά το workbook globals BOF, και σε BIFF5 αρχείο το body της είναι ακριβώς 4 bytes: το XOR key ακολουθούμενο από τον password verifier. Δεν υπάρχει salt, ούτε αναγνωριστικό αλγορίθμου, ούτε κρυπτογραφημένο verifier blob του είδους που κουβαλάνε τα σχήματα RC4 και AES
Από εκείνες τις δύο λέξεις ένας reader ξαναχτίζει τρία πράγματα:
- Ο verifier, ένα hash 16 bit των bytes του κωδικού με XOR
$CE4B. Η σύγκρισή του με την αποθηκευμένη λέξη είναι ο έλεγχος κωδικού, και ο μόνος - Το XOR key, μια τιμή 16 bit από το
CreateXorKey_Method1στο [MS-OFFCRYPTO] §2.3.7.2, που το οδηγούν δύο πίνακες σταθερών (InitialCode, 15 λέξεις, καιXorMatrix, 105 λέξεις) - Το XOR array, 16 bytes από τα bytes του κωδικού συμπληρωμένα με σταθερό pad 16 byte, καθένα με XOR το χαμηλό byte του κλειδιού (ζυγές θέσεις) ή το υψηλό (μονές θέσεις), και μετά περιστραμμένα δεξιά κατά ένα bit
Τα record headers μένουν σε plain text, όπως και μια χούφτα ολόκληρες εγγραφές που το σχήμα εξαιρεί, μεταξύ των οποίων BOF, FILEPASS και INTERFACEHDR. Κάθε άλλο record body μετασχηματίζεται byte προς byte: περιστροφή αριστερά κατά 5 bits, μετά XOR με μια εγγραφή του array 16 byte. Η αποκρυπτογράφηση, που το [MS-OFFCRYPTO] §2.3.7.3 γράφει ως DecryptData_Method1, είναι ο καθρέφτης: πρώτα XOR, μετά περιστροφή δεξιά κατά 5
Στο HotXLS δεν αγγίζετε ποτέ τίποτα από αυτά απευθείας. Ορίστε έναν κωδικό, διαλέξτε το format, και το SaveAs εκπέμπει FILEPASS και μετασχηματίζει το stream:
uses
SysUtils, lxHandle;
procedure SaveLegacyProtectedBook(const FileName: string);
var
Wb: IXLSWorkbook;
begin
Wb := TXLSWorkbook.Create;
Wb.Sheets.Add.Name := 'Ledger';
Wb.Sheets[1].Range['A1', 'A1'].Value := 'Account';
Wb.Sheets[1].Range['B1', 'B1'].Value := 1250.75;
// Το BIFF5 υποστηρίζει μόνο XOR obfuscation· το xletAuto θα το διάλεγε κι αυτό
Wb.EncryptionType := xletXor;
// Κρατήστε το ASCII και το πολύ 15 χαρακτήρες (βλ. παρακάτω)
Wb.EncryptionPassword := 'secret';
if Wb.SaveAs(FileName, xlExcel5) <> 1 then
raise Exception.Create('BIFF5 save failed');
end;
Γιατί η Excel λέει ότι ο κωδικός είναι λάθος όταν ο verifier ταιριάζει;
Η Excel απορρίπτει τον κωδικό επειδή δεν εμπιστεύεται το αποθηκευμένο κλειδί: η Excel παράγει το κλειδί από τον πληκτρολογημένο κωδικό με CreateXorKey_Method1 και το συγκρίνει με τη λέξη κλειδί του FILEPASS, οπότε αρχείο με οποιοδήποτε άλλο κλειδί αποτυγχάνει τον έλεγχο κωδικού ακόμα κι όταν ο verifier είναι σωστός. Το specification περιγράφει το κλειδί ως έξοδο του κωδικού, όχι ως ελεύθερη παράμετρο, και η Excel 16 επιβάλλει εκείνο το διάβασμα
Ο writer του HotXLS πριν το v2.384.54 γέμιζε τη λέξη κλειδί με δύο τυχαία bytes. Στο χαρτί μοιάζει αβλαβές, αφού ο verifier είναι ο τεκμηριωμένος έλεγχος κωδικού και το array χτίζεται από ό,τι κλειδί δηλώνει το αρχείο. Το ίδιο το HotXLS διάβαζε εκείνα τα αρχεία χωρίς πρόβλημα, γιατί ο reader του έπαιρνε το κλειδί από το FILEPASS ως δεδομένο. Η Excel 16, με το ίδιο αρχείο και τον σωστό κωδικό, απάντησε ότι ο κωδικός δεν ήταν σωστός. Από το v2.384.54 το κλειδί παράγεται, οπότε το FILEPASS για τον κωδικό secret κρατά πάντα κλειδί $014D και verifier $DAA7, τιμές διασταυρωμένες με ανεξάρτητη υλοποίηση του specification
Η ίδια η παραγωγή είναι σύντομη μόλις μπουν οι δύο πίνακες. Διασχίστε τον κωδικό προς τα πίσω, κοιτάξτε το bit 6 κάθε byte επτά φορές ενώ το μετατοπίζετε αριστερά, και κάντε XOR με μία εγγραφή XorMatrix κάθε φορά που το bit είναι 1. Το παρακάτω είναι σκίτσο αρχής που αναπαράγει τον αλγόριθμο του specification και ταιριάζει με την υλοποίηση του HotXLS· δεν είναι API του HotXLS:
// Σκίτσο αρχής του [MS-OFFCRYPTO] 2.3.7.2 CreateXorKey_Method1
// και CreateXorArray_Method1 (μόνο επεξήγηση, όχι API του HotXLS)
type
TXorArray = array [0..15] of Byte;
function DemoCreateXorKey(const Password: AnsiString): Word;
const
InitialCode: array [0..14] of Word = ($E1F0, $1D0F, $CC9C, $84C0, $110C,
$0E10, $F1CE, $313E, $1872, $E139, $D40F, $84F9, $280C, $A96A, $4EC3);
XorMatrix: array [0..104] of Word = (
$AEFC, $4DD9, $9BB2, $2745, $4E8A, $9D14, $2A09,
$7B61, $F6C2, $FDA5, $EB6B, $C6F7, $9DCF, $2BBF,
$4563, $8AC6, $05AD, $0B5A, $16B4, $2D68, $5AD0,
$0375, $06EA, $0DD4, $1BA8, $3750, $6EA0, $DD40,
$D849, $A0B3, $5147, $A28E, $553D, $AA7A, $44D5,
$6F45, $DE8A, $AD35, $4A4B, $9496, $390D, $721A,
$EB23, $C667, $9CEF, $29FF, $53FE, $A7FC, $5FD9,
$47D3, $8FA6, $0F6D, $1EDA, $3DB4, $7B68, $F6D0,
$B861, $60E3, $C1C6, $93AD, $377B, $6EF6, $DDEC,
$45A0, $8B40, $06A1, $0D42, $1A84, $3508, $6A10,
$AA51, $4483, $8906, $022D, $045A, $08B4, $1168,
$76B4, $ED68, $CAF1, $85C3, $1BA7, $374E, $6E9C,
$3730, $6E60, $DCC0, $A9A1, $4363, $86C6, $1DAD,
$3331, $6662, $CCC4, $89A9, $0373, $06E6, $0DCC,
$1021, $2042, $4084, $8108, $1231, $2462, $48C4);
var
Len, I, Bit, Element: Integer;
Ch: Byte;
begin
Result := 0;
Len := Length(Password);
if Len > 15 then
Len := 15; // το κλειδί βλέπει μόνο 15 bytes
if Len = 0 then
Exit;
Result := InitialCode[Len - 1];
Element := $68; // τελευταία εγγραφή XorMatrix
for I := Len downto 1 do
begin
Ch := Ord(Password[I]);
for Bit := 1 to 7 do
begin
if (Ch and $40) <> 0 then
Result := Result xor XorMatrix[Element];
Ch := Byte(Ch shl 1);
Dec(Element);
end;
end;
end;
function XorRor(B, KeyByte: Byte): Byte;
begin
B := B xor KeyByte;
Result := Byte((B shr 1) or (B shl 7)); // περιστροφή δεξιά κατά ένα bit
end;
procedure DemoCreateXorArray(const Password: AnsiString; out Arr: TXorArray);
const
PadArray: TXorArray = ($BB, $FF, $FF, $BA, $FF, $FF, $B9, $80,
$00, $BE, $0F, $00, $BF, $0F, $00, $00);
var
Key: Word;
Len, I: Integer;
begin
Key := DemoCreateXorKey(Password);
Len := Length(Password);
if Len > 16 then
Len := 16;
for I := 0 to Len - 1 do
Arr[I] := Ord(Password[I + 1]);
for I := Len to 15 do
Arr[I] := PadArray[I - Len];
for I := 0 to 15 do
if Odd(I) then
Arr[I] := XorRor(Arr[I], Byte(Key shr 8))
else
Arr[I] := XorRor(Arr[I], Byte(Key and $FF));
end;
Για τον secret αυτό παράγει το array 1F 32 17 B9 14 BA 7B 7F 59 DD 59 7F 7A C0 A6 DF, χρήσιμο fixture αν τεστάρετε δικό σας reader
Γιατί το σωστό κλειδί παράγει ακόμα κατεστραμμένο αρχείο;
Σωστό κλειδί παράγει ακόμα κατεστραμμένο αρχείο όταν το XOR array περιστρέφεται ανάποδα: το [MS-OFFCRYPTO] ορίζει το βήμα array ως XorRor, περιστροφή δεξιά κατά ένα bit, και ένα array περιστραμμένο αριστερά κατά δύο αποκρυπτογραφεί κάθε record body σε θόρυβο. Η διόρθωση του κλειδιού έσπρωξε την Excel 16 πέρα από την προτροπή κωδικού και κατευθείαν σε διαφορετικό σφάλμα, μια αναφορά ότι το αρχείο έχει πρόβλημα και δεν μπορεί να ανοίξει
Ο παλιός κώδικας του HotXLS περιστρεφόσε κάθε byte του array αριστερά κατά 2 bits, μια μορφή που κυκλοφορεί σε αρκετές υλοποιήσεις BIFF. Επειδή το HotXLS χρησιμοποιούσε την ίδια περιστροφή και στις δύο πλευρές, ο δικός του reader δεν το πρόσεξε ποτέ. Η Excel 16 δεν σώζει πλέον αρχεία Excel 5.0/95, και δεν προσφέρει XOR όταν σώζει BIFF8, οπότε δεν υπήρχε εγγενές δείγμα Excel για diff. Η απόδειξη έπρεπε να έρθει από την άλλη κατεύθυνση: γράψε ένα plain-text BIFF5 stream, κωδικοποίησέ το ξανά με οκτώ τρόπους, και άφησε την Excel 16 να ανοίξει κάθε παραλλαγή. Οι οκτώ παραλλαγές σταυρώνονταν σε τρεις ανεξάρτητες επιλογές:
| Επιλογή | Επιλογή A | Επιλογή B |
|---|---|---|
| Περιστροφή array | XorRor (rotate right 1) | Περιστροφή αριστερά 2 |
| Index array | (offset + record length) mod 16 | offset mod 16 |
| Σειρά μετασχηματισμού byte | Περιστροφή αριστερά 5, μετά XOR | XOR, μετά περιστροφή αριστερά 5 |
Η Excel 16 άνοιξε ακριβώς δύο από τις οκτώ: XorRor με rotate-then-XOR και το index record-length, και μία παραλλαγή που απλώς φαίνεται διαφορετική. Το rotate-left-2 με XOR-then-rotate και το ίδιο index είναι η ίδια συνάρτηση μεταμφιεσμένη. Η περιστροφή κατανέμεται πάνω στο XOR, οπότε το rol5(p xor rol2(b)) ισούται με rol5(p) xor rol7(b), και σε τιμή 8 bit μια περιστροφή αριστερά κατά 7 είναι περιστροφή δεξιά κατά 1. Με λίγα λόγια, rol5 ∘ rol2 = ror1, γι’ αυτό το array rotate-left-2 φαίνεται πειστικό μεμονωμένα: είναι σωστό μόνο μαζί με την αντίστροφη σειρά μετασχηματισμού. Ζευγάρι με τη σειρά του specification, καταστρέφει κάθε μετασχηματισμένο byte
Το ίδιο πείραμα έκλεισε και δεύτερη ερώτηση. Οι παραλλαγές που έριξναν το record length από το index απέτυχαν όλες, που επιβεβαίωσε τον κανόνα index που το HotXLS είχε υιοθετήσει ένα release νωρίτερα βασισμένο μόνο στο κείμενο του specification
Πώς υπολογίζεται το XorArrayIndex για κάθε byte;
Το XorArrayIndex για ένα byte είναι το offset του στο workbook stream συν το μήκος ολόκληρου του record data στο οποίο ανήκει, mod 16. Το index άρα ξεκινά από τιμή ανάλογη της εγγραφής σε κάθε εγγραφή και αυξάνεται κατά ένα ανά byte μέσα της. Το pseudocode του specification ονομάζει τις εισόδους FileOffset και Data.Length, που διαβάζεται εύκολα λάθος ως το σκέτο offset αρχής της εγγραφής, και εκείνη η παρανόηση είναι ακριβώς όσα μετέδιδε το HotXLS μέχρι το v2.384.47
Τρεις λεπτομέρειες αποφασίζουν αν τα indices σας ευθυγραμμίζονται με την Excel:
- Το record header 4 byte δεν μετασχηματίζεται ποτέ, αλλά εξακολουθεί να καταλαμβάνει θέσεις stream, οπότε το πρώτο body byte μιας εγγραφής κάθεται στο header offset + 4
- Ο όρος μήκους είναι το πλήρες record data length, όχι ο αριθμός των bytes που μετασχηματίστηκαν στην πραγματικότητα
- Το BOUNDSHEET είναι εν μέρει σκέτο: τα πρώτα 4 bytes του, το
lbPlyPos, το stream offset του sheet BOF, μένουν αναγνώσιμα ώστε ένας parser να εντοπίζει sheets. Εκείνα τα 4 bytes προσπερνιούνται από τον μετασχηματισμό αλλά μετρούν και στο offset και στο record length
Βαλμένα μαζί, ο ανά εγγραφή μετασχηματισμός είναι λίγες γραμμές. Και πάλι, αυτό είναι σκίτσο του κανόνα, όχι κάτι που χρειάζεται να καλέσετε:
function Rol8(B: Byte; N: Integer): Byte;
begin
Result := Byte((B shl N) or (B shr (8 - N)));
end;
// Κρυπτογράφησε ένα record body στη θέση του. Το BodyPos είναι το stream offset
// του Body[0], δηλαδή header offset + 4. Το PlainPrefix είναι 4 για
// BOUNDSHEET, το πλήρες μήκος για BOF / FILEPASS, 0 για τις περισσότερες εγγραφές
procedure DemoObfuscateRecord(var Body: array of Byte; RecordLength: Word;
PlainPrefix: Integer; BodyPos: LongWord; const Arr: TXorArray);
var
I: Integer;
begin
for I := PlainPrefix to RecordLength - 1 do
Body[I] := Rol8(Body[I], 5) xor
Arr[(BodyPos + LongWord(I) + RecordLength) mod 16];
end;
// Το διάβασμα είναι ο καθρέφτης: B := Body[I] xor Arr[...];
// μετά περιστροφή δεξιά κατά 5, δηλαδή Rol8(B, 3)
Πριν το v2.384.47 ο reader του HotXLS υπολόγιζε το index από τη θέση του stream σκέτη και ο writer χρησιμοποιούσε το σκέτο byte offset. Και τα δύο αγνοούσαν το record length, οπότε ξανά τα δύο μισά συμφωνούσαν μεταξύ τους και με κανέναν άλλον. Ένας decoder γραμμένος ανεξάρτητα διάβασε σωστά την έξοδο του v2.384.47 και την παλαιότερη έξοδο ως σκουπίδια, και το test οκτώ παραλλαγών στην Excel 16 επιβεβαίωσε αργότερα τον κανόνα απέναντι στον πραγματικό στόχο
Τι γίνεται με τα XOR αρχεία που έγραψαν παλαιότερες εκδόσεις HotXLS;
Το HotXLS εξακολουθεί να διαβάζει τα δικά του XOR αρχεία πριν το v2.384.54 ελέγχοντας το κλειδί FILEPASS: όταν το αποθηκευμένο κλειδί ισούται με το κλειδί που παράγεται από τον κωδικό, ο reader χτίζει το array XorRor του specification, και όταν διαφέρει, ο reader μεταχειρίζεται το αρχείο ως παλαιότερο αρχείο HotXLS και ξαναχτίζει το array rotate-left-2. Τα αρχεία γραμμένα από Excel κουβαλάνε πάντα το παραγόμενο κλειδί, οπότε παίρνουν πάντα τη διαδρομή του specification
Ο έλεγχος είναι ευρετική με ακριβές ποσοστό αστοχίας. Παλιό αρχείο του οποίου το τυχαίο κλειδί έτυχε να ισούται με το παραγόμενο θα διαβαζόταν με λάθος array, και η πιθανότητα είναι 1 στις 65.536. Το fallback καλύπτει μόνο την περιστροφή του array· ο κανόνας index δεν αλλάζει, οπότε τα αρχεία που σώζει είναι όσα γράφτηκαν μεταξύ v2.384.47 και v2.384.53. Αν ακόμα κρατάτε BIFF5 XOR αρχεία από εκείνο το παράθυρο, ανοίξτε τα με το τρέχον HotXLS και σώστε τα ξανά για να πάρετε αρχείο που η Excel δέχεται
Δύο λεπτομέρειες κωδικού ισχύουν για κάθε αρχείο, παλιό ή νέο:
- Μήκος. Το
CreateXorKey_Method1διαβάζει μόνο τα πρώτα 15 bytes του κωδικού, που είναι το όριο του specification. Το HotXLS εφαρμόζει εκείνο το πλαφόν στο κλειδί και κρατά τον verifier και το array στους συνηθισμένους κανόνες τους, πλήρους μήκους και 16 byte, συνεπώς και στις δύο πλευρές. Η ίδια η Excel αρνείται κωδικούς πάνω από 15 χαρακτήρες γι’ αυτό το format, οπότε να θεωρείτε το 15 το πραγματικό μέγιστο - Σύνολο χαρακτήρων. Το HotXLS μετατρέπει τον κωδικό σε bytes μέσω της ANSI code page του συστήματος. Το specification περιγράφει να παίρνεις το low byte κάθε χαρακτήρα UTF-16, που συμφωνεί για ASCII. Χωρίς δείγματα Excel προστατευμένα με μη-ASCII κωδικούς δεν υπάρχει ground truth για τα υπόλοιπα, οπότε μείνετε σε ASCII κωδικούς για XOR αρχεία
Στην πλευρά ανάγνωσης, το TXLSWorkbook.OnPassword σας αφήνει να ζητήσετε κωδικό όταν το Open συναντά εγγραφή FILEPASS. Το event είναι TXLSPasswordEvent με var PassWord: WideString και var Retry: Boolean; βάλτε Retry σε True για να ξαναδοκιμάσετε, έως τρεις επαναλήψεις:
procedure TImportForm.WorkbookPassword(Sender: TObject;
var PassWord: WideString; var Retry: Boolean);
var
S: string;
begin
S := '';
Retry := InputQuery('Protected workbook', 'Password:', S);
PassWord := S;
end;
procedure TImportForm.ImportLegacyFile(const FileName: string);
var
Wb: IXLSWorkbook;
Rc: Integer;
begin
Wb := TXLSWorkbook.Create;
Wb.OnPassword := WorkbookPassword;
Rc := Wb.Open(FileName);
// -1003: χρειάζεται κωδικός αλλά δεν δόθηκε· -1005: λάθος κωδικός
if Rc <> 1 then
raise Exception.CreateFmt('Cannot open %s (code %d)', [FileName, Rc]);
ShowMessage(VarToStr(Wb.Sheets[1].Range['A1', 'A1'].Value));
end;
Αν ξέρετε ήδη τον κωδικό, το Open(FileName, APassWord) προσπερνά τελείως το event
Είναι το XOR obfuscation αρκετά ασφαλές για οτιδήποτε;
Το BIFF XOR obfuscation δεν είναι κρυπτογράφηση και δεν προστατεύει τίποτα από έναν αποφασισμένο αναγνώστη. Ο έλεγχος κωδικού είναι verifier 16 bit, το κλειδί είναι 16 bits, και το array 16 byte επαναλαμβάνεται σε όλο το stream, οπότε προβλέψιμα περιεχόμενα εγγραφών BIFF εκθέτουν bytes του array χωρίς κανέναν κωδικό. Το HotXLS γράφει XOR μόνο επειδή τα αρχεία Excel 5.0/95 δεν έχουν άλλη επιλογή, και ο λόγος να παράγετε τέτοια αρχεία σήμερα είναι ένας legacy καταναλωτής που δεν διαβάζει τίποτα νεότερο
Η classic engine διαλέγει το σχήμα μέσω TXLSWorkbook.EncryptionType, και ο συνδυασμός με το save format ελέγχεται αυστηρά:
xletAuto(προεπιλογή) γράφει RC4 CryptoAPI γιαxlExcel97και XOR γιαxlExcel5, ό,τι έγραφε η ίδια η Excel για κάθε formatxletXorείναι έγκυρο μόνο για BIFF5· μεxlExcel97η αποθήκευση πετάει exception αντί να πέσει σιωπηλά σε άλλο σχήμαxletRC4καιxletRC4CryptoAPIείναι μόνο BIFF8, και το να τα ζητήσετε σε αποθήκευση BIFF5 πετάει επίσης exception
Το RC4 είναι κι αυτό παλιό, και οι λεπτομέρειες διαλειτουργικότητάς του καλύπτονται στο γιατί η Excel απορρίπτει κρυπτογραφημένο workbook με σωστό κωδικό. Αν ο παραλήπτης διαβάζει XLSX, χρησιμοποιήστε τη XLSX engine: το TXLSXWorkbook.SaveAsEncryptedAgile γράφει Agile Encryption (hashing κωδικού SHA-512 με spin count 100.000 επαναλήψεων και AES-256-CBC), το format που γράφουν εξ ορισμού η Excel 2010 και νεότερες, ενώ το SaveAsEncrypted γράφει την παλαιότερη AES-128 Standard Encryption. Τα trade-offs ανάμεσά τους είναι στο κρυπτογράφηση XLSX αρχείων με AES στο Delphi, και η πλευρά ανάγνωσης καλύπτεται στο ανάγνωση Agile-κρυπτογραφημένων αρχείων Excel με το HotXLS
Σύντομη αναφορά: BIFF XOR obfuscation που δέχεται η Excel 16
- FILEPASS (
$002F) ακολουθεί το globals BOF· σε BIFF5 το body του είναι 4 bytes: κλειδί, μετά verifier - Κλειδί =
CreateXorKey_Method1(password)κατά [MS-OFFCRYPTO] §2.3.7.2, ποτέ τυχαίο· γιαsecretείναι$014D - Array = bytes κωδικού + pad, XOR το χαμηλό byte κλειδιού σε ζυγές θέσεις και το υψηλό σε μονές, μετά XorRor (rotate right 1)
- Κρυπτογράφησε byte: rotate left 5, μετά XOR· αποκρυπτογράφησε: XOR, μετά rotate right 5 (§2.3.7.3)
- XorArrayIndex = (byte stream offset + record data length) mod 16· τα headers και τα σκέτα προθέματα μετρούν στο offset
- Το BOUNDSHEET κρατά τα πρώτα 4 bytes σκέτα· BOF, FILEPASS και INTERFACEHDR μένουν τελείως σκέτα
- Κωδικοί: ASCII, το πολύ 15 χαρακτήρες
- HotXLS: index διορθωμένο στο v2.384.47, κλειδί και XorRor διορθωμένα στο v2.384.54, παλαιότερα XOR αρχεία HotXLS αναγνωρίζονται από ασυμφωνία κλειδιού
- Για πραγματική προστασία χρησιμοποιήστε τουλάχιστον BIFF8 RC4 CryptoAPI, ή XLSX Agile Encryption
Το HotXLS διαχειρίζεται προστασία κωδικού BIFF5 και BIFF8, XLSX Standard και Agile Encryption, και το password callback της ανάγνωσης από μία βιβλιοθήκη Delphi και C++Builder, με τις λεπτομέρειες διαλειτουργικότητας παραπάνω τακτοποιημένες για εσάς. Δείτε το HotXLS Delphi spreadsheet component για εκδόσεις, platforms και δοκιμαστική λήψη