diff --git a/2021-01-BambooFox/better-than-asm/README.md b/2021-01-BambooFox/better-than-asm/README.md index 47aa80d..34f9a48 100644 --- a/2021-01-BambooFox/better-than-asm/README.md +++ b/2021-01-BambooFox/better-than-asm/README.md @@ -454,24 +454,36 @@ XOR ## گام ششم: اسکریپت نویسی برای تحلیل -اسکریپت اول: +### اسکریپت اول: ```sh -secret = b'B\x0A|_\x22\x06\x1Bg7#\x5CF\x0A)\x090Q8_{Y\x13\x18\x0DP\x00...' # متغیر 'secret' را تعریف می‌کند که حاوی بایت‌های کلید رمزنگاری است. این مقدار از تحلیل فایل باینری اصلی (مثلاً با Cutter) به دست آمده است. -flag = b'\x1DU#hJ7.8\x06\x16\x03rUO=[bg9JmtGt`7U\x0BnNjD\x01\x03\x120...' # متغیر 'flag' را تعریف می‌کند که حاوی بایت‌های پیام رمزنگاری شده است. این مقدار نیز از تحلیل فایل باینری به دست آمده است. +secret = b'B\x0A|_\x22\x06\x1Bg7#\x5CF\x0A)\x090Q8_{Y\x13\x18\x0DP\x00...' +# متغیر `secret` را تعریف می‌کند که حاوی بایت‌های کلید رمزنگاری است. +# این مقدار از تحلیل فایل باینری اصلی (مثلاً با Cutter) به دست آمده و برای عملیات XOR استفاده می‌شود. -newStr = "" # یک رشته خالی به نام 'newStr' ایجاد می‌کند. این رشته برای ذخیره کاراکترهای رمزگشایی شده به کار می‌رود. +flag = b'\x1DU#hJ7.8\x06\x16\x03rUO=[bg9JmtGt`7U\x0BnNjD\x01\x03\x120...' +# متغیر `flag` را تعریف می‌کند که حاوی بایت‌های پیام رمزنگاری شده است (این فلگ، فلگ جعلی است). +# این مقدار نیز از تحلیل فایل باینری به دست آمده است. -for i in range(0, 56): # یک حلقه 'for' را آغاز می‌کند که از عدد 0 شروع شده و تا 55 ادامه می‌یابد (در مجموع 56 تکرار). - # عدد 56 احتمالاً طول رشته رمزگشایی شده مورد انتظار است که قبلاً از تحلیل برنامه به دست آمده (مثلاً از 'strlen(&what)'). +newStr = "" +# یک رشته خالی به نام `newStr` ایجاد می‌کند. +# این رشته برای ذخیره کاراکترهای رمزگشایی شده (خروجی نهایی) به کار می‌رود. - newStr += chr(secret[i % len(secret)] ^ flag[i]) # این خط هسته عملیات رمزگشایی است: - # `flag[i]`: بایت `i`ام از رشته رمزنگاری شده 'flag' را انتخاب می‌کند. - # `secret[i % len(secret)]`: بایت `i`ام از کلید 'secret' را انتخاب می‌کند. عملگر `% len(secret)` تضمین می‌کند که اگر طول 'secret' از 56 کمتر باشد، کلید به صورت چرخشی (دوره‌ای) تکرار شود و هر بایت از 'flag' با یک بایت از 'secret' جفت شود. +for i in range(0, 56): + # یک حلقه `for` را آغاز می‌کند که از عدد 0 شروع شده و تا 55 ادامه می‌یابد (در مجموع 56 تکرار). + # عدد 56 احتمالاً طول رشته رمزگشایی شده مورد انتظار است که از تحلیل برنامه (مثلاً از `strlen()`) به دست آمده است. + + newStr += chr(secret[i % len(secret)] ^ flag[i]) + # این خط هسته عملیات رمزگشایی با XOR است: + # `flag[i]`: بایت `i`ام از رشته رمزنگاری شده `flag` را انتخاب می‌کند. + # `len(secret)`: طول رشته `secret` را برمی‌گرداند. + # `i % len(secret)`: این عملگر باقی‌مانده تقسیم `i` بر طول `secret` را محاسبه می‌کند. این کار تضمین می‌کند که اگر طول `secret` از 56 کمتر باشد، کلید به صورت چرخشی (دوره‌ای) تکرار شود و هر بایت از `flag` با یک بایت از `secret` جفت شود. + # `secret[...]`: بایت متناظر از کلید `secret` را انتخاب می‌کند. # `^`: این عملگر `XOR` (یا انحصاری) است که عملیات بیتی را روی دو بایت انجام می‌دهد. # `chr()`: نتیجه عددی عملیات XOR را به کاراکتر ASCII مربوطه تبدیل می‌کند. - # `newStr += ...`: کاراکتر حاصل را به انتهای رشته 'newStr' اضافه می‌کند. + # `newStr += ...`: کاراکتر حاصل را به انتهای رشته `newStr` اضافه می‌کند. -print(newStr) # پس از اتمام حلقه، رشته 'newStr' که حاوی پیام رمزگشایی شده است، در خروجی چاپ می‌شود. +print(newStr) +# پس از اتمام حلقه، رشته `newStr` که حاوی پیام رمزگشایی شده است، در خروجی چاپ می‌شود. ``` خروجی: @@ -486,36 +498,57 @@ ___7h15_15_4_f4k3_f14g_y0u_w1ll_f41l_1f_y0u_subm17_17___ -اسکریپت دوم: +### اسکریپت دوم: ```python -__int64 __fastcall check(__int64 a1) // 1. تعریف تابع check: یک آرگومان (a1) از نوع __int64 می‌گیرد که به ورودی کاربر (رشته 's' در تابع main) اشاره دارد. مقدار برگشتی آن نیز __int64 است (که در واقع یک Boolean (true/false) را نشان می‌دهد). +__int64 __fastcall check(__int64 a1) +// تعریف تابع `check`: +// این تابع یک آرگومان از نوع `__int64` به نام `a1` می‌گیرد. `a1` اشاره‌گری به رشته ورودی کاربر (مانند "s" در تابع main) است. +// این تابع یک مقدار از نوع `__int64` برمی‌گرداند که در اینجا عملاً یک مقدار بولین (0 یا 1 برای False/True) است. +// `__fastcall` یک قرارداد فراخوانی (calling convention) است که به نحوه پاس دادن آرگومان‌ها به تابع اشاره دارد. { - int v2; // [rsp+1Ch] [rbp-1Ch] // 2. تعریف متغیر محلی: برای ذخیره موقت یک کاراکتر (بایت) از ورودی کاربر. - int i; // [rsp+28h] [rbp-10h] // 3. تعریف متغیر محلی: به عنوان شمارنده حلقه 'for'. - unsigned int v4; // [rsp+2Ch] [rbp-Ch] // 4. تعریف متغیر محلی: برای نگهداری نتیجه نهایی اعتبارسنجی (true/false). + int v2; // [rsp+1Ch] [rbp-1Ch] + // تعریف یک متغیر محلی از نوع `int` به نام `v2`. + // این متغیر برای ذخیره موقت بایت فعلی از ورودی کاربر در هر تکرار حلقه استفاده می‌شود. - v4 = 1; // 5. مقداردهی اولیه: 'v4' را با 1 (معادل True یا موفقیت) مقداردهی می‌کند. این فرض اولیه است که ورودی صحیح است، مگر اینکه در حلقه نقض شود. + int i; // [rsp+28h] [rbp-10h] + // تعریف یک متغیر محلی از نوع `int` به نام `i`. + // این متغیر به عنوان شمارنده اصلی حلقه `for` استفاده می‌شود. - for ( i = 0; i < strlen(what); ++i ) // 6. شروع حلقه 'for': -// حلقه از i = 0 شروع می‌شود. -// تا زمانی که 'i' کوچکتر از طول رشته 'what' باشد، ادامه می‌یابد (مثلاً 56 تکرار). -// در هر تکرار، 'i' یک واحد افزایش می‌یابد. + unsigned int v4; // [rsp+2Ch] [rbp-Ch] + // تعریف یک متغیر محلی از نوع `unsigned int` به نام `v4`. + // این متغیر برای نگهداری نتیجه نهایی اعتبارسنجی استفاده می‌شود. مقدار 1 به معنای "ورودی صحیح" و 0 به معنای "ورودی غلط" است. + + v4 = 1; + // مقداردهی اولیه `v4` به 1. + // این خط فرض می‌کند که ورودی در ابتدا صحیح است و اگر هر یک از مقایسه‌ها در حلقه غلط باشد، این مقدار به 0 تغییر خواهد کرد. + + for ( i = 0; i < strlen(what); ++i ) + // شروع حلقه `for`: + // حلقه از `i = 0` (اولین بایت) شروع می‌شود. + // تا زمانی که `i` کمتر از طول رشته ثابت `what` باشد، ادامه می‌یابد. `strlen(what)` طول رشته `what` را برمی‌گرداند. + // در هر تکرار، `i` یک واحد افزایش می‌یابد (`++i`). { - v2 = *(char *)(a1 + i); // 7. دسترسی به کاراکتر فعلی: - // *(char *)(a1 + i) به بایت 'i'ام از رشته ورودی کاربر (a1) دسترسی پیدا می‌کند و آن را در 'v2' ذخیره می‌کند. + v2 = *(char *)(a1 + i); + // دسترسی به بایت فعلی از ورودی کاربر: + // `a1 + i`: به آدرس بایت `i`ام از رشته ورودی کاربر اشاره می‌کند. + // `*(char *)`: محتوای آدرس را به عنوان یک کاراکتر (بایت) می‌خواند. + // مقدار این بایت خوانده شده در `v2` ذخیره می‌شود. -// 8. هسته الگوریتم اعتبارسنجی: v4 = (unsigned __int8)v4 & ((*(char *)(a1 + (i + 1) % strlen(what)) ^ v2) == what[i]); -// این خط یک عملیات پیچیده است که در هر تکرار، 'v4' را بروزرسانی می‌کند: -// الف) `*(char *)(a1 + (i + 1) % strlen(what))`: به بایت بعدی از ورودی کاربر (a1) دسترسی پیدا می‌کند. - // `% strlen(what)` در اینجا بسیار مهم است. اگر به انتهای رشته برسد، به ابتدای آن برمی‌گردد و بایت اول را در نظر می‌گیرد. این یک عملیات "چرخشی" یا "گرد" است. -// ب) `^ v2`: بایت بعدی (از ورودی کاربر) با بایت فعلی (v2) ورودی کاربر **XOR** می‌شود. -// ج) `... == what[i]`: نتیجه XOR مرحله قبل با بایت `i`ام از رشته ثابت **'what'** مقایسه می‌شود. -// د) `(unsigned __int8)v4 & (...)`: نتیجه این مقایسه (که True/False است) با مقدار فعلی `v4` به صورت **AND** بیتی می‌شود. -// یعنی اگر در هر مرحله یکی از مقایسه‌ها `False` شود، `v4` به `0` (False) تبدیل می‌شود و برای باقی حلقه همان `0` باقی می‌ماند. این بدان معناست که همه مقایسه‌ها باید `True` باشند تا تابع `True` برگرداند. + // این خط هسته الگوریتم اعتبارسنجی است و `v4` را در هر تکرار بروزرسانی می‌کند: + // 1. `(i + 1) % strlen(what)`: شاخص بایت **بعدی** از ورودی کاربر را محاسبه می‌کند. عملگر `%` (باقیمانده) تضمین می‌کند که اگر به انتهای رشته `what` رسید، به ابتدای آن برگردد (عملیات چرخشی). + // 2. `*(char *)(a1 + ...)`: بایت **بعدی** از رشته ورودی کاربر (`a1`) را می‌خواند. + // 3. `^ v2`: بایت **بعدی** ورودی کاربر را با بایت **فعلی** ورودی کاربر (`v2`) به صورت **XOR** می‌کند. + // 4. `(...) == what[i]`: نتیجه عملیات XOR را با بایت `i`ام از رشته **ثابت `what`** مقایسه می‌کند. این مقایسه یک مقدار بولین (True یا False) برمی‌گرداند. + // 5. `(unsigned __int8)v4 & (...)`: مقدار فعلی `v4` (که 0 یا 1 است) را با نتیجه بولین مقایسه قبل، به صورت **AND بیتی** می‌کند. + // * اگر `v4` قبلاً 0 (False) شده باشد، حاصل `AND` همیشه 0 باقی می‌ماند. + // * اگر `v4` هنوز 1 (True) باشد، و نتیجه مقایسه `True` باشد، `v4` همچنان 1 می‌ماند. + // * اگر `v4` هنوز 1 (True) باشد، اما نتیجه مقایسه `False` باشد، `v4` به 0 تغییر می‌کند. + // این یعنی **همه مقایسه‌ها در طول حلقه باید True باشند** تا `v4` در پایان 1 (صحیح) باقی بماند. } - return v4; // 9. بازگرداندن نتیجه: در نهایت، مقدار نهایی 'v4' (1 برای موفقیت، 0 برای عدم موفقیت) را برمی‌گرداند. + return v4; + // پس از اتمام حلقه، مقدار نهایی `v4` (1 برای موفقیت، 0 برای عدم موفقیت) را برمی‌گرداند. } ``` @@ -523,8 +556,6 @@ __int64 __fastcall check(__int64 a1) // 1. تعریف تابع check: یک آر تحلیل تابع check 🕵️‍♀️ -C - __int64 __fastcall check(__int64 a1) // 1. تعریف تابع check: یک آرگومان (a1) از نوع __int64 می‌گیرد که به ورودی کاربر (رشته 's' در تابع main) اشاره دارد. مقدار برگشتی آن نیز __int64 است (که در واقع یک Boolean (true/false) را نشان می‌دهد). { int v2; // [rsp+1Ch] [rbp-1Ch] // 2. تعریف متغیر محلی: برای ذخیره موقت یک کاراکتر (بایت) از ورودی کاربر. @@ -576,49 +607,66 @@ __int64 __fastcall check(__int64 a1) // 1. تعریف تابع check: یک آر -اسکریپت سوم: +### اسکریپت سوم: + ```python what = b"\x17/'\x17\x1DJy\x03,\x11\x1E&\x0AexjONacA-&\x01LANH'.&\x12>#'Z\x0FO\x0B%:(&HI\x0CJylL'\x1EmtdC\x00\x00\x00\x00\x00\x00\x00\x00" +# متغیر `what` را تعریف می‌کند که حاوی بایت‌های رشته ثابت `what` است. +# این رشته مستقیماً از فایل باینری برنامه اصلی (که در تابع `check()` استفاده می‌شود) استخراج شده است. + secret = b'B\x0A|_\x22\x06\x1Bg7#\x5CF\x0A)\x090Q8_{Y\x13\x18\x0DP\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' -# این دو خط، رشته‌های بایت 'what' و 'secret' را با مقادیر استخراج شده از فایل باینری تعریف می‌کنند. -# 'what' در تابع check() استفاده می‌شود و 'secret' کلید XORینگ است. +# متغیر `secret` را تعریف می‌کند که حاوی بایت‌های کلید رمزنگاری است. +# این مقدار نیز از تحلیل فایل باینری به دست آمده و برای عملیات XOR نهایی استفاده می‌شود. what = what.rstrip(b'\0') +# بایت‌های NULL (`\0`) را از انتهای رشته `what` حذف می‌کند. +# این کار برای اطمینان از اینکه طول دقیق داده‌ها در محاسبات بعدی استفاده شود، انجام می‌شود. + secret = secret.rstrip(b'\0') -# این خطوط بایت‌های NUL (صفر) را از انتهای رشته‌های 'what' و 'secret' حذف می‌کنند. -# در C، رشته‌ها با بایت NUL (\0) خاتمه می‌یابند. در پایتون، برای کار با طول دقیق داده‌ها، اغلب این بایت‌های اضافه حذف می‌شوند. +# بایت‌های NULL (`\0`) را از انتهای رشته `secret` حذف می‌کند. +# این کار نیز برای حفظ دقت طول رشته در عملیات XOR ضروری است. for i in range(30, 127): - # این حلقه یک بروت فورس (حدس زدن) روی اولین کاراکتر فلگ (که ورودی 's' تابع main و 'a1' تابع check است) انجام می‌دهد. - # range(30, 127) شامل کدهای ASCII برای کاراکترهای قابل چاپ (اعداد، حروف، نمادها) است. - # فرض می‌شود که کاراکتر اول فلگ در این محدوده قرار دارد. + # این حلقه یک **بروت‌فورس (حدس زدن)** هوشمند را آغاز می‌کند. + # `range(30, 127)` شامل کدهای ASCII برای کاراکترهای قابل چاپ رایج (مانند اعداد، حروف، نمادها) است. + # در هر تکرار، `i` به عنوان **اولین کاراکتر احتمالی ورودی صحیح** برای تابع `check()` در نظر گرفته می‌شود. - flag = [] # در هر تکرار حلقه 'i' (برای هر حدس کاراکتر اول)، یک لیست خالی 'flag' ایجاد می‌شود. - flag.append(i) # اولین حدس (کاراکتر 'i') به عنوان اولین عنصر به لیست 'flag' اضافه می‌شود. - # این لیست 'flag' در اینجا در واقع کاندیدای رشته ورودی صحیح برای تابع check() است (یعنی همان 's' که باید پیدا شود). + flag = [] + # در هر تکرار حلقه بیرونی (`for i in ...`)، یک لیست خالی به نام `flag` ایجاد می‌شود. + # این لیست `flag` در اینجا در واقع دنباله بایت‌های **ورودی صحیح برای تابع `check()`** (یعنی رشته `s` در تابع `main`) را نشان می‌دهد که ما در حال تلاش برای بازسازی آن هستیم. + + flag.append(i) + # اولین کاراکتر حدس زده شده (`i`) به عنوان اولین بایت به لیست `flag` اضافه می‌شود. for j in range(len(what)): - # این حلقه بقیه کاراکترهای کاندیدای فلگ را بر اساس منطق تابع check() بازسازی می‌کند. - # تابع check() از شرط `(*(char *)(a1 + (i + 1) % strlen(what)) ^ v2) == what[i]` استفاده می‌کرد. - # این به معنی `char_next ^ char_current == what[j]` است. - # با استفاده از خاصیت XOR (`A ^ B = C` پس `A = C ^ B`)، می‌توانیم کاراکتر بعدی را بازسازی کنیم: - # `char_next = what[j] ^ char_current` + # این حلقه داخلی، بقیه بایت‌های دنباله ورودی صحیح (`flag` در اینجا) را بازسازی می‌کند. + # این فرآیند **معکوس کردن منطق تابع `check()`** است. + # از آنجایی که از تابع `check()` می‌دانیم: `(بایت_بعدی_ورودی ^ بایت_فعلی_ورودی) == what[j]` + # با استفاده از خاصیت XOR (`A ^ B = C` پس `A = C ^ B`)، می‌توانیم بایت بعدی را به صورت زیر محاسبه کنیم: + # `بایت_بعدی_ورودی = بایت_فعلی_ورودی ^ what[j]` + flag.append(flag[j]^what[j]) - # `flag[j]` در اینجا همان `char_current` است. - # `what[j]` همان مقدار 'what[i]' از تابع check است. - # نتیجه XOR به عنوان `char_next` (یعنی `flag[j+1]`) به لیست اضافه می‌شود. - # این خط در واقع الگوریتم check را برای پیدا کردن ورودی صحیح، معکوس می‌کند. + # `flag[j]` (که در اینجا همان `بایت_فعلی_ورودی` است) با `what[j]` (که از رشته ثابت `what` می‌آید) XOR می‌شود. + # نتیجه XOR به عنوان `بایت_بعدی_ورودی` (یعنی `flag[j+1]`) به لیست `flag` اضافه می‌شود. + # این خط به صورت زنجیره‌ای بایت‌های بعدی ورودی صحیح را بر اساس بایت قبلی و مقادیر `what` بازسازی می‌کند. + + newFlag = "" + # یک رشته خالی به نام `newFlag` ایجاد می‌شود. + # این رشته برای ذخیره فلگ نهایی (که پس از رمزگشایی کاندیدای ورودی صحیح با `secret` به دست می‌آید) استفاده می‌شود. - newFlag = "" # یک رشته خالی 'newFlag' برای ذخیره نتیجه نهایی رمزگشایی ایجاد می‌شود. for j in range(len(what)): - # این حلقه، کاندیدای فلگ بازسازی شده (که در لیست 'flag' است) را با 'secret' XOR می‌کند. - # این دقیقاً همان عملیاتی است که برنامه اصلی روی ورودی صحیح (اگر تابع check() موفق باشد) انجام می‌دهد. - newFlag += chr(flag[j]^secret[j%len(secret)]) - # `flag[j]`: کاراکتر jام از کاندیدای فلگ بازسازی شده (ورودی صحیح) - # `secret[j%len(secret)]`: بایت متناظر از کلید secret (به صورت دوره‌ای) - # `chr(...)`: تبدیل نتیجه XOR به کاراکتر و اضافه کردن به 'newFlag'. + # این حلقه، کاندیدای ورودی صحیح بازسازی شده (که در لیست `flag` است) را با کلید `secret` XOR می‌کند. + # این عملیات **شبیه‌سازی فرآیند نهایی تولید فلگ توسط برنامه اصلی** است که در صورت موفقیت `check()` انجام می‌شود. - print(newFlag) # هر کاندیدای 'newFlag' تولید شده (که بر اساس هر حدس اولیه 'i' ایجاد شده است) چاپ می‌شود. + newFlag += chr(flag[j]^secret[j%len(secret)]) + # `flag[j]`: بایت `j`ام از کاندیدای ورودی صحیح بازسازی شده. + # `secret[j%len(secret)]`: بایت متناظر از کلید `secret` را انتخاب می‌کند (با استفاده از عملگر `%` برای تکرار چرخشی کلید). + # `^`: عملیات XOR را بین دو بایت انجام می‌دهد. + # `chr()`: نتیجه عددی XOR را به کاراکتر ASCII مربوطه تبدیل می‌کند. + # `newFlag += ...`: کاراکتر حاصل را به انتهای رشته `newFlag` اضافه می‌کند. + + print(newFlag) + # هر کاندیدای `newFlag` تولید شده (که بر اساس هر حدس اولیه `i` و فرآیند کامل بازسازی و XOR نهایی ایجاد شده است) در خروجی چاپ می‌شود. ``` @@ -644,61 +692,79 @@ for i in range(30, 127): -اسکریپت چهارم: +### اسکریپت چهارم: ```python what = b"\x17/'\x17\x1DJy\x03,\x11\x1E&\x0AexjONacA-&\x01LANH'.&\x12>#'Z\x0FO\x0B%:(&HI\x0CJylL'\x1EmtdC\x00\x00\x00\x00\x00\x00\x00\x00" +# متغیر `what` را تعریف می‌کند که حاوی بایت‌های رشته ثابت `what` است. +# این رشته مستقیماً از فایل باینری برنامه اصلی (که در تابع `check()` استفاده می‌شود) استخراج شده است. + secret = b'B\x0A|_\x22\x06\x1Bg7#\x5CF\x0A)\x090Q8_{Y\x13\x18\x0DP\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' -# این دو خط، رشته‌های بایت `what` و `secret` را با مقادیر استخراج شده از فایل باینری تعریف می‌کنند. -# `what` در تابع `check()` برای اعتبارسنجی ورودی استفاده می‌شود و `secret` کلید XORینگ برای رمزنگاری/رمزگشایی است. +# متغیر `secret` را تعریف می‌کند که حاوی بایت‌های کلید رمزنگاری است. +# این مقدار نیز از تحلیل فایل باینری به دست آمده و برای عملیات XOR نهایی استفاده می‌شود. what = what.rstrip(b'\0') +# بایت‌های NULL (`\0`) را از انتهای رشته `what` حذف می‌کند. +# این کار برای اطمینان از اینکه طول دقیق داده‌ها در محاسبات بعدی استفاده شود، انجام می‌شود. + secret = secret.rstrip(b'\0') -# این خطوط بایت‌های `NUL` (`\0`) را از انتهای رشته‌های بایت `what` و `secret` حذف می‌کنند. -# این کار برای اطمینان از اینکه طول دقیق داده‌ها در عملیات بعدی استفاده شود، انجام می‌شود. +# بایت‌های NULL (`\0`) را از انتهای رشته `secret` حذف می‌کند. +# این کار نیز برای حفظ دقت طول رشته در عملیات XOR ضروری است. for i in range(30, 127): - # این حلقه یک **بروت‌فورس هوشمند** را آغاز می‌کند. - # `range(30, 127)` شامل کدهای ASCII برای کاراکترهای قابل چاپ رایج است (مانند اعداد، حروف، نمادها). - # در هر تکرار، `i` به عنوان **اولین کاراکتر احتمالی فلگ صحیح** (ورودی‌ای که `check()` را True می‌کند) در نظر گرفته می‌شود. + # این حلقه یک **بروت‌فورس (حدس زدن)** هوشمند را آغاز می‌کند. + # `range(30, 127)` شامل کدهای ASCII برای کاراکترهای قابل چاپ رایج (مانند اعداد، حروف، نمادها) است. + # در هر تکرار، `i` به عنوان **اولین کاراکتر احتمالی ورودی صحیح** برای تابع `check()` در نظر گرفته می‌شود. - flag = [] # برای هر حدس `i`، یک لیست خالی `flag` جدید ایجاد می‌شود. - flag.append(i) # حدس فعلی `i` به عنوان اولین بایت به این لیست `flag` اضافه می‌شود. - # این لیست `flag` در اینجا در واقع دنباله بایت‌های **ورودی صحیح برای تابع `check()`** را نشان می‌دهد. + flag = [] + # در هر تکرار حلقه بیرونی (`for i in ...`)، یک لیست خالی به نام `flag` ایجاد می‌شود. + # این لیست `flag` در اینجا در واقع دنباله بایت‌های **ورودی صحیح برای تابع `check()`** (یعنی رشته `s` در تابع `main`) را نشان می‌دهد که ما در حال تلاش برای بازسازی آن هستیم. + + flag.append(i) + # اولین کاراکتر حدس زده شده (`i`) به عنوان اولین بایت به لیست `flag` اضافه می‌شود. for j in range(len(what)): - # این حلقه بقیه بایت‌های دنباله ورودی صحیح (`flag` در اینجا) را بازسازی می‌کند. - # این فرآیند **معکوس کردن منطق تابع `check()`** است: - # از آنجایی که می‌دانیم `(next_char_of_input ^ current_char_of_input) == what[j]` باید درست باشد (از تابع `check()`), - # می‌توانیم `next_char_of_input` را با `current_char_of_input ^ what[j]` به دست آوریم. + # این حلقه داخلی، بقیه بایت‌های دنباله ورودی صحیح (`flag` در اینجا) را بازسازی می‌کند. + # این فرآیند **معکوس کردن منطق تابع `check()`** است. + # از آنجایی که از تابع `check()` می‌دانیم: `(بایت_بعدی_ورودی ^ بایت_فعلی_ورودی) == what[j]` + # با استفاده از خاصیت XOR (`A ^ B = C` پس `A = C ^ B`)، می‌توانیم بایت بعدی را به صورت زیر محاسبه کنیم: + # `بایت_بعدی_ورودی = بایت_فعلی_ورودی ^ what[j]` + flag.append(flag[j]^what[j]) - # `flag[j]` (که `current_char_of_input` است) با `what[j]` XOR می‌شود. - # نتیجه به عنوان `next_char_of_input` به لیست `flag` اضافه می‌شود. + # `flag[j]` (که در اینجا همان `بایت_فعلی_ورودی` است) با `what[j]` (که از رشته ثابت `what` می‌آید) XOR می‌شود. + # نتیجه XOR به عنوان `بایت_بعدی_ورودی` (یعنی `flag[j+1]`) به لیست `flag` اضافه می‌شود. + # این خط به صورت زنجیره‌ای بایت‌های بعدی ورودی صحیح را بر اساس بایت قبلی و مقادیر `what` بازسازی می‌کند. + + newFlag = "" + # یک رشته خالی به نام `newFlag` ایجاد می‌شود. + # این رشته برای ذخیره فلگ نهایی (که پس از رمزگشایی کاندیدای ورودی صحیح با `secret` به دست می‌آید) استفاده می‌شود. - newFlag = "" # یک رشته خالی `newFlag` برای ذخیره نتیجه نهایی رمزگشایی ایجاد می‌شود. for j in range(len(what)): - # این حلقه، کاندیدای فلگ بازسازی شده (که در لیست `flag` قرار دارد) را با `secret` XOR می‌کند. - # این دقیقاً همان عملیاتی است که برنامه اصلی روی **ورودی صحیح** (اگر تابع `check()` موفق باشد) انجام می‌دهد تا فلگ نهایی را چاپ کند. + # این حلقه، کاندیدای ورودی صحیح بازسازی شده (که در لیست `flag` است) را با کلید `secret` XOR می‌کند. + # این عملیات **شبیه‌سازی فرآیند نهایی تولید فلگ توسط برنامه اصلی** است که در صورت موفقیت `check()` انجام می‌شود. char = chr(flag[j]^secret[j%len(secret)]) - # `flag[j]`: بایت `j`ام از کاندیدای فلگ بازسازی شده. - # `secret[j%len(secret)]`: بایت متناظر از کلید `secret` (به صورت دوره‌ای). - # `chr(...)`: تبدیل نتیجه XOR به کاراکتر. + # `flag[j]`: بایت `j`ام از کاندیدای ورودی صحیح بازسازی شده. + # `secret[j%len(secret)]`: بایت متناظر از کلید `secret` را انتخاب می‌کند (با استفاده از عملگر `%` برای تکرار چرخشی کلید). + # `^`: عملیات XOR را بین دو بایت انجام می‌دهد. + # `chr()`: نتیجه عددی XOR را به کاراکتر ASCII مربوطه تبدیل می‌کند. if ord(char) not in range(30, 127): - # این خط **فیلتر اصلی** این قطعه کد است و آن را از نسخه قبلی متمایز می‌کند. + # این خط **فیلتر اصلی** این قطعه کد است و آن را از نسخه قبلی (سوم) متمایز می‌کند. # `ord(char)`: کد ASCII کاراکتر `char` را برمی‌گرداند. - # `range(30, 127)`: محدوده کدهای ASCII برای کاراکترهای قابل چاپ است. - # `if ... not in ...`: اگر کاراکتر تولید شده **قابل چاپ نباشد** (مثلاً یک کاراکتر کنترلی یا غیرعادی باشد)... + # `range(30, 127)`: محدوده کدهای ASCII برای کاراکترهای قابل چاپ رایج است (شامل اعداد، حروف، نمادها و برخی کاراکترهای کنترلی محدود). + # `if ... not in ...`: اگر کاراکتر تولید شده **قابل چاپ نباشد** (مثلاً یک کاراکتر کنترلی، غیرقابل خواندن یا غیرمعمول باشد)... + continue - # ...کل حلقه داخلی (برای این کاندیدای `newFlag`) ادامه پیدا نمی‌کند و به تکرار بعدی (برای `j`) می‌رود. - # این باعث می‌شود رشته `newFlag` ناقص بماند یا اصلاً تشکیل نشود. + # ...این خط باعث می‌شود که حلقه داخلی (برای `j` فعلی) به تکرار بعدی برود و این کاراکتر به `newFlag` اضافه نشود. + # این فیلتر به شدت نتایج چاپ شده را محدود می‌کند و فقط آن دسته از خروجی‌هایی را که شبیه یک فلگ معتبر (شامل کاراکترهای قابل چاپ) هستند، نمایش می‌دهد. else: - # اگر کاراکتر قابل چاپ باشد... + # اگر کاراکتر قابل چاپ باشد (یعنی کد ASCII آن در محدوده مجاز باشد)... newFlag += char - # ...آن را به رشته `newFlag` اضافه می‌کند. + # ...آن کاراکتر به رشته `newFlag` اضافه می‌شود. - print(newFlag) # هر `newFlag` تولید شده (که بر اساس هر حدس اولیه `i` و فیلتر کاراکترهای قابل چاپ تشکیل شده) چاپ می‌شود. + print(newFlag) + # هر `newFlag` تولید شده (که بر اساس هر حدس اولیه `i` و فرآیند کامل بازسازی و XOR نهایی، و پس از فیلتر کاراکترهای غیرقابل چاپ تشکیل شده است) در خروجی چاپ می‌شود. ``` دید کلی درباره این قطعه کد: