Updated: 2021-01-BambooFox/better-than-asm

This commit is contained in:
Hadi Mottale 2025-07-22 11:28:35 +03:30
parent c748f98b21
commit b66d5cfe83
1 changed files with 159 additions and 93 deletions

View File

@ -454,24 +454,36 @@ XOR
## گام ششم: اسکریپت نویسی برای تحلیل ## گام ششم: اسکریپت نویسی برای تحلیل
اسکریپت اول: ### اسکریپت اول:
```sh ```sh
secret = b'B\x0A|_\x22\x06\x1Bg7#\x5CF\x0A)\x090Q8_{Y\x13\x18\x0DP\x00...' # متغیر 'secret' را تعریف می‌کند که حاوی بایت‌های کلید رمزنگاری است. این مقدار از تحلیل فایل باینری اصلی (مثلاً با Cutter) به دست آمده است. secret = b'B\x0A|_\x22\x06\x1Bg7#\x5CF\x0A)\x090Q8_{Y\x13\x18\x0DP\x00...'
flag = b'\x1DU#hJ7.8\x06\x16\x03rUO=[bg9JmtGt`7U\x0BnNjD\x01\x03\x120...' # متغیر 'flag' را تعریف می‌کند که حاوی بایت‌های پیام رمزنگاری شده است. این مقدار نیز از تحلیل فایل باینری به دست آمده است. # متغیر `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 تکرار). newStr = ""
# عدد 56 احتمالاً طول رشته رمزگشایی شده مورد انتظار است که قبلاً از تحلیل برنامه به دست آمده (مثلاً از 'strlen(&what)'). # یک رشته خالی به نام `newStr` ایجاد می‌کند.
# این رشته برای ذخیره کاراکترهای رمزگشایی شده (خروجی نهایی) به کار می‌رود.
newStr += chr(secret[i % len(secret)] ^ flag[i]) # این خط هسته عملیات رمزگشایی است: for i in range(0, 56):
# `flag[i]`: بایت `i`ام از رشته رمزنگاری شده 'flag' را انتخاب می‌کند. # یک حلقه `for` را آغاز می‌کند که از عدد 0 شروع شده و تا 55 ادامه می‌یابد (در مجموع 56 تکرار).
# `secret[i % len(secret)]`: بایت `i`ام از کلید 'secret' را انتخاب می‌کند. عملگر `% len(secret)` تضمین می‌کند که اگر طول 'secret' از 56 کمتر باشد، کلید به صورت چرخشی (دوره‌ای) تکرار شود و هر بایت از 'flag' با یک بایت از 'secret' جفت شود. # عدد 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` (یا انحصاری) است که عملیات بیتی را روی دو بایت انجام می‌دهد. # `^`: این عملگر `XOR` (یا انحصاری) است که عملیات بیتی را روی دو بایت انجام می‌دهد.
# `chr()`: نتیجه عددی عملیات XOR را به کاراکتر ASCII مربوطه تبدیل می‌کند. # `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 ```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 v2; // [rsp+1Ch] [rbp-1Ch]
int i; // [rsp+28h] [rbp-10h] // 3. تعریف متغیر محلی: به عنوان شمارنده حلقه 'for'. // تعریف یک متغیر محلی از نوع `int` به نام `v2`.
unsigned int v4; // [rsp+2Ch] [rbp-Ch] // 4. تعریف متغیر محلی: برای نگهداری نتیجه نهایی اعتبارسنجی (true/false). // این متغیر برای ذخیره موقت بایت فعلی از ورودی کاربر در هر تکرار حلقه استفاده می‌شود.
v4 = 1; // 5. مقداردهی اولیه: 'v4' را با 1 (معادل True یا موفقیت) مقداردهی می‌کند. این فرض اولیه است که ورودی صحیح است، مگر اینکه در حلقه نقض شود. int i; // [rsp+28h] [rbp-10h]
// تعریف یک متغیر محلی از نوع `int` به نام `i`.
// این متغیر به عنوان شمارنده اصلی حلقه `for` استفاده می‌شود.
for ( i = 0; i < strlen(what); ++i ) // 6. شروع حلقه 'for': unsigned int v4; // [rsp+2Ch] [rbp-Ch]
// حلقه از i = 0 شروع می‌شود. // تعریف یک متغیر محلی از نوع `unsigned int` به نام `v4`.
// تا زمانی که 'i' کوچکتر از طول رشته 'what' باشد، ادامه می‌یابد (مثلاً 56 تکرار). // این متغیر برای نگهداری نتیجه نهایی اعتبارسنجی استفاده می‌شود. مقدار 1 به معنای "ورودی صحیح" و 0 به معنای "ورودی غلط" است.
// در هر تکرار، 'i' یک واحد افزایش می‌یابد.
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. دسترسی به کاراکتر فعلی: v2 = *(char *)(a1 + i);
// *(char *)(a1 + i) به بایت 'i'ام از رشته ورودی کاربر (a1) دسترسی پیدا می‌کند و آن را در 'v2' ذخیره می‌کند. // دسترسی به بایت فعلی از ورودی کاربر:
// `a1 + i`: به آدرس بایت `i`ام از رشته ورودی کاربر اشاره می‌کند.
// `*(char *)`: محتوای آدرس را به عنوان یک کاراکتر (بایت) می‌خواند.
// مقدار این بایت خوانده شده در `v2` ذخیره می‌شود.
// 8. هسته الگوریتم اعتبارسنجی:
v4 = (unsigned __int8)v4 & ((*(char *)(a1 + (i + 1) % strlen(what)) ^ v2) == what[i]); v4 = (unsigned __int8)v4 & ((*(char *)(a1 + (i + 1) % strlen(what)) ^ v2) == what[i]);
// این خط یک عملیات پیچیده است که در هر تکرار، 'v4' را بروزرسانی می‌کند: // این خط هسته الگوریتم اعتبارسنجی است و `v4` را در هر تکرار بروزرسانی می‌کند:
// الف) `*(char *)(a1 + (i + 1) % strlen(what))`: به بایت بعدی از ورودی کاربر (a1) دسترسی پیدا می‌کند. // 1. `(i + 1) % strlen(what)`: شاخص بایت **بعدی** از ورودی کاربر را محاسبه می‌کند. عملگر `%` (باقیمانده) تضمین می‌کند که اگر به انتهای رشته `what` رسید، به ابتدای آن برگردد (عملیات چرخشی).
// `% strlen(what)` در اینجا بسیار مهم است. اگر به انتهای رشته برسد، به ابتدای آن برمی‌گردد و بایت اول را در نظر می‌گیرد. این یک عملیات "چرخشی" یا "گرد" است. // 2. `*(char *)(a1 + ...)`: بایت **بعدی** از رشته ورودی کاربر (`a1`) را می‌خواند.
// ب) `^ v2`: بایت بعدی (از ورودی کاربر) با بایت فعلی (v2) ورودی کاربر **XOR** می‌شود. // 3. `^ v2`: بایت **بعدی** ورودی کاربر را با بایت **فعلی** ورودی کاربر (`v2`) به صورت **XOR** می‌کند.
// ج) `... == what[i]`: نتیجه XOR مرحله قبل با بایت `i`ام از رشته ثابت **'what'** مقایسه می‌شود. // 4. `(...) == what[i]`: نتیجه عملیات XOR را با بایت `i`ام از رشته **ثابت `what`** مقایسه می‌کند. این مقایسه یک مقدار بولین (True یا False) برمی‌گرداند.
// د) `(unsigned __int8)v4 & (...)`: نتیجه این مقایسه (که True/False است) با مقدار فعلی `v4` به صورت **AND** بیتی می‌شود. // 5. `(unsigned __int8)v4 & (...)`: مقدار فعلی `v4` (که 0 یا 1 است) را با نتیجه بولین مقایسه قبل، به صورت **AND بیتی** می‌کند.
// یعنی اگر در هر مرحله یکی از مقایسه‌ها `False` شود، `v4` به `0` (False) تبدیل می‌شود و برای باقی حلقه همان `0` باقی می‌ماند. این بدان معناست که همه مقایسه‌ها باید `True` باشند تا تابع `True` برگرداند. // * اگر `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 🕵️‍♀️ تحلیل تابع check 🕵️‍♀️
C
__int64 __fastcall check(__int64 a1) // 1. تعریف تابع check: یک آرگومان (a1) از نوع __int64 می‌گیرد که به ورودی کاربر (رشته 's' در تابع main) اشاره دارد. مقدار برگشتی آن نیز __int64 است (که در واقع یک Boolean (true/false) را نشان می‌دهد). __int64 __fastcall check(__int64 a1) // 1. تعریف تابع check: یک آرگومان (a1) از نوع __int64 می‌گیرد که به ورودی کاربر (رشته 's' در تابع main) اشاره دارد. مقدار برگشتی آن نیز __int64 است (که در واقع یک Boolean (true/false) را نشان می‌دهد).
{ {
int v2; // [rsp+1Ch] [rbp-1Ch] // 2. تعریف متغیر محلی: برای ذخیره موقت یک کاراکتر (بایت) از ورودی کاربر. int v2; // [rsp+1Ch] [rbp-1Ch] // 2. تعریف متغیر محلی: برای ذخیره موقت یک کاراکتر (بایت) از ورودی کاربر.
@ -576,49 +607,66 @@ __int64 __fastcall check(__int64 a1) // 1. تعریف تابع check: یک آر
اسکریپت سوم: ### اسکریپت سوم:
```python ```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 = 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' 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' را با مقادیر استخراج شده از فایل باینری تعریف می‌کنند. # متغیر `secret` را تعریف می‌کند که حاوی بایت‌های کلید رمزنگاری است.
# 'what' در تابع check() استفاده می‌شود و 'secret' کلید XORینگ است. # این مقدار نیز از تحلیل فایل باینری به دست آمده و برای عملیات XOR نهایی استفاده می‌شود.
what = what.rstrip(b'\0') what = what.rstrip(b'\0')
# بایت‌های NULL (`\0`) را از انتهای رشته `what` حذف می‌کند.
# این کار برای اطمینان از اینکه طول دقیق داده‌ها در محاسبات بعدی استفاده شود، انجام می‌شود.
secret = secret.rstrip(b'\0') secret = secret.rstrip(b'\0')
# این خطوط بایت‌های NUL (صفر) را از انتهای رشته‌های 'what' و 'secret' حذف می‌کنند. # بایت‌های NULL (`\0`) را از انتهای رشته `secret` حذف می‌کند.
# در C، رشته‌ها با بایت NUL (\0) خاتمه می‌یابند. در پایتون، برای کار با طول دقیق داده‌ها، اغلب این بایت‌های اضافه حذف می‌شوند. # این کار نیز برای حفظ دقت طول رشته در عملیات XOR ضروری است.
for i in range(30, 127): for i in range(30, 127):
# این حلقه یک بروت فورس (حدس زدن) روی اولین کاراکتر فلگ (که ورودی 's' تابع main و 'a1' تابع check است) انجام می‌دهد. # این حلقه یک **بروت‌فورس (حدس زدن)** هوشمند را آغاز می‌کند.
# range(30, 127) شامل کدهای ASCII برای کاراکترهای قابل چاپ (اعداد، حروف، نمادها) است. # `range(30, 127)` شامل کدهای ASCII برای کاراکترهای قابل چاپ رایج (مانند اعداد، حروف، نمادها) است.
# فرض می‌شود که کاراکتر اول فلگ در این محدوده قرار دارد. # در هر تکرار، `i` به عنوان **اولین کاراکتر احتمالی ورودی صحیح** برای تابع `check()` در نظر گرفته می‌شود.
flag = [] # در هر تکرار حلقه 'i' (برای هر حدس کاراکتر اول)، یک لیست خالی 'flag' ایجاد می‌شود. flag = []
flag.append(i) # اولین حدس (کاراکتر 'i') به عنوان اولین عنصر به لیست 'flag' اضافه می‌شود. # در هر تکرار حلقه بیرونی (`for i in ...`)، یک لیست خالی به نام `flag` ایجاد می‌شود.
# این لیست 'flag' در اینجا در واقع کاندیدای رشته ورودی صحیح برای تابع check() است (یعنی همان 's' که باید پیدا شود). # این لیست `flag` در اینجا در واقع دنباله بایت‌های **ورودی صحیح برای تابع `check()`** (یعنی رشته `s` در تابع `main`) را نشان می‌دهد که ما در حال تلاش برای بازسازی آن هستیم.
flag.append(i)
# اولین کاراکتر حدس زده شده (`i`) به عنوان اولین بایت به لیست `flag` اضافه می‌شود.
for j in range(len(what)): for j in range(len(what)):
# این حلقه بقیه کاراکترهای کاندیدای فلگ را بر اساس منطق تابع check() بازسازی می‌کند. # این حلقه داخلی، بقیه بایت‌های دنباله ورودی صحیح (`flag` در اینجا) را بازسازی می‌کند.
# تابع check() از شرط `(*(char *)(a1 + (i + 1) % strlen(what)) ^ v2) == what[i]` استفاده می‌کرد. # این فرآیند **معکوس کردن منطق تابع `check()`** است.
# این به معنی `char_next ^ char_current == what[j]` است. # از آنجایی که از تابع `check()` می‌دانیم: `(بایت_بعدی_ورودی ^ بایت_فعلی_ورودی) == what[j]`
# با استفاده از خاصیت XOR (`A ^ B = C` پس `A = C ^ B`)، می‌توانیم کاراکتر بعدی را بازسازی کنیم: # با استفاده از خاصیت XOR (`A ^ B = C` پس `A = C ^ B`)، می‌توانیم بایت بعدی را به صورت زیر محاسبه کنیم:
# `char_next = what[j] ^ char_current` # ایت_بعدی_ورودی = بایت_فعلی_ورودی ^ what[j]`
flag.append(flag[j]^what[j]) flag.append(flag[j]^what[j])
# `flag[j]` در اینجا همان `char_current` است. # `flag[j]` (که در اینجا همان ایت_فعلی_ورودی` است) با `what[j]` (که از رشته ثابت `what` می‌آید) XOR می‌شود.
# `what[j]` همان مقدار 'what[i]' از تابع check است. # نتیجه XOR به عنوان ایت_بعدی_ورودی` (یعنی `flag[j+1]`) به لیست `flag` اضافه می‌شود.
# نتیجه XOR به عنوان `char_next` (یعنی `flag[j+1]`) به لیست اضافه می‌شود. # این خط به صورت زنجیره‌ای بایت‌های بعدی ورودی صحیح را بر اساس بایت قبلی و مقادیر `what` بازسازی می‌کند.
# این خط در واقع الگوریتم check را برای پیدا کردن ورودی صحیح، معکوس می‌کند.
newFlag = ""
# یک رشته خالی به نام `newFlag` ایجاد می‌شود.
# این رشته برای ذخیره فلگ نهایی (که پس از رمزگشایی کاندیدای ورودی صحیح با `secret` به دست می‌آید) استفاده می‌شود.
newFlag = "" # یک رشته خالی 'newFlag' برای ذخیره نتیجه نهایی رمزگشایی ایجاد می‌شود.
for j in range(len(what)): for j in range(len(what)):
# این حلقه، کاندیدای فلگ بازسازی شده (که در لیست 'flag' است) را با 'secret' XOR می‌کند. # این حلقه، کاندیدای ورودی صحیح بازسازی شده (که در لیست `flag` است) را با کلید `secret` XOR می‌کند.
# این دقیقاً همان عملیاتی است که برنامه اصلی روی ورودی صحیح (اگر تابع check() موفق باشد) انجام می‌دهد. # این عملیات **شبیه‌سازی فرآیند نهایی تولید فلگ توسط برنامه اصلی** است که در صورت موفقیت `check()` انجام می‌شود.
newFlag += chr(flag[j]^secret[j%len(secret)])
# `flag[j]`: کاراکتر jام از کاندیدای فلگ بازسازی شده (ورودی صحیح)
# `secret[j%len(secret)]`: بایت متناظر از کلید secret (به صورت دوره‌ای)
# `chr(...)`: تبدیل نتیجه XOR به کاراکتر و اضافه کردن به 'newFlag'.
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 ```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 = 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' 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` را با مقادیر استخراج شده از فایل باینری تعریف می‌کنند. # متغیر `secret` را تعریف می‌کند که حاوی بایت‌های کلید رمزنگاری است.
# `what` در تابع `check()` برای اعتبارسنجی ورودی استفاده می‌شود و `secret` کلید XORینگ برای رمزنگاری/رمزگشایی است. # این مقدار نیز از تحلیل فایل باینری به دست آمده و برای عملیات XOR نهایی استفاده می‌شود.
what = what.rstrip(b'\0') what = what.rstrip(b'\0')
# بایت‌های NULL (`\0`) را از انتهای رشته `what` حذف می‌کند.
# این کار برای اطمینان از اینکه طول دقیق داده‌ها در محاسبات بعدی استفاده شود، انجام می‌شود.
secret = secret.rstrip(b'\0') secret = secret.rstrip(b'\0')
# این خطوط بایت‌های `NUL` (`\0`) را از انتهای رشته‌های بایت `what` و `secret` حذف می‌کنند. # بایت‌های NULL (`\0`) را از انتهای رشته `secret` حذف می‌کند.
# این کار برای اطمینان از اینکه طول دقیق داده‌ها در عملیات بعدی استفاده شود، انجام می‌شود. # این کار نیز برای حفظ دقت طول رشته در عملیات XOR ضروری است.
for i in range(30, 127): for i in range(30, 127):
# این حلقه یک **بروت‌فورس هوشمند** را آغاز می‌کند. # این حلقه یک **بروت‌فورس (حدس زدن)** هوشمند را آغاز می‌کند.
# `range(30, 127)` شامل کدهای ASCII برای کاراکترهای قابل چاپ رایج است (مانند اعداد، حروف، نمادها). # `range(30, 127)` شامل کدهای ASCII برای کاراکترهای قابل چاپ رایج (مانند اعداد، حروف، نمادها) است.
# در هر تکرار، `i` به عنوان **اولین کاراکتر احتمالی فلگ صحیح** (ورودی‌ای که `check()` را True می‌کند) در نظر گرفته می‌شود. # در هر تکرار، `i` به عنوان **اولین کاراکتر احتمالی ورودی صحیح** برای تابع `check()` در نظر گرفته می‌شود.
flag = [] # برای هر حدس `i`، یک لیست خالی `flag` جدید ایجاد می‌شود. flag = []
flag.append(i) # حدس فعلی `i` به عنوان اولین بایت به این لیست `flag` اضافه می‌شود. # در هر تکرار حلقه بیرونی (`for i in ...`)، یک لیست خالی به نام `flag` ایجاد می‌شود.
# این لیست `flag` در اینجا در واقع دنباله بایت‌های **ورودی صحیح برای تابع `check()`** را نشان می‌دهد. # این لیست `flag` در اینجا در واقع دنباله بایت‌های **ورودی صحیح برای تابع `check()`** (یعنی رشته `s` در تابع `main`) را نشان می‌دهد که ما در حال تلاش برای بازسازی آن هستیم.
flag.append(i)
# اولین کاراکتر حدس زده شده (`i`) به عنوان اولین بایت به لیست `flag` اضافه می‌شود.
for j in range(len(what)): for j in range(len(what)):
# این حلقه بقیه بایت‌های دنباله ورودی صحیح (`flag` در اینجا) را بازسازی می‌کند. # این حلقه داخلی، بقیه بایت‌های دنباله ورودی صحیح (`flag` در اینجا) را بازسازی می‌کند.
# این فرآیند **معکوس کردن منطق تابع `check()`** است: # این فرآیند **معکوس کردن منطق تابع `check()`** است.
# از آنجایی که می‌دانیم `(next_char_of_input ^ current_char_of_input) == what[j]` باید درست باشد (از تابع `check()`), # از آنجایی که از تابع `check()` می‌دانیم: `(بایت_بعدی_ورودی ^ بایت_فعلی_ورودی) == what[j]`
# می‌توانیم `next_char_of_input` را با `current_char_of_input ^ what[j]` به دست آوریم. # با استفاده از خاصیت XOR (`A ^ B = C` پس `A = C ^ B`)، می‌توانیم بایت بعدی را به صورت زیر محاسبه کنیم:
# ایت_بعدی_ورودی = بایت_فعلی_ورودی ^ what[j]`
flag.append(flag[j]^what[j]) flag.append(flag[j]^what[j])
# `flag[j]` (که `current_char_of_input` است) با `what[j]` XOR می‌شود. # `flag[j]` (که در اینجا همان ایت_فعلی_ورودی` است) با `what[j]` (که از رشته ثابت `what` می‌آید) XOR می‌شود.
# نتیجه به عنوان `next_char_of_input` به لیست `flag` اضافه می‌شود. # نتیجه XOR به عنوان ایت_بعدی_ورودی` (یعنی `flag[j+1]`) به لیست `flag` اضافه می‌شود.
# این خط به صورت زنجیره‌ای بایت‌های بعدی ورودی صحیح را بر اساس بایت قبلی و مقادیر `what` بازسازی می‌کند.
newFlag = ""
# یک رشته خالی به نام `newFlag` ایجاد می‌شود.
# این رشته برای ذخیره فلگ نهایی (که پس از رمزگشایی کاندیدای ورودی صحیح با `secret` به دست می‌آید) استفاده می‌شود.
newFlag = "" # یک رشته خالی `newFlag` برای ذخیره نتیجه نهایی رمزگشایی ایجاد می‌شود.
for j in range(len(what)): for j in range(len(what)):
# این حلقه، کاندیدای فلگ بازسازی شده (که در لیست `flag` قرار دارد) را با `secret` XOR می‌کند. # این حلقه، کاندیدای ورودی صحیح بازسازی شده (که در لیست `flag` است) را با کلید `secret` XOR می‌کند.
# این دقیقاً همان عملیاتی است که برنامه اصلی روی **ورودی صحیح** (اگر تابع `check()` موفق باشد) انجام می‌دهد تا فلگ نهایی را چاپ کند. # این عملیات **شبیه‌سازی فرآیند نهایی تولید فلگ توسط برنامه اصلی** است که در صورت موفقیت `check()` انجام می‌شود.
char = chr(flag[j]^secret[j%len(secret)]) char = chr(flag[j]^secret[j%len(secret)])
# `flag[j]`: بایت `j`ام از کاندیدای فلگ بازسازی شده. # `flag[j]`: بایت `j`ام از کاندیدای ورودی صحیح بازسازی شده.
# `secret[j%len(secret)]`: بایت متناظر از کلید `secret` (به صورت دوره‌ای). # `secret[j%len(secret)]`: بایت متناظر از کلید `secret` را انتخاب می‌کند (با استفاده از عملگر `%` برای تکرار چرخشی کلید).
# `chr(...)`: تبدیل نتیجه XOR به کاراکتر. # `^`: عملیات XOR را بین دو بایت انجام می‌دهد.
# `chr()`: نتیجه عددی XOR را به کاراکتر ASCII مربوطه تبدیل می‌کند.
if ord(char) not in range(30, 127): if ord(char) not in range(30, 127):
# این خط **فیلتر اصلی** این قطعه کد است و آن را از نسخه قبلی متمایز می‌کند. # این خط **فیلتر اصلی** این قطعه کد است و آن را از نسخه قبلی (سوم) متمایز می‌کند.
# `ord(char)`: کد ASCII کاراکتر `char` را برمی‌گرداند. # `ord(char)`: کد ASCII کاراکتر `char` را برمی‌گرداند.
# `range(30, 127)`: محدوده کدهای ASCII برای کاراکترهای قابل چاپ است. # `range(30, 127)`: محدوده کدهای ASCII برای کاراکترهای قابل چاپ رایج است (شامل اعداد، حروف، نمادها و برخی کاراکترهای کنترلی محدود).
# `if ... not in ...`: اگر کاراکتر تولید شده **قابل چاپ نباشد** (مثلاً یک کاراکتر کنترلی یا غیرعادی باشد)... # `if ... not in ...`: اگر کاراکتر تولید شده **قابل چاپ نباشد** (مثلاً یک کاراکتر کنترلی، غیرقابل خواندن یا غیرمعمول باشد)...
continue continue
# ...کل حلقه داخلی (برای این کاندیدای `newFlag`) ادامه پیدا نمی‌کند و به تکرار بعدی (برای `j`) می‌رود. # ...این خط باعث می‌شود که حلقه داخلی (برای `j` فعلی) به تکرار بعدی برود و این کاراکتر به `newFlag` اضافه نشود.
# این باعث می‌شود رشته `newFlag` ناقص بماند یا اصلاً تشکیل نشود. # این فیلتر به شدت نتایج چاپ شده را محدود می‌کند و فقط آن دسته از خروجی‌هایی را که شبیه یک فلگ معتبر (شامل کاراکترهای قابل چاپ) هستند، نمایش می‌دهد.
else: else:
# اگر کاراکتر قابل چاپ باشد... # اگر کاراکتر قابل چاپ باشد (یعنی کد ASCII آن در محدوده مجاز باشد)...
newFlag += char newFlag += char
# ...آن را به رشته `newFlag` اضافه می‌کند. # ...آن کاراکتر به رشته `newFlag` اضافه می‌شود.
print(newFlag) # هر `newFlag` تولید شده (که بر اساس هر حدس اولیه `i` و فیلتر کاراکترهای قابل چاپ تشکیل شده) چاپ می‌شود. print(newFlag)
# هر `newFlag` تولید شده (که بر اساس هر حدس اولیه `i` و فرآیند کامل بازسازی و XOR نهایی، و پس از فیلتر کاراکترهای غیرقابل چاپ تشکیل شده است) در خروجی چاپ می‌شود.
``` ```
دید کلی درباره این قطعه کد: دید کلی درباره این قطعه کد: