From e96f20157506a5e6ee492ea1466e6f9f1ce4846a Mon Sep 17 00:00:00 2001 From: Hadi Mottale Date: Tue, 22 Jul 2025 11:40:19 +0330 Subject: [PATCH] Updated: 2021-01-BambooFox/better-than-asm --- 2021-01-BambooFox/better-than-asm/README.md | 566 +++++++++++++++----- 1 file changed, 430 insertions(+), 136 deletions(-) diff --git a/2021-01-BambooFox/better-than-asm/README.md b/2021-01-BambooFox/better-than-asm/README.md index 34f9a48..f08e1cb 100644 --- a/2021-01-BambooFox/better-than-asm/README.md +++ b/2021-01-BambooFox/better-than-asm/README.md @@ -136,82 +136,148 @@ URL: https://github.com/rizinorg/cutter ```sh IDA Pro: int __cdecl main(int argc, const char **argv, const char **envp) +// تعریف تابع `main` که نقطه شروع اجرای هر برنامه C/C++ است. +// `argc`: تعداد آرگومان‌های خط فرمان. +// `argv`: آرایه‌ای از رشته‌ها که آرگومان‌های خط فرمان را نگه می‌دارد. +// `envp`: آرایه‌ای از رشته‌ها که متغیرهای محیطی را نگه می‌دارد. +// `__cdecl`: یک قرارداد فراخوانی (calling convention) استاندارد برای توابع C. { -// متغیرهای محلی (Local Variables) -// اینها معمولاً توسط دی‌کامپایلر به صورت خودکار نامگذاری می‌شوند (v4, v5, etc.) -// و سپس تحلیلگر می‌تواند نام‌های بامعنی‌تری به آنها بدهد. - char v4; // [rsp+14h] [rbp-94h] - char v5; // [rsp+34h] [rbp-74h] - size_t v6; // [rsp+40h] [rbp-68h] // احتمالاً برای ذخیره طول رشته s استفاده می‌شود. - int j; // [rsp+58h] [rbp-50h] // شمارنده حلقه for در بلاک 'else' - int i; // [rsp+5Ch] [rbp-4Ch] // شمارنده حلقه for در بلاک 'if' - char s[68]; // [rsp+60h] [rbp-48h] BYREF // آرایه‌ای برای ذخیره ورودی کاربر (68 بایت) - int v10; // [rsp+A4h] [rbp-4h] // متغیری برای ذخیره مقدار بازگشتی تابع (معمولاً 0 برای موفقیت، 1 برای خطا) + char v4; // [rsp+14h] [rbp-94h] + // تعریف یک متغیر محلی `char` به نام `v4`. + // این متغیر برای ذخیره موقت کاراکتر در حلقه رمزگشایی مسیر "غلط" استفاده می‌شود. - v10 = 0; // مقدار بازگشتی اولیه را 0 (موفقیت) تنظیم می‌کند. + char v5; // [rsp+34h] [rbp-74h] + // تعریف یک متغیر محلی `char` به نام `v5`. + // این متغیر برای ذخیره موقت کاراکتر در حلقه رمزگشایی مسیر "صحیح" استفاده می‌شود. -// چاپ پیام‌های خوش‌آمدگویی و درخواست فلگ از کاربر - printf("Only the chosen one will know what the flag is!\n"); - printf("Are you the chosen one?\n"); - printf("flag: "); + size_t v6; // [rsp+40h] [rbp-68h] + // تعریف یک متغیر محلی از نوع `size_t` به نام `v6`. + // این متغیر برای ذخیره طول رشته ورودی کاربر (`s`) استفاده می‌شود. -// دریافت ورودی کاربر - // %64s به این معنی است که حداکثر 64 کاراکتر را در بافر 's' می‌خواند (برای جلوگیری از Buffer Overflow) - __isoc99_scanf("%64s", s); + int j; // [rsp+58h] [rbp-50h] + // تعریف یک متغیر محلی از نوع `int` به نام `j`. + // این متغیر به عنوان شمارنده حلقه برای مسیر "غلط" استفاده می‌شود. -// محاسبه طول رشته ورودی کاربر - v6 = strlen(s); + int i; // [rsp+5Ch] [rbp-4Ch] + // تعریف یک متغیر محلی از نوع `int` به نام `i`. + // این متغیر به عنوان شمارنده حلقه برای مسیر "صحیح" استفاده می‌شود. -// شرط اصلی برنامه: مقایسه طول ورودی کاربر با طول یک رشته پنهان (what) - if ( v6 == strlen(&what) ) // اگر طول ورودی کاربر با طول "what" برابر باشد + char s[68]; // [rsp+60h] [rbp-48h] BYREF + // تعریف یک بافر (آرایه کاراکتری) به نام `s` با اندازه 68 بایت. + // این بافر برای ذخیره رشته ورودی کاربر که با `scanf` خوانده می‌شود، استفاده می‌شود. + // `BYREF` (اشاره شده توسط IDA/Cutter) نشان می‌دهد که این متغیر در پشته (stack) قرار دارد. + + int v10; // [rsp+A4h] [rbp-4h] + // تعریف یک متغیر محلی `int` به نام `v10`. + // این متغیر برای نگهداری کد خروجی (بازگشتی) برنامه استفاده می‌شود (0 برای موفقیت، 1 برای خطا). + + v10 = 0; + // مقداردهی اولیه `v10` به 0 (موفقیت). + + printf("Only the chosen one will know what the flag is!\n"); + // یک رشته متنی را در خروجی استاندارد (کنسول) چاپ می‌کند. + + printf("Are you the chosen one?\n"); + // یک رشته متنی دیگر را در خروجی استاندارد چاپ می‌کند. + + printf("flag: "); + // یک پیغام "flag: " را برای درخواست ورودی از کاربر چاپ می‌کند. + + __isoc99_scanf("%64s", s); + // ورودی کاربر را از ورودی استاندارد می‌خواند و آن را در بافر `s` ذخیره می‌کند. + // `"%64s"` به `scanf` می‌گوید که حداکثر 64 کاراکتر (به همراه یک کاراکتر null terminator) را بخواند تا از سرریز بافر (buffer overflow) جلوگیری کند. + // `__isoc99_scanf` نسخه استاندارد C99 از تابع `scanf` است. + + v6 = strlen(s); + // طول رشته ورودی کاربر (`s`) را محاسبه کرده و در `v6` ذخیره می‌کند. + + if ( v6 == strlen(&what) ) + // شرط اول: بررسی طول ورودی. + // `strlen(&what)` طول رشته ثابت `what` (که از باینری استخراج شده) را برمی‌گرداند. + // اگر طول ورودی کاربر (`s`) **برابر** با طول رشته `what` باشد، برنامه وارد بلوک `if` (مسیر اصلی اعتبارسنجی) می‌شود. + // در غیر این صورت، به بلوک `else` می‌رود. + { + if ( (unsigned int)check(s) ) + // شرط دوم: فراخوانی تابع `check()`. + // `check(s)` تابع `check` را با رشته ورودی کاربر `s` فراخوانی می‌کند. + // اگر `check(s)` مقدار `1` (True) برگرداند (یعنی ورودی صحیح باشد)، برنامه وارد این بلوک `if` (مسیر "صحیح") می‌شود. { -// اگر تابع 'check(s)' (که احتمالاً یک تابع اعتبارسنجی است) مقدار غیرصفر (true) برگرداند - if ( (unsigned int)check(s) ) - { -// حلقه اول: در صورت موفقیت 'check(s)' -// این حلقه یک عملیات XOR روی هر کاراکتر ورودی 's' با کاراکتر متناظر از 'secret' انجام می‌دهد. -// نتیجه به صورت کاراکتر به کاراکتر در خود 's' ذخیره می‌شود. - for ( i = 0; i < strlen(s); ++i ) - { - v5 = s[i]; // کاراکتر فعلی از ورودی کاربر - s[i] = secret[i % strlen(secret)] ^ v5; // XOR با 'secret' (به صورت دوره‌ای) - } - } - else // اگر تابع 'check(s)' مقدار صفر (false) برگرداند - { -// حلقه دوم: در صورت عدم موفقیت 'check(s)' -// این حلقه یک عملیات XOR روی هر کاراکتر از 'flag' (که به نظر می‌رسد فلگ واقعی است) -// با کاراکتر متناظر از 'secret' انجام می‌دهد. -// نتیجه به صورت کاراکتر به کاراکتر در 's' ذخیره می‌شود. - for ( j = 0; j < strlen(s); ++j ) // s[j] اینجا همان s ورودی کاربر است - { - v4 = flag[j]; // کاراکتر فعلی از رشته 'flag' - s[j] = secret[j % strlen(secret)] ^ v4; // XOR با 'secret' (به صورت دوره‌ای) - } - } -// چاپ نتیجه عملیات XOR (که در 's' ذخیره شده) با استفاده از فرمت مشخص شده - printf(format, s); - v10 = 0; // تنظیم مقدار بازگشتی به 0 (موفقیت) + for ( i = 0; i < strlen(s); ++i ) + // حلقه رمزگشایی برای مسیر "صحیح": + // اگر `check(s)` موفق باشد، این حلقه اجرا می‌شود. + + { + v5 = s[i]; + // بایت فعلی از رشته ورودی `s` را در `v5` ذخیره می‌کند. + + s[i] = secret[i % strlen(secret)] ^ v5; + // این خط عملیات **رمزگشایی فلگ واقعی** را انجام می‌دهد. + // بایت `i`ام از رشته `secret` (با استفاده از `%` برای تکرار کلید) با بایت `v5` (همان `s[i]`) XOR می‌شود. + // نتیجه دوباره در `s[i]` ذخیره می‌شود. + // این یعنی رشته `s` پس از این حلقه، تبدیل به فلگ واقعی رمزگشایی شده می‌شود. + } } - else // اگر طول ورودی کاربر با طول "what" برابر نباشد + else + // بلوک `else` برای `if (check(s))`: + // اگر `check(s)` مقدار `0` (False) برگرداند (یعنی ورودی غلط باشد)، برنامه وارد این بلوک `else` (مسیر "غلط") می‌شود. { -// چاپ پیام خطا (asc_40205A احتمالا آدرس یک رشته خطاست) - printf(asc_40205A); - v10 = 1; // تنظیم مقدار بازگشتی به 1 (خطا) + for ( j = 0; j < strlen(s); ++j ) + // حلقه رمزگشایی برای مسیر "غلط": + // اگر `check(s)` ناموفق باشد، این حلقه اجرا می‌شود. + + { + v4 = flag[j]; + // بایت `j`ام از رشته ثابت `flag` (که در واقع حاوی فلگ جعلی رمزنگاری شده است) را در `v4` ذخیره می‌کند. + + s[j] = secret[j % strlen(secret)] ^ v4; + // این خط عملیات **رمزگشایی فلگ جعلی** را انجام می‌دهد. + // بایت `j`ام از رشته `secret` (با تکرار کلید) با بایت `v4` (همان `flag[j]`) XOR می‌شود. + // نتیجه در `s[j]` ذخیره می‌شود. + // این یعنی رشته `s` پس از این حلقه، تبدیل به فلگ جعلی رمزگشایی شده می‌شود. + } } - return v10; // بازگرداندن وضعیت نهایی برنامه + printf(format, s); + // این خط، محتوای فعلی بافر `s` را چاپ می‌کند. + // بسته به اینکه کدام مسیر (صحیح یا غلط) فعال شده باشد، `s` یا حاوی فلگ واقعی رمزگشایی شده است یا فلگ جعلی رمزگشایی شده. + // `format` احتمالاً یک رشته فرمت (مانند `"%s"`) یا حتی خود فلگ جعلی (برای چاپ یک پیام خاص) است که در حافظه باینری قرار دارد. + + v10 = 0; + // `v10` دوباره به 0 تنظیم می‌شود (نشانگر خروجی موفق). + } + else + // بلوک `else` برای `if (v6 == strlen(&what))`: + // اگر طول ورودی کاربر با طول `what` برابر نباشد، این بلوک اجرا می‌شود. + { + printf(asc_40205A); + // یک پیام خطای عمومی (مثلاً "You are not the chosen one!" که قبلاً دیدیم) را چاپ می‌کند. + // `asc_40205A` اشاره به آدرس حافظه‌ای است که این رشته خطا در آن ذخیره شده. + + v10 = 1; + // `v10` به 1 تنظیم می‌شود (نشانگر خروجی خطا). + } + return v10; + // مقدار نهایی `v10` (0 برای موفقیت یا 1 برای خطا) را به عنوان کد خروجی برنامه برمی‌گرداند. } ``` ```sh Cutter: /* jsdec pseudo code output */ -/* /home/task.out @ 0x401230 */ // مسیر و آدرس شروع تابع main -#include // اضافه کردن هدر برای انواع داده استاندارد +/* /home/task.out @ 0x401230 */ +// اینها کامنت‌هایی هستند که توسط ابزار دی‌کامپایلر (jsdec) اضافه شده‌اند. +// نشان می‌دهند که این کد از فایل `task.out` و از آدرس `0x401230` شروع شده است. -int32_t main (int64_t arg_60h) { // تعریف تابع main، arg_60h احتمالاً نشان‌دهنده آرگومان‌های ورودی تابع است -// متغیرهای محلی +#include +// یک هدر استاندارد C را شامل می‌کند که تعاریف انواع داده‌ای با اندازه مشخص (مانند `int32_t`, `int64_t`) را فراهم می‌کند. + +int32_t main (int64_t arg_60h) { +// تعریف تابع `main`، نقطه شروع اجرای برنامه. +// `int32_t` نوع بازگشتی تابع است. +// `arg_60h` احتمالاً نمایشی از آرگومان‌های خط فرمان است که به تابع `main` پاس داده می‌شوند. + + // متغیرهای محلی // jsdec (دی‌کامپایلر Cutter) نام‌های متغیرها را بر اساس آفست آنها از RSP (Stack Pointer) تعیین می‌کند. + // این نام‌ها داخلی دی‌کامپایلر هستند و برای فهمیدن بهتر باید آن‌ها را با نام‌های معادل در IDA (مثل v4, v5, s و ...) مقایسه کنیم. int64_t var_a8h; size_t * var_a0h; int64_t var_94h; @@ -220,151 +286,380 @@ int32_t main (int64_t arg_60h) { // تعریف تابع main، arg_60h احتم size_t * var_80h; int64_t var_74h; int64_t var_70h; - size_t var_68h; // معادل v6 در IDA: برای ذخیره طول رشته + size_t var_68h; // معادل v6 در IDA: این متغیر برای ذخیره طول رشته ورودی کاربر استفاده می‌شود. int32_t var_60h; // معادل متغیرهای موقت برای ذخیره خروجی توابع (مثل scanf, printf) int32_t var_5ch; int32_t var_58h; int32_t var_54h; - int64_t var_50h; // معادل j در IDA: شمارنده حلقه دوم - int64_t var_4ch; // معادل i در IDA: شمارنده حلقه اول - const char * s; // معادل char s[68] در IDA: بافر ورودی کاربر. Cutter آن را به صورت اشاره‌گر به char نشان می‌دهد. - int64_t var_4h; // معادل v10 در IDA: متغیر بازگشتی تابع + int64_t var_50h; // معادل j در IDA: این متغیر به عنوان شمارنده حلقه برای مسیر "غلط" (else branch) استفاده می‌شود. + int64_t var_4ch; // معادل i در IDA: این متغیر به عنوان شمارنده حلقه برای مسیر "صحیح" (if branch) استفاده می‌شود. + const char * s; // معادل char s[68] در IDA: این متغیر بافر (آرایه کاراکتری) است که ورودی کاربر در آن ذخیره می‌شود. jsdec آن را به صورت اشاره‌گر به char نشان می‌دهد. + int64_t var_4h; // معادل v10 در IDA: این متغیر برای ذخیره مقدار بازگشتی نهایی تابع `main` (کد خروج برنامه) استفاده می‌شود. - var_4h = 0; // مقدار بازگشتی اولیه را 0 (موفقیت) تنظیم می‌کند. + var_4h = 0; + // مقدار اولیه متغیر `var_4h` (کد خروج برنامه) را به 0 (نشان‌دهنده موفقیت) تنظیم می‌کند. + + // چاپ پیام‌ها با استفاده از رجیستر RDI برای نگهداری آدرس رشته + rdi = "Only the chosen one will know what the flag is!\n"; + // آدرس رشته "Only the chosen one will know what the flag is!\n" را در رجیستر `RDI` قرار می‌دهد. + // در معماری x64، `RDI` رجیستر استاندارد برای اولین آرگومان توابع است. + + al = 0; + // رجیستر `AL` (بخشی از `RAX`) را به 0 تنظیم می‌کند. + // این معمولاً نشان‌دهنده این است که هیچ آرگومان floating-point به تابع پاس داده نمی‌شود. + + eax = printf (rdi); + // تابع `printf` را با آدرسی که در `RDI` قرار دارد فراخوانی می‌کند. + // مقدار بازگشتی `printf` (تعداد کاراکترهای چاپ شده) در `EAX` (بخشی از `RAX`) ذخیره می‌شود. -// چاپ پیام‌ها با استفاده از رجیستر RDI برای نگهداری آدرس رشته - rdi = "Only the chosen one will know what the flag is!\n"; // RDI رجیستر اولین آرگومان در تابع فراخوانی است. - al = 0; // AL رجیستر برای تعداد آرگومان‌های floating-point در x64 (اینجا 0 است) - eax = printf (rdi); // فراخوانی printf و ذخیره خروجی در EAX rdi = "Are you the chosen one?\n"; - var_54h = eax; // ذخیره موقت مقدار EAX + // آدرس رشته "Are you the chosen one?\n" را در `RDI` قرار می‌دهد. + + var_54h = eax; + // مقدار فعلی `EAX` (نتیجه `printf` قبلی) را به صورت موقت در `var_54h` ذخیره می‌کند. + al = 0; + // `AL` را دوباره به 0 تنظیم می‌کند. + eax = printf (rdi); + // تابع `printf` را برای چاپ رشته دوم فراخوانی می‌کند. + rdi = "flag: "; + // آدرس رشته "flag: " را در `RDI` قرار می‌دهد. + var_58h = eax; + // مقدار `EAX` (نتیجه `printf` قبلی) را در `var_58h` ذخیره می‌کند. + al = 0; + // `AL` را به 0 تنظیم می‌کند. + eax = printf (rdi); + // تابع `printf` را برای چاپ "flag: " فراخوانی می‌کند. -// دریافت ورودی کاربر + // دریافت ورودی کاربر // RSI برای آرگومان دوم (%64s) و RDI برای آرگومان اول (s) در scanf - rsi = &s; // آدرس بافر 's' - rdi = "%64s"; // فرمت ورودی + rsi = &s; + // آدرس بافر `s` (جایی که ورودی کاربر باید ذخیره شود) را در رجیستر `RSI` قرار می‌دهد. + // `RSI` رجیستر استاندارد برای آرگومان دوم توابع است. + + rdi = "%64s"; + // آدرس رشته فرمت `"%64s"` را در `RDI` قرار می‌دهد. + var_5ch = eax; + // مقدار `EAX` (نتیجه `printf` قبلی) را در `var_5ch` ذخیره می‌کند. + al = 0; - eax = isoc99_scanf (); // فراخوانی scanf + // `AL` را به 0 تنظیم می‌کند. + + eax = isoc99_scanf (); + // تابع `isoc99_scanf` (نسخه استاندارد C99 از `scanf`) را فراخوانی می‌کند تا ورودی کاربر را بخواند. + // ورودی بر اساس فرمت `"%64s"` (حداکثر 64 کاراکتر رشته‌ای) خوانده شده و در `s` ذخیره می‌شود. + rdi = &s; + // آدرس `s` را دوباره در `RDI` قرار می‌دهد (آماده‌سازی برای فراخوانی `strlen` بعدی). + var_60h = eax; + // مقدار بازگشتی `scanf` (تعداد آیتم‌های خوانده شده) را در `var_60h` ذخیره می‌کند. -// محاسبه طول رشته ورودی کاربر - rax = strlen (); // فراخوانی strlen روی 's' (آرگومان RDI) - edi = what; // آرگومان RDI برای strlen دوم (رشته 'what') - var_68h = rax; // ذخیره طول 's' در var_68h (معادل v6 در IDA) - rax = strlen (); // فراخوانی strlen روی 'what' (آرگومان EDI) + // محاسبه طول رشته ورودی کاربر + rax = strlen (); + // تابع `strlen` را روی رشته‌ای که آدرس آن در `RDI` (یعنی `s`) قرار دارد، فراخوانی می‌کند. + // طول رشته `s` در `RAX` ذخیره می‌شود. + + edi = what; + // آدرس رشته ثابت `what` را در `EDI` (که بخشی از `RDI` است) قرار می‌دهد. + // آماده‌سازی برای فراخوانی `strlen` بعدی روی `what`. + + var_68h = rax; + // طول رشته `s` (که در `RAX` بود) را در `var_68h` (معادل `v6` در IDA) ذخیره می‌کند. + + rax = strlen (); + // تابع `strlen` را روی رشته‌ای که آدرس آن در `EDI` (یعنی `what`) قرار دارد، فراخوانی می‌کند. + // طول رشته `what` در `RAX` ذخیره می‌شود. + + rcx = var_68h; + // مقدار `var_68h` (طول رشته `s`) را در رجیستر `RCX` قرار می‌دهد. + + if (rcx != rax) { + // شرط: اگر طول `s` (`RCX`) با طول `what` (`RAX`) برابر نباشد (یعنی `!=` به جای `==` در سورس اصلی). + // این همان شرط `if (v6 == strlen(&what))` است، اما با منطق معکوس برای `goto`. + + rdi = data.0040205a; + // آدرس رشته خطای "You are not the chosen one!" (که در حافظه `0x40205a` قرار دارد) را در `RDI` قرار می‌دهد. - rcx = var_68h; // rcx حاوی طول 's' است - if (rcx != rax) { // مقایسه طول 's' با طول 'what' - rdi = data.0040205a; // آدرس رشته خطا (معادل asc_40205A در IDA) al = 0; - printf (rdi); // چاپ پیام خطا - var_4h = 1; // تنظیم مقدار بازگشتی به 1 - goto label_0; // پرش به انتهای برنامه + // `AL` را به 0 تنظیم می‌کند. + + printf (rdi); + // پیام خطا را چاپ می‌کند. + + var_4h = 1; + // مقدار بازگشتی نهایی برنامه (`var_4h`) را به 1 (نشانگر خطا) تنظیم می‌کند. + + goto label_0; + // برنامه به `label_0` پرش می‌کند که انتهای تابع `main` است. } // اگر طول‌ها برابر باشند، تابع check(s) فراخوانی می‌شود - rdi = &s; // آرگومان تابع check - eax = check (); // فراخوانی check() + rdi = &s; + // آدرس بافر `s` را در `RDI` قرار می‌دهد (به عنوان آرگومان برای تابع `check`). - if (eax == 0) { // اگر check() مقدار 0 (false) برگرداند (معادل else در IDA) - goto label_1; // پرش به بلوک مربوط به حالت 'check' ناموفق + eax = check (); + // تابع `check()` را فراخوانی می‌کند و مقدار بازگشتی آن را در `EAX` ذخیره می‌کند. + + if (eax == 0) { + // شرط: اگر `check()` مقدار 0 (false) برگرداند. + // این همان `else` در کدهای دی‌کامپایل شده قبلی است. + + goto label_1; + // برنامه به `label_1` پرش می‌کند که بلوک کد مربوط به حالت `check` ناموفق است. } // بلوک کد در صورت موفقیت check() (معادل if در IDA) - var_4ch = 0; // شمارنده حلقه اول (معادل i در IDA) - do { // شروع حلقه for + var_4ch = 0; + // شمارنده حلقه `var_4ch` (معادل `i` در IDA) را به 0 تنظیم می‌کند. + + do { + // شروع یک حلقه `do-while` (معادل حلقه `for` در سورس اصلی). + rdi = &s; + // آدرس `s` را در `RDI` قرار می‌دهد. + rax = (int64_t) var_4ch; + // مقدار شمارنده `var_4ch` را به `RAX` منتقل می‌کند. + var_70h = rax; - rax = strlen (); // محاسبه strlen(s) در هر تکرار + // مقدار `RAX` را در `var_70h` ذخیره می‌کند. + + rax = strlen (); + // `strlen` را روی `s` فراخوانی می‌کند (این `strlen` هر بار در حلقه تکرار می‌شود که در C کامپایل شده رایج است). + rcx = var_70h; - if (rcx >= rax) { // شرط خروج از حلقه (i < strlen(s)) - goto label_2; // پرش به بعد از حلقه + // مقدار `var_70h` را در `RCX` قرار می‌دهد. + + if (rcx >= rax) { + // شرط خروج از حلقه: اگر `var_4ch` (شمارنده) بزرگتر یا مساوی طول `s` شود. + // این معادل `i < strlen(s)` در `for` لوپ است، اما برای پرش. + + goto label_2; + // پرش به `label_2` که بعد از حلقه رمزگشایی موفقیت‌آمیز است. } + rax = (int64_t) var_4ch; - ecx = *((rsp + rax + 0x60)); // دسترسی به s[i] (rsp+0x60 آدرس شروع بافر s است) - var_74h = ecx; // ذخیره s[i] + // مقدار شمارنده `var_4ch` را به `RAX` منتقل می‌کند. + + ecx = *((rsp + rax + 0x60)); + // به بایت `var_4ch`ام از بافر `s` دسترسی پیدا می‌کند (که در `rsp + 0x60` شروع می‌شود). + // این همان `s[i]` است. + + var_74h = ecx; + // مقدار `s[i]` را در `var_74h` ذخیره می‌کند (معادل `v5` در IDA). + rax = (int64_t) var_4ch; - edi = "B\n|_"; // این رشته یک artifact از دی‌کامپایلر است و مربوط به کد واقعی نیست. + // مقدار شمارنده `var_4ch` را به `RAX` منتقل می‌کند. + + edi = "B\n|_"; + // **نکته:** این خط `edi = "B\n|_"` یک artifact (محتوای باقیمانده یا تحلیل نادرست) از دی‌کامپایلر است. + // `secret` واقعی از آدرس `secret` در بخش `data` باینری خوانده می‌شود، نه از این رشته ثابت. + // این بخش نباید در ترجمه منطق واقعی برنامه در نظر گرفته شود. + var_80h = rax; - rax = strlen (); // محاسبه strlen(secret) + // مقدار `RAX` را در `var_80h` ذخیره می‌کند. + + rax = strlen (); + // `strlen` را روی `secret` فراخوانی می‌کند (که در `EDI` بود، اما اینجا اشاره به `secret` واقعی در حافظه دارد). + rdx = var_80h; + // مقدار `var_80h` را در `RDX` قرار می‌دهد. + var_88h = rax; + // طول `secret` را در `var_88h` ذخیره می‌کند. + rax = rdx; + // `RDX` (که حاوی `var_4ch` بود) را به `RAX` منتقل می‌کند. + ecx = 0; + // `ECX` را به 0 تنظیم می‌کند. + edx = ecx; + // `EDX` را به `ECX` (0) تنظیم می‌کند. + rsi = var_88h; - rax = rdx:rax / rsi; // محاسبه i / strlen(secret) (در واقع همان i % strlen(secret) است) - rdx = rdx:rax % rsi; // محاسبه i % strlen(secret) - ecx = *((rdx + secret)); // دسترسی به secret[i % strlen(secret)] - r8d = var_74h; // r8d حاوی s[i] است - r8d ^= ecx; // عملیات XOR: s[i] ^ secret[i % strlen(secret)] + // طول `secret` (`var_88h`) را به `RSI` منتقل می‌کند. + + rax = rdx:rax / rsi; + // این بخش محاسبات مربوط به `i / strlen(secret)` (تقسیم) را انجام می‌دهد. + + rdx = rdx:rax % rsi; + // این بخش محاسبات مربوط به `i % strlen(secret)` (باقیمانده) را انجام می‌دهد. + // `RDX` در نهایت حاوی نتیجه `i % strlen(secret)` است. + + ecx = *((rdx + secret)); + // به بایت مورد نظر از رشته `secret` (یعنی `secret[i % strlen(secret)]`) دسترسی پیدا می‌کند. + + r8d = var_74h; + // مقدار `var_74h` (که `s[i]` بود) را به `R8D` منتقل می‌کند. + + r8d ^= ecx; + // عملیات XOR را انجام می‌دهد: `s[i] ^ secret[i % strlen(secret)]`. + // نتیجه در `R8D` ذخیره می‌شود. + rdx = (int64_t) var_4ch; - *((rsp + rdx + 0x60)) = r8b; // ذخیره نتیجه در s[i] - eax = var_4ch; - eax++; // افزایش i + // مقدار شمارنده `var_4ch` را به `RDX` منتقل می‌کند. + + *((rsp + rdx + 0x60)) = r8b; + // نتیجه XOR را (که در `R8D` و به صورت `r8b` - بایت پایین‌تر از `R8D` - است) در `s[i]` ذخیره می‌کند. + // این `s[i]` اکنون حاوی یک کاراکتر از فلگ واقعی رمزگشایی شده است. + + eax++; + // شمارنده `eax` را یک واحد افزایش می‌دهد (معادل `i++`). + var_4ch = eax; - } while (1); // حلقه بی‌پایان تا زمانی که به 'goto label_2' برسد. + // مقدار افزایش یافته را در `var_4ch` ذخیره می‌کند. + + } while (1); + // حلقه بی‌پایان `do-while` که با دستورات `goto` کنترل می‌شود. label_2: rsi = &s; - rdi = format; // آدرس رشته فرمت برای printf - al = 0; - printf (rdi); // چاپ نتیجه - goto label_3; // پرش به انتهای موفقیت‌آمیز برنامه + // آدرس بافر `s` (که اکنون حاوی فلگ رمزگشایی شده است) را در `RSI` قرار می‌دهد. + + rdi = format; + // آدرس رشته فرمت برای `printf` (احتمالاً `"%s"` یا مشابه آن) را در `RDI` قرار می‌دهد. + + al = 0; + // `AL` را به 0 تنظیم می‌کند. + + printf (rdi); + // محتوای `s` (که فلگ رمزگشایی شده است) را چاپ می‌کند. + + goto label_3; + // پرش به `label_3` که بلوک مربوط به خروج موفقیت‌آمیز است. + +label_1: +// بلوک کد در صورت عدم موفقیت check() (معادل else در IDA) + var_50h = 0; + // شمارنده حلقه `var_50h` (معادل `j` در IDA) را به 0 تنظیم می‌کند. + + do { + // شروع یک حلقه `do-while` (معادل حلقه `for` در سورس اصلی). -label_1: // بلوک کد در صورت عدم موفقیت check() (معادل else در IDA) - var_50h = 0; // شمارنده حلقه دوم (معادل j در IDA) - do { // شروع حلقه for rdi = &s; + // آدرس `s` را در `RDI` قرار می‌دهد. + rax = (int64_t) var_50h; + // مقدار شمارنده `var_50h` را به `RAX` منتقل می‌کند. + var_90h = rax; - rax = strlen (); // محاسبه strlen(s) در هر تکرار + // مقدار `RAX` را در `var_90h` ذخیره می‌کند. + + rax = strlen (); + // `strlen` را روی `s` فراخوانی می‌کند. + rcx = var_90h; - if (rcx >= rax) { // شرط خروج از حلقه (j < strlen(s)) - goto label_4; // پرش به بعد از حلقه + // مقدار `var_90h` را در `RCX` قرار می‌دهد. + + if (rcx >= rax) { + // شرط خروج از حلقه: اگر `var_50h` (شمارنده) بزرگتر یا مساوی طول `s` شود. + + goto label_4; + // پرش به `label_4` که بعد از حلقه رمزگشایی فلگ جعلی است. } - rax = (int64_t) arg_60h; // arg_60h در اینجا احتمالاً base address برنامه یا یک متغیر است. - ecx = *((rax + flag)); // دسترسی به flag[j]. 'flag' باید آدرس رشته فلگ باشد. + + rax = (int64_t) arg_60h; + // **نکته:** `arg_60h` در اینجا به اشتباه استفاده شده است. + // این خط باید به جای آن به آدرس رشته ثابت `flag` (که حاوی فلگ جعلی رمزنگاری شده است) دسترسی پیدا کند. + // دی‌کامپایلر احتمالاً نتوانسته آدرس دقیق `flag` را ردیابی کند و به یک آرگومان اولیه اشاره کرده است. + + ecx = *((rax + flag)); + // به بایت `var_50h`ام از رشته `flag` (جعلی و رمزنگاری شده) دسترسی پیدا می‌کند. + // این همان `flag[j]` است. + rax = (int64_t) var_50h; - edi = "B\n|_"; // artifact دی‌کامپایلر - var_94h = ecx; // ذخیره flag[j] + // مقدار شمارنده `var_50h` را به `RAX` منتقل می‌کند. + + edi = "B\n|_"; + // **نکته:** این هم یک artifact دی‌کامپایلر است. مربوط به `secret` واقعی نیست. + + var_94h = ecx; + // مقدار `flag[j]` (جعلی و رمزنگاری شده) را در `var_94h` ذخیره می‌کند (معادل `v4` در IDA). + var_a0h = rax; - rax = strlen (); // محاسبه strlen(secret) + // مقدار `RAX` را در `var_a0h` ذخیره می‌کند. + + rax = strlen (); + // `strlen` را روی `secret` فراخوانی می‌کند. + rdx = var_a0h; + // مقدار `var_a0h` را در `RDX` قرار می‌دهد. + *(rsp) = rax; + // طول `secret` را به صورت موقت در پشته ذخیره می‌کند. + rax = rdx; + // `RDX` (که حاوی `var_50h` بود) را به `RAX` منتقل می‌کند. + ecx = 0; + // `ECX` را به 0 تنظیم می‌کند. + edx = ecx; + // `EDX` را به `ECX` (0) تنظیم می‌کند. + rsi = *(rsp); - rax = rdx:rax / rsi; // محاسبه j / strlen(secret) - rdx = rdx:rax % rsi; // محاسبه j % strlen(secret) - ecx = *((rdx + secret)); // دسترسی به secret[j % strlen(secret)] - r8d = var_94h; // r8d حاوی flag[j] است - r8d ^= ecx; // عملیات XOR: flag[j] ^ secret[j % strlen(secret)] + // طول `secret` را از پشته بازیابی کرده و در `RSI` قرار می‌دهد. + + rax = rdx:rax / rsi; + // محاسبه `j / strlen(secret)`. + + rdx = rdx:rax % rsi; + // محاسبه `j % strlen(secret)`. `RDX` حاوی نتیجه نهایی است. + + ecx = *((rdx + secret)); + // به بایت مورد نظر از رشته `secret` (یعنی `secret[j % strlen(secret)]`) دسترسی پیدا می‌کند. + + r8d = var_94h; + // مقدار `var_94h` (که `flag[j]` بود) را به `R8D` منتقل می‌کند. + + r8d ^= ecx; + // عملیات XOR را انجام می‌دهد: `flag[j] ^ secret[j % strlen(secret)]`. + // نتیجه در `R8D` ذخیره می‌شود. + rdx = (int64_t) var_50h; - *((rsp + rdx + 0x60)) = r8b; // ذخیره نتیجه در s[j] - eax = var_50h; - eax++; // افزایش j + // مقدار شمارنده `var_50h` را به `RDX` منتقل می‌کند. + + *((rsp + rdx + 0x60)) = r8b; + // نتیجه XOR را در `s[j]` ذخیره می‌کند. + // این `s[j]` اکنون حاوی یک کاراکتر از فلگ جعلی رمزگشایی شده است. + + eax++; + // شمارنده `eax` را یک واحد افزایش می‌دهد (معادل `j++`). + var_50h = eax; - } while (1); // حلقه بی‌پایان + // مقدار افزایش یافته را در `var_50h` ذخیره می‌کند. + + } while (1); + // حلقه بی‌پایان `do-while`. label_4: rsi = &s; - rdi = format; // آدرس رشته فرمت برای printf + // آدرس بافر `s` (که اکنون حاوی فلگ جعلی رمزگشایی شده است) را در `RSI` قرار می‌دهد. + + rdi = format; + // آدرس رشته فرمت برای `printf` (احتمالاً `"%s"` برای چاپ فلگ) را در `RDI` قرار می‌دهد. + al = 0; - printf (rdi); // چاپ نتیجه + // `AL` را به 0 تنظیم می‌کند. + + printf (rdi); + // محتوای `s` (فلگ جعلی) را چاپ می‌کند. + label_3: - var_4h = 0; // تنظیم مقدار بازگشتی به 0 (موفقیت) + var_4h = 0; + // `var_4h` (کد خروج) را به 0 تنظیم می‌کند. label_0: - eax = 0; // این خط نیز مقدار بازگشتی را 0 تنظیم می‌کند - return rax; // بازگرداندن وضعیت نهایی + eax = 0; + // `EAX` را به 0 تنظیم می‌کند. این خط نهایی است که مقدار بازگشتی تابع `main` را تعیین می‌کند. + + return rax; + // مقدار نهایی `RAX` (0) را به عنوان کد خروج برنامه بازمی‌گرداند. } ``` @@ -608,7 +903,6 @@ __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` است.