مقاله فنی

مبهم‌سازی XOR در XLS با HotXLS: ساخت کلید و XorRor

HotXLS کارپوشه‌های مبهم‌شده با XOR یعنی Excel 5.0/95 (BIFF5) را طوری می‌نویسد که Excel 16 فقط وقتی آن‌ها را باز می‌کند که سه جزئیات دقیقاً با [MS-OFFCRYPTO] جور دربیاید: کلید FILEPASS باید CreateXorKey_Method1(password) باشد، آرایه 16 بایتی XOR باید با XorRor ساخته شود (چرخش یک‌بیتی به راست)، و هر بایت باید از XorArrayIndex = (stream offset + record length) mod 16 استفاده کند. HotXLS اندیس را در v2.384.47 درست کرد و کلید و چرخش را در v2.384.54. قبل از آن، هر فایل BIFF5 محافظت‌شده با رمز عبوری که تولید می‌کرد در HotXLS درست باز می‌شد و در Excel خطا می‌داد

آن جمله آخر کل ماجرا در مقیاس کوچک است. یک reader و یک writer که ایده غلط مشترکی دارند با هم کاملاً کنار می‌آیند، پس تست‌های round-trip سبز می‌مانند در حالی که تنها مصرف‌کننده مهم می‌گوید نه. Excel 16 دو بار نه گفت، با دو پیام متفاوت، و هر پیام به لایه متفاوتی از طرح اشاره می‌کرد. این مقاله آن لایه‌ها را به همان ترتیبی که Excel چک‌شان می‌کند مرور می‌کند، با جزئیات در سطح بایت که چه HotXLS را صدا بزنید چه BIFF reader خودتان را بنویسید به کارتان می‌آید

مبهم‌سازی XOR در BIFF واقعاً چه چیزی در فایل ذخیره می‌کند؟

مبهم‌سازی XOR در BIFF فقط دو word شانزده‌بیتی در فایل ذخیره می‌کند و بقیه همه از رمز عبور دوباره محاسبه می‌شود. رکورد FILEPASS یعنی $002F بلافاصله بعد از workbook globals BOF می‌نشیند، و در یک فایل BIFF5 بدنه‌اش دقیقاً 4 بایت است: کلید XOR و بعدش password verifier. نه salt در کار است، نه شناسه الگوریتم، و نه blob verifier رمزنگاری‌شده از جنس طرح‌های RC4 و AES

از همین دو word، یک reader سه چیز را بازسازی می‌کند:

  • verifier، یک هش 16 بیتی از بایت‌های رمز عبور که با $CE4B XOR شده. مقایسه‌اش با word ذخیره‌شده همان چک رمز عبور است، و تنها چک
  • کلید XOR، یک مقدار 16 بیتی از CreateXorKey_Method1 در [MS-OFFCRYPTO] §2.3.7.2، که دو جدول ثابت یعنی InitialCode با 15 word و XorMatrix با 105 word آن را می‌رانند
  • آرایه XOR، 16 بایت ساخته‌شده از بایت‌های رمز عبور با padding یک pad ثابت 16 بایتی، هر کدام XOR شده با بایت کم کلید (موقعیت‌های زوج) یا بایت بالا کلید (موقعیت‌های فرد)، و بعد چرخش یک‌بیتی به راست

هدر رکوردها به‌صورت متن ساده می‌مانند، و تعداد کمی رکورد کامل هم که طرح از آن‌ها معاف است همین‌طور، از جمله BOF و FILEPASS و INTERFACEHDR. بدنه هر رکورد دیگر بایت به بایت تبدیل می‌شود: چرخش 5 بیتی به چپ، بعد XOR با یکی از درایه‌های آرایه 16 بایتی. رمزگشایی که [MS-OFFCRYPTO] §2.3.7.3 با نام DecryptData_Method1 شرحش می‌دهد تصویر آینه‌ای همان است: اول XOR، بعد چرخش 5 بیتی به راست

در HotXLS هیچ‌وقت مستقیم سر و کار با هیچ‌کدام از این‌ها ندارید. یک رمز عبور ست کنید، فرمت را انتخاب کنید، و 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 را پشتیبانی می‌کند؛ xletAuto هم همین را انتخاب می‌کرد
  Wb.EncryptionType := xletXor;
  // اسکی نگهش دارید و حداکثر 15 کاراکتر (پایین را ببینید)
  Wb.EncryptionPassword := 'secret';

  if Wb.SaveAs(FileName, xlExcel5) <> 1 then
    raise Exception.Create('BIFF5 save failed');
end;

چرا وقتی verifier درست است Excel می‌گوید رمز عبور غلط است؟

Excel رمز عبور را رد می‌کند چون به کلید ذخیره‌شده اعتماد نمی‌کند: Excel کلید را از رمز عبور تایپ‌شده با CreateXorKey_Method1 می‌سازد و با word کلید در FILEPASS مقایسه می‌کند، پس فایلی که کلیدش چیز دیگری باشد حتی با verifier درست هم چک رمز عبور را رد می‌شود. مشخصات کلید را خروجی رمز عبور توصیف می‌کند نه پارامتری آزاد، و Excel 16 همین خوانش را الزامی می‌کند

نویسنده HotXLS قبل از v2.384.54 word کلید را با دو بایت تصادفی پر می‌کرد. روی کاغذ بی‌ضرر به نظر می‌رسد، چون verifier همان چک مستند رمز عبور است و آرایه هم از هر کلیدی که فایل declare کرده ساخته می‌شود. خود HotXLS این فایل‌ها را بدون دردسر می‌خواند، چون reader آن کلید را همان‌طور که هست از FILEPASS می‌گرفت. Excel 16 با همان فایل و رمز عبور درست جواب می‌داد که رمز عبور درست نیست. از v2.384.54 کلید مشتق می‌شود، پس FILEPASS برای رمز عبور secret همیشه کلید $014D و verifier $DAA7 را دارد، مقادیری که با یک پیاده‌سازی مستقل از مشخصات cross-check شده‌اند

نمودار HotXLS از رکورد FILEPASS مربوط به مبهم‌سازی XOR در BIFF5 که Excel 16 موقع باز کردن چک می‌کند: بدنه چهار بایتی ساده کلید XOR و verifier رمز عبور را نگه می‌دارد، Excel کلید را از رمز عبور تایپ‌شده با CreateXorKey_Method1 می‌سازد و کلید تصادفی را حتی وقتی verifier می‌خواند با خطای رمز عبور رد می‌کند؛ HotXLS برای secret کلید مشتق‌شده 014D را ذخیره می‌کند
بدنه FILEPASS فقط دو word است، اما Excel کلید را از رمز عبور شما دوباره می‌سازد و مقایسه می‌کند؛ کلید پرشده با بایت‌های تصادفی چک را رد می‌کند حتی با verifier درست، و برای همین HotXLS از v2.384.54 آن را مشتق می‌کند

خود derivation وقتی دو جدول سر جایشان باشند کوتاه است. رمز عبور را از آخر به اول بروید، هفت بار بیت 6 هر بایت را نگاه کنید در حالی که آن را به چپ شیفت می‌کنید، و هر بار که بیت یک بود یکی از درایه‌های XorMatrix را XOR کنید. چیزی که می‌بینید یک طرح مفهومی است که الگوریتم مشخصات را بازتولید می‌کند و با پیاده‌سازی 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 بایت را می‌بیند
  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));   // چرخش یک‌بیتی به راست
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 این تابع آرایه 1F 32 17 B9 14 BA 7B 7F 59 DD 59 7F 7A C0 A6 DF را تولید می‌کند، که اگر reader خودتان را تست می‌کنید یک fixture دست‌دومن دردگیر است

نمودار HotXLS از ساخت آرایه XOR برای مبهم‌سازی BIFF5: شانزده بایت که از رمز عبور و یک pad ثابت seed می‌شوند در موقعیت‌های زوج با بایت کم کلید و در موقعیت‌های فرد با بایت بالا کلید XOR می‌شوند، بعد با XorRor یک‌بیتی به راست می‌چرخند و برای رمز عبور secret نتیجه fixture یعنی 1F 32 17 B9 می‌شود
بایت‌های آرایه از رمز عبور و pad و دو بایت کلید می‌آیند و یک چرخش هم در انتهاست؛ چرخش 2 بیتی به چپ فقط وقتی جواب می‌داد که تبدیل بایت به ترتیب معکوس اجرا شود، و Excel از ترتیب مشخصات پیروی می‌کند

چرا کلید درست هم هنوز فایل خراب تولید می‌کند؟

یک کلید درست وقتی آرایه XOR به سمت اشتباه بچرخد همچنان فایل خراب تولید می‌کند: [MS-OFFCRYPTO] مرحله آرایه را XorRor تعریف می‌کند، یعنی چرخش یک‌بیتی به راست، و آرایه‌ای که به چپ دو بیت بچرخد بدنه هر رکورد را به آشغال رمزگشایی می‌کند. فیکس کلید Excel 16 را از پرامپت رمز عبور رد کرد و مستقیم به یک خطای متفاوت رساند، گزارشی که فایل مشکل دارد و قابل باز کردن نیست

کد قدیمی HotXLS هر بایت آرایه را 2 بیت به چپ می‌چرخاند، فرمی که در چند پیاده‌سازی BIFF دست به دست می‌چرخد. چون HotXLS همان چرخش را در دو طرف استفاده می‌کرد، reader خودش هرگز متوجه نشد. Excel 16 دیگر نمی‌تواند فایل‌های Excel 5.0/95 ذخیره کند و هنگام ذخیره BIFF8 هم XOR پیشنهاد نمی‌دهد، پس نمونه بومی Excel برای diff کردن وجود نداشت. شواهد باید از جهت دیگر می‌آمد: یک stream بگیرید که کاملاً متن ساده است، آن را هشت جور دوباره کد کنید، و بگذارید Excel 16 هر واریانت را باز کند. آن هشت واریانت سه انتخاب مستقل را پوشش می‌دادند:

انتخابگزینه Aگزینه B
چرخش آرایهXorRor (چرخش به راست 1)چرخش به چپ 2
اندیس آرایه(offset + record length) mod 16offset mod 16
ترتیب تبدیل بایتچرخش به چپ 5، بعد XORXOR، بعد چرخش به چپ 5

Excel 16 از هشت واریانت دقیقاً دو تا را باز کرد: XorRor با چرخش-بعد-XOR و اندیس مبتنی بر طول رکورد، و یک واریانت که فقط متفاوت به نظر می‌رسید. چرخش-به-چپ-2 با XOR-بعد-چرخش و همان اندیس همان تابع است با مبدل لباس. چرخش روی XOR توزیع می‌شود، پس rol5(p xor rol2(b)) برابر rol5(p) xor rol7(b) است، و روی مقدار 8 بیتی چرخش به چپ 7 همان چرخش به راست 1 است. خلاصه اینکه rol5 ∘ rol2 = ror1، و برای همین آرایه چرخش-به-چپ-2 در تنهایی باورپذیر می‌شود: فقط همراه ترتیب معکوس تبدیل درست است. جفت با ترتیب مشخصات، هر بایت تبدیل‌شده را خراب می‌کند

همان آزمایش یک سؤال دوم را هم حل کرد. واریانت‌هایی که طول رکورد را از اندیس حذف می‌کردند همه شکست خوردند، و این قاعده اندیسی را تأیید کرد که HotXLS یک نسخه قبل‌تر فقط به اتکای متن مشخصات پذیرفته بود

XorArrayIndex برای هر بایت چطور محاسبه می‌شود؟

XorArrayIndex برای یک بایت، offset آن در workbook stream به‌علاوه طول کل داده رکوردی که به آن تعلق دارد، mod 16 است. پس اندیس برای هر رکورد از مقداری وابسته به همان رکورد شروع می‌شود و داخل آن بایت به بایت یکی‌یکی جلو می‌رود. شبه‌کد مشخصات ورودی‌ها را FileOffset و Data.Length می‌نامد، که به‌راحتی می‌شود آن را فقط offset شروع رکورد خواند، و همان بدفهمی دقیقاً چیزی است که HotXLS تا v2.384.47 عرضه می‌کرد

سه جزئیات تصمیم می‌گیرند که اندیس‌هایتان با Excel جور شود:

  • هدر 4 بایتی رکورد هرگز تبدیل نمی‌شود اما همچنان جای stream را اشغال می‌کند، پس اولین بایت بدنه رکورد در header offset + 4 می‌نشیند
  • جمله طول، طول کامل داده رکورد است نه تعداد بایت‌هایی که واقعاً تبدیل می‌شوند
  • BOUNDSHEET تا حدی ساده است: 4 بایت اولش یعنی lbPlyPos، همان offset stream از BOF شیت، خوانا می‌ماند تا parser بتواند شیت‌ها را پیدا کند. این 4 بایت از تبدیل رد می‌شوند اما هم در offset و هم در طول رکورد حساب می‌شوند
نمودار HotXLS از قاعده XorArrayIndex برای مبهم‌سازی XOR در BIFF5: هر بایت بدنه از offset stream به‌علاوه طول کامل داده رکورد به پیمانه 16 استفاده می‌کند، هدر چهار بایتی ساده و پیشوند ساده lbPlyPos در BOUNDSHEET همچنان در offset حساب می‌شوند، و نادیده گرفتن جمله طول رکورد همان نقصی بود که HotXLS در v2.384.47 فیکس کرد
اندیس آرایه به‌ازای هر رکورد یک بار ریست می‌شود نه به‌ازای هر stream: هدر و هر پیشوند ساده‌ای جای اشغال می‌کنند، طول داده رکورد وارد پیمانه می‌شود، و هر دو نیمه HotXLS قدیمی روی فرمول غلط با هم توافق داشتند

یک‌جا جمع شود، تبدیل به‌ازای هر رکورد چند خط است. باز هم این طرحی از قاعده است، نه چیزی که لازم باشد صدا بزنید:

function Rol8(B: Byte; N: Integer): Byte;
begin
  Result := Byte((B shl N) or (B shr (8 - N)));
end;

// بدنه یک رکورد را درجا مبهم می‌کند. BodyPos همان offset stream از
// Body[0] است، یعنی offset هدر رکورد + 4. مقدار PlainPrefix برای
// BOUNDSHEET برابر 4، برای 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 خواننده HotXLS اندیس را فقط از موقعیت stream می‌گرفت و نویسنده از offset بایت ساده استفاده می‌کرد. هر دو طول رکورد را نادیده می‌گرفتند، پس باز هم دو نیمه با هم توافق داشتند و با هیچ‌کس دیگر. یک decoder که مستقل نوشته شده بود خروجی v2.384.47 را درست می‌خواند و خروجی قدیمی‌تر را به‌عنوان garbage، و تست هشت-واریانتی Excel 16 هم بعداً قاعده را مقابل هدف واقعی تأیید کرد

با فایل‌های XOR نوشته‌شده توسط نسخه‌های قدیمی‌تر HotXLS چه می‌شود؟

HotXLS فایل‌های XOR خودش قبل از v2.384.54 را با چک کردن کلید FILEPASS همچنان می‌خواند: وقتی کلید ذخیره‌شده با کلید مشتق‌شده از رمز عبور برابر باشد، reader آرایه مطابق مشخصات یعنی XorRor را می‌سازد، و وقتی متفاوت باشد فایل را فایل قدیمی HotXLS تلقی می‌کند و آرایه چرخش-به-چپ-2 را بازسازی می‌کند. فایل‌های نوشته‌شده توسط Excel همیشه کلید مشتق‌شده دارند، پس همیشه مسیر مشخصات را می‌روند

این تست یک heuristic با نرخ خطای دقیق است. یک فایل قدیمی که کلید تصادفی‌اش اتفاقی با کلید مشتق‌شده برابر می‌بود با آرایه غلط خوانده می‌شد، و شانس آن 1 در 65,536 است. fallback فقط چرخش آرایه را پوشش می‌دهد؛ قاعده اندیس سوییچ نمی‌شود، پس فایل‌هایی که نجات پیدا می‌کنند همان‌هایی‌اند که بین v2.384.47 و v2.384.53 نوشته شده‌اند. اگر هنوز فایل‌های XOR با BIFF5 از آن بازه دارید، با HotXLS فعلی بازشان کنید و دوباره ذخیره‌شان کنید تا فایلی بگیرید که Excel می‌پذیرد

دو جزئیات رمز عبور روی هر فایلی اعمال می‌شود، قدیمی یا جدید:

  • طول. CreateXorKey_Method1 فقط 15 بایت اول رمز عبور را می‌خواند، که همان سقف مشخصات است. HotXLS این سقف را روی کلید اعمال می‌کند و verifier و آرایه را روی قواعد همیشگی خودشان یعنی طول کامل و 16 بایت نگه می‌دارد، به‌طور سازگار در هر دو طرف. خود Excel برای این فرمت رمزهای عبور بلندتر از 15 کاراکتر را رد می‌کند، پس 15 را سقف واقعی بدانید
  • مجموعه کاراکتر. HotXLS رمز عبور را از طریق code page ANSI سیستم به بایت تبدیل می‌کند. مشخصات می‌گوید بایت پایین هر کاراکتر UTF-16 برداشته شود، که برای ASCII یکی است. بدون نمونه Excel محافظت‌شده با رمزهای غیر-اسکی برای بقیه هیچ ground truth وجود ندارد، پس برای فایل‌های 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 برای هیچ‌چیز امنیت کافی دارد؟

مبهم‌سازی XOR در BIFF رمزنگاری نیست و مقابل یک خواننده مصمم هیچ چیزی را حفاظت نمی‌کند. چک رمز عبور یک verifier شانزده‌بیتی است، کلید 16 بیت است، و آرایه 16 بایتی در کل stream تکرار می‌شود، پس محتوای قابل پیش‌بینی رکوردهای BIFF بایت‌های آرایه را بدون هیچ رمز عبوری لو می‌دهد. HotXLS فقط برای این XOR می‌نویسد که فایل‌های Excel 5.0/95 گزینه دیگری ندارند، و دلیل تولید چنین فایل‌هایی امروز یک مصرف‌کننده legacy است که چیزی جدیدتر نمی‌خواند

موتور کلاسیک طرح را از طریق TXLSWorkbook.EncryptionType انتخاب می‌کند، و ترکیبش با فرمت ذخیره سخت‌گیرانه چک می‌شود:

  • xletAuto (پیش‌فرض) برای xlExcel97 یعنی RC4 CryptoAPI می‌نویسد و برای xlExcel5 یعنی XOR، مطابق همان چیزی که خود Excel برای هر فرمت می‌نوشت
  • xletXor فقط برای BIFF5 معتبر است؛ با xlExcel97 عملیات ذخیره به‌جای fallback بی‌سروصدا exception می‌دهد
  • xletRC4 و xletRC4CryptoAPI فقط برای BIFF8 هستند، و درخواستشان روی ذخیره BIFF5 هم exception می‌دهد

RC4 هم قدیمی است، و جزئیات interop کردنش در چرا اکسل کتاب کار رمزگذاری‌شده شما را رد می‌کند: ECB و RC4 پوشش داده شده. اگر گیرنده می‌تواند XLSX بخواند، به‌جایش موتور XLSX را بردارید: TXLSXWorkbook.SaveAsEncryptedAgile می‌نویسد Agile Encryption (هش رمز عبور SHA-512 با spin count صد-هزار-تکراری و AES-256-CBC)، همان فرمتی که Excel 2010 و بعدتر به‌طور پیش‌فرض می‌نویسند، و SaveAsEncrypted می‌نویسد Standard Encryption قدیمی‌تر با AES-128. مقایسه این دو در رمزگذاری فایل‌های XLSX با AES در دلفی با HotXLS است و سمت خواندن در خواندن فایل‌های اکسل رمزگذاری‌شده Agile در دلفی با HotXLS پوشش داده شده

مرجع سریع: مبهم‌سازی XOR در BIFF که Excel 16 می‌پذیرد

  • FILEPASS یعنی $002F بعد از globals BOF می‌آید؛ در BIFF5 بدنه‌اش 4 بایت است: کلید، بعد verifier
  • کلید = CreateXorKey_Method1(password) طبق [MS-OFFCRYPTO] §2.3.7.2، هرگز تصادفی نیست؛ برای secret برابر $014D است
  • آرایه = بایت‌های رمز عبور + pad، موقعیت زوج با بایت کم کلید XOR می‌شود و موقعیت فرد با بایت بالا کلید، بعد XorRor (چرخش به راست 1)
  • رمز کردن یک بایت: چرخش به چپ 5، بعد XOR؛ رمزگشایی: XOR، بعد چرخش به راست 5 (§2.3.7.3)
  • XorArrayIndex = (offset بایت در stream + طول داده رکورد) mod 16؛ هدرها و پیشوندهای ساده در offset حساب می‌شوند
  • BOUNDSHEET چهار بایت اولش را ساده نگه می‌دارد؛ BOF و FILEPASS و INTERFACEHDR کاملاً ساده می‌مانند
  • رمزهای عبور: اسکی، حداکثر 15 کاراکتر
  • HotXLS: اندیس در v2.384.47 فیکس شد، کلید و XorRor در v2.384.54، فایل‌های XOR قدیمی HotXLS با ناهمخوانی کلید شناسایی می‌شوند
  • برای حفاظت واقعی حداقل BIFF8 RC4 CryptoAPI یا Agile Encryption در XLSX را بردارید

HotXLS حفاظت با رمز عبور BIFF5 و BIFF8 و Standard Encryption و Agile Encryption در XLSX و callback رمز عبور سمت خواندن را در یک کتابخانه Delphi و C++Builder جا داده، با همان جزئیات interop بالا که برای شما مدیریت می‌شوند. برای نسخه‌ها، پلتفرم‌ها و دانلود آزمایشی کامپوننت صفحه‌گسترده HotXLS در Delphi را ببینید