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

This commit is contained in:
Hadi Mottale 2025-07-22 11:40:19 +03:30
parent b66d5cfe83
commit e96f201575
1 changed files with 430 additions and 136 deletions

View File

@ -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` به نام `v4`.
// این متغیر برای ذخیره موقت کاراکتر در حلقه رمزگشایی مسیر "غلط" استفاده می‌شود.
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` به نام `v5`.
// این متغیر برای ذخیره موقت کاراکتر در حلقه رمزگشایی مسیر "صحیح" استفاده می‌شود.
v10 = 0; // مقدار بازگشتی اولیه را 0 (موفقیت) تنظیم می‌کند.
size_t v6; // [rsp+40h] [rbp-68h]
// تعریف یک متغیر محلی از نوع `size_t` به نام `v6`.
// این متغیر برای ذخیره طول رشته ورودی کاربر (`s`) استفاده می‌شود.
int j; // [rsp+58h] [rbp-50h]
// تعریف یک متغیر محلی از نوع `int` به نام `j`.
// این متغیر به عنوان شمارنده حلقه برای مسیر "غلط" استفاده می‌شود.
int i; // [rsp+5Ch] [rbp-4Ch]
// تعریف یک متغیر محلی از نوع `int` به نام `i`.
// این متغیر به عنوان شمارنده حلقه برای مسیر "صحیح" استفاده می‌شود.
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: " را برای درخواست ورودی از کاربر چاپ می‌کند.
// دریافت ورودی کاربر
// %64s به این معنی است که حداکثر 64 کاراکتر را در بافر 's' می‌خواند (برای جلوگیری از Buffer Overflow)
__isoc99_scanf("%64s", s);
// ورودی کاربر را از ورودی استاندارد می‌خواند و آن را در بافر `s` ذخیره می‌کند.
// `"%64s"` به `scanf` می‌گوید که حداکثر 64 کاراکتر (به همراه یک کاراکتر null terminator) را بخواند تا از سرریز بافر (buffer overflow) جلوگیری کند.
// `__isoc99_scanf` نسخه استاندارد C99 از تابع `scanf` است.
// محاسبه طول رشته ورودی کاربر
v6 = strlen(s);
// طول رشته ورودی کاربر (`s`) را محاسبه کرده و در `v6` ذخیره می‌کند.
// شرط اصلی برنامه: مقایسه طول ورودی کاربر با طول یک رشته پنهان (what)
if ( v6 == strlen(&what) ) // اگر طول ورودی کاربر با طول "what" برابر باشد
if ( v6 == strlen(&what) )
// شرط اول: بررسی طول ورودی.
// `strlen(&what)` طول رشته ثابت `what` (که از باینری استخراج شده) را برمی‌گرداند.
// اگر طول ورودی کاربر (`s`) **برابر** با طول رشته `what` باشد، برنامه وارد بلوک `if` (مسیر اصلی اعتبارسنجی) می‌شود.
// در غیر این صورت، به بلوک `else` می‌رود.
{
// اگر تابع 'check(s)' (که احتمالاً یک تابع اعتبارسنجی است) مقدار غیرصفر (true) برگرداند
if ( (unsigned int)check(s) )
// شرط دوم: فراخوانی تابع `check()`.
// `check(s)` تابع `check` را با رشته ورودی کاربر `s` فراخوانی می‌کند.
// اگر `check(s)` مقدار `1` (True) برگرداند (یعنی ورودی صحیح باشد)، برنامه وارد این بلوک `if` (مسیر "صحیح") می‌شود.
{
// حلقه اول: در صورت موفقیت 'check(s)'
// این حلقه یک عملیات XOR روی هر کاراکتر ورودی 's' با کاراکتر متناظر از 'secret' انجام می‌دهد.
// نتیجه به صورت کاراکتر به کاراکتر در خود 's' ذخیره می‌شود.
for ( i = 0; i < strlen(s); ++i )
// حلقه رمزگشایی برای مسیر "صحیح":
// اگر `check(s)` موفق باشد، این حلقه اجرا می‌شود.
{
v5 = s[i]; // کاراکتر فعلی از ورودی کاربر
s[i] = secret[i % strlen(secret)] ^ v5; // XOR با 'secret' (به صورت دوره‌ای)
v5 = s[i];
// بایت فعلی از رشته ورودی `s` را در `v5` ذخیره می‌کند.
s[i] = secret[i % strlen(secret)] ^ v5;
// این خط عملیات **رمزگشایی فلگ واقعی** را انجام می‌دهد.
// بایت `i`ام از رشته `secret` (با استفاده از `%` برای تکرار کلید) با بایت `v5` (همان `s[i]`) XOR می‌شود.
// نتیجه دوباره در `s[i]` ذخیره می‌شود.
// این یعنی رشته `s` پس از این حلقه، تبدیل به فلگ واقعی رمزگشایی شده می‌شود.
}
}
else // اگر تابع 'check(s)' مقدار صفر (false) برگرداند
else
// بلوک `else` برای `if (check(s))`:
// اگر `check(s)` مقدار `0` (False) برگرداند (یعنی ورودی غلط باشد)، برنامه وارد این بلوک `else` (مسیر "غلط") می‌شود.
{
// حلقه دوم: در صورت عدم موفقیت 'check(s)'
// این حلقه یک عملیات XOR روی هر کاراکتر از 'flag' (که به نظر می‌رسد فلگ واقعی است)
// با کاراکتر متناظر از 'secret' انجام می‌دهد.
// نتیجه به صورت کاراکتر به کاراکتر در 's' ذخیره می‌شود.
for ( j = 0; j < strlen(s); ++j ) // s[j] اینجا همان s ورودی کاربر است
for ( j = 0; j < strlen(s); ++j )
// حلقه رمزگشایی برای مسیر "غلط":
// اگر `check(s)` ناموفق باشد، این حلقه اجرا می‌شود.
{
v4 = flag[j]; // کاراکتر فعلی از رشته 'flag'
s[j] = secret[j % strlen(secret)] ^ v4; // XOR با 'secret' (به صورت دوره‌ای)
v4 = flag[j];
// بایت `j`ام از رشته ثابت `flag` (که در واقع حاوی فلگ جعلی رمزنگاری شده است) را در `v4` ذخیره می‌کند.
s[j] = secret[j % strlen(secret)] ^ v4;
// این خط عملیات **رمزگشایی فلگ جعلی** را انجام می‌دهد.
// بایت `j`ام از رشته `secret` (با تکرار کلید) با بایت `v4` (همان `flag[j]`) XOR می‌شود.
// نتیجه در `s[j]` ذخیره می‌شود.
// این یعنی رشته `s` پس از این حلقه، تبدیل به فلگ جعلی رمزگشایی شده می‌شود.
}
}
// چاپ نتیجه عملیات XOR (که در 's' ذخیره شده) با استفاده از فرمت مشخص شده
printf(format, s);
v10 = 0; // تنظیم مقدار بازگشتی به 0 (موفقیت)
// این خط، محتوای فعلی بافر `s` را چاپ می‌کند.
// بسته به اینکه کدام مسیر (صحیح یا غلط) فعال شده باشد، `s` یا حاوی فلگ واقعی رمزگشایی شده است یا فلگ جعلی رمزگشایی شده.
// `format` احتمالاً یک رشته فرمت (مانند `"%s"`) یا حتی خود فلگ جعلی (برای چاپ یک پیام خاص) است که در حافظه باینری قرار دارد.
v10 = 0;
// `v10` دوباره به 0 تنظیم می‌شود (نشانگر خروجی موفق).
}
else // اگر طول ورودی کاربر با طول "what" برابر نباشد
else
// بلوک `else` برای `if (v6 == strlen(&what))`:
// اگر طول ورودی کاربر با طول `what` برابر نباشد، این بلوک اجرا می‌شود.
{
// چاپ پیام خطا (asc_40205A احتمالا آدرس یک رشته خطاست)
printf(asc_40205A);
v10 = 1; // تنظیم مقدار بازگشتی به 1 (خطا)
// یک پیام خطای عمومی (مثلاً "You are not the chosen one!" که قبلاً دیدیم) را چاپ می‌کند.
// `asc_40205A` اشاره به آدرس حافظه‌ای است که این رشته خطا در آن ذخیره شده.
v10 = 1;
// `v10` به 1 تنظیم می‌شود (نشانگر خروجی خطا).
}
return v10; // بازگرداندن وضعیت نهایی برنامه
return v10;
// مقدار نهایی `v10` (0 برای موفقیت یا 1 برای خطا) را به عنوان کد خروجی برنامه برمی‌گرداند.
}
```
```sh
Cutter:
/* jsdec pseudo code output */
/* /home/task.out @ 0x401230 */ // مسیر و آدرس شروع تابع main
#include <stdint.h> // اضافه کردن هدر برای انواع داده استاندارد
/* /home/task.out @ 0x401230 */
// اینها کامنت‌هایی هستند که توسط ابزار دی‌کامپایلر (jsdec) اضافه شده‌اند.
// نشان می‌دهند که این کد از فایل `task.out` و از آدرس `0x401230` شروع شده است.
#include <stdint.h>
// یک هدر استاندارد C را شامل می‌کند که تعاریف انواع داده‌ای با اندازه مشخص (مانند `int32_t`, `int64_t`) را فراهم می‌کند.
int32_t main (int64_t arg_60h) {
// تعریف تابع `main`، نقطه شروع اجرای برنامه.
// `int32_t` نوع بازگشتی تابع است.
// `arg_60h` احتمالاً نمایشی از آرگومان‌های خط فرمان است که به تابع `main` پاس داده می‌شوند.
int32_t main (int64_t arg_60h) { // تعریف تابع main، arg_60h احتمالاً نشان‌دهنده آرگومان‌های ورودی تابع است
// متغیرهای محلی
// 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"; // RDI رجیستر اولین آرگومان در تابع فراخوانی است.
al = 0; // AL رجیستر برای تعداد آرگومان‌های floating-point در x64 (اینجا 0 است)
eax = printf (rdi); // فراخوانی printf و ذخیره خروجی در EAX
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 = "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` است.