Updated: 2021-01-BambooFox/better-than-asm
This commit is contained in:
parent
c748f98b21
commit
b66d5cfe83
|
|
@ -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 نهایی، و پس از فیلتر کاراکترهای غیرقابل چاپ تشکیل شده است) در خروجی چاپ میشود.
|
||||
```
|
||||
|
||||
دید کلی درباره این قطعه کد:
|
||||
|
|
|
|||
Loading…
Reference in New Issue