Updated: 2021-01-BambooFox/better-than-asm
This commit is contained in:
parent
b66d5cfe83
commit
e96f201575
|
|
@ -136,82 +136,148 @@ URL: https://github.com/rizinorg/cutter
|
||||||
```sh
|
```sh
|
||||||
IDA Pro:
|
IDA Pro:
|
||||||
int __cdecl main(int argc, const char **argv, const char **envp)
|
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; // [rsp+14h] [rbp-94h]
|
||||||
|
// تعریف یک متغیر محلی `char` به نام `v4`.
|
||||||
|
// این متغیر برای ذخیره موقت کاراکتر در حلقه رمزگشایی مسیر "غلط" استفاده میشود.
|
||||||
|
|
||||||
char v5; // [rsp+34h] [rbp-74h]
|
char v5; // [rsp+34h] [rbp-74h]
|
||||||
size_t v6; // [rsp+40h] [rbp-68h] // احتمالاً برای ذخیره طول رشته s استفاده میشود.
|
// تعریف یک متغیر محلی `char` به نام `v5`.
|
||||||
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 برای خطا)
|
|
||||||
|
|
||||||
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("Only the chosen one will know what the flag is!\n");
|
||||||
|
// یک رشته متنی را در خروجی استاندارد (کنسول) چاپ میکند.
|
||||||
|
|
||||||
printf("Are you the chosen one?\n");
|
printf("Are you the chosen one?\n");
|
||||||
|
// یک رشته متنی دیگر را در خروجی استاندارد چاپ میکند.
|
||||||
|
|
||||||
printf("flag: ");
|
printf("flag: ");
|
||||||
|
// یک پیغام "flag: " را برای درخواست ورودی از کاربر چاپ میکند.
|
||||||
|
|
||||||
// دریافت ورودی کاربر
|
|
||||||
// %64s به این معنی است که حداکثر 64 کاراکتر را در بافر 's' میخواند (برای جلوگیری از Buffer Overflow)
|
|
||||||
__isoc99_scanf("%64s", s);
|
__isoc99_scanf("%64s", s);
|
||||||
|
// ورودی کاربر را از ورودی استاندارد میخواند و آن را در بافر `s` ذخیره میکند.
|
||||||
|
// `"%64s"` به `scanf` میگوید که حداکثر 64 کاراکتر (به همراه یک کاراکتر null terminator) را بخواند تا از سرریز بافر (buffer overflow) جلوگیری کند.
|
||||||
|
// `__isoc99_scanf` نسخه استاندارد C99 از تابع `scanf` است.
|
||||||
|
|
||||||
// محاسبه طول رشته ورودی کاربر
|
|
||||||
v6 = strlen(s);
|
v6 = strlen(s);
|
||||||
|
// طول رشته ورودی کاربر (`s`) را محاسبه کرده و در `v6` ذخیره میکند.
|
||||||
|
|
||||||
// شرط اصلی برنامه: مقایسه طول ورودی کاربر با طول یک رشته پنهان (what)
|
if ( v6 == strlen(&what) )
|
||||||
if ( v6 == strlen(&what) ) // اگر طول ورودی کاربر با طول "what" برابر باشد
|
// شرط اول: بررسی طول ورودی.
|
||||||
|
// `strlen(&what)` طول رشته ثابت `what` (که از باینری استخراج شده) را برمیگرداند.
|
||||||
|
// اگر طول ورودی کاربر (`s`) **برابر** با طول رشته `what` باشد، برنامه وارد بلوک `if` (مسیر اصلی اعتبارسنجی) میشود.
|
||||||
|
// در غیر این صورت، به بلوک `else` میرود.
|
||||||
{
|
{
|
||||||
// اگر تابع 'check(s)' (که احتمالاً یک تابع اعتبارسنجی است) مقدار غیرصفر (true) برگرداند
|
|
||||||
if ( (unsigned int)check(s) )
|
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 )
|
for ( i = 0; i < strlen(s); ++i )
|
||||||
|
// حلقه رمزگشایی برای مسیر "صحیح":
|
||||||
|
// اگر `check(s)` موفق باشد، این حلقه اجرا میشود.
|
||||||
|
|
||||||
{
|
{
|
||||||
v5 = s[i]; // کاراکتر فعلی از ورودی کاربر
|
v5 = s[i];
|
||||||
s[i] = secret[i % strlen(secret)] ^ v5; // XOR با 'secret' (به صورت دورهای)
|
// بایت فعلی از رشته ورودی `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)'
|
for ( j = 0; j < strlen(s); ++j )
|
||||||
// این حلقه یک عملیات XOR روی هر کاراکتر از 'flag' (که به نظر میرسد فلگ واقعی است)
|
// حلقه رمزگشایی برای مسیر "غلط":
|
||||||
// با کاراکتر متناظر از 'secret' انجام میدهد.
|
// اگر `check(s)` ناموفق باشد، این حلقه اجرا میشود.
|
||||||
// نتیجه به صورت کاراکتر به کاراکتر در 's' ذخیره میشود.
|
|
||||||
for ( j = 0; j < strlen(s); ++j ) // s[j] اینجا همان s ورودی کاربر است
|
|
||||||
{
|
{
|
||||||
v4 = flag[j]; // کاراکتر فعلی از رشته 'flag'
|
v4 = flag[j];
|
||||||
s[j] = secret[j % strlen(secret)] ^ v4; // XOR با 'secret' (به صورت دورهای)
|
// بایت `j`ام از رشته ثابت `flag` (که در واقع حاوی فلگ جعلی رمزنگاری شده است) را در `v4` ذخیره میکند.
|
||||||
|
|
||||||
|
s[j] = secret[j % strlen(secret)] ^ v4;
|
||||||
|
// این خط عملیات **رمزگشایی فلگ جعلی** را انجام میدهد.
|
||||||
|
// بایت `j`ام از رشته `secret` (با تکرار کلید) با بایت `v4` (همان `flag[j]`) XOR میشود.
|
||||||
|
// نتیجه در `s[j]` ذخیره میشود.
|
||||||
|
// این یعنی رشته `s` پس از این حلقه، تبدیل به فلگ جعلی رمزگشایی شده میشود.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// چاپ نتیجه عملیات XOR (که در 's' ذخیره شده) با استفاده از فرمت مشخص شده
|
|
||||||
printf(format, 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);
|
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
|
```sh
|
||||||
Cutter:
|
Cutter:
|
||||||
/* jsdec pseudo code output */
|
/* jsdec pseudo code output */
|
||||||
/* /home/task.out @ 0x401230 */ // مسیر و آدرس شروع تابع main
|
/* /home/task.out @ 0x401230 */
|
||||||
#include <stdint.h> // اضافه کردن هدر برای انواع داده استاندارد
|
// اینها کامنتهایی هستند که توسط ابزار دیکامپایلر (jsdec) اضافه شدهاند.
|
||||||
|
// نشان میدهند که این کد از فایل `task.out` و از آدرس `0x401230` شروع شده است.
|
||||||
|
|
||||||
int32_t main (int64_t arg_60h) { // تعریف تابع main، arg_60h احتمالاً نشاندهنده آرگومانهای ورودی تابع است
|
#include <stdint.h>
|
||||||
// متغیرهای محلی
|
// یک هدر استاندارد C را شامل میکند که تعاریف انواع دادهای با اندازه مشخص (مانند `int32_t`, `int64_t`) را فراهم میکند.
|
||||||
|
|
||||||
|
int32_t main (int64_t arg_60h) {
|
||||||
|
// تعریف تابع `main`، نقطه شروع اجرای برنامه.
|
||||||
|
// `int32_t` نوع بازگشتی تابع است.
|
||||||
|
// `arg_60h` احتمالاً نمایشی از آرگومانهای خط فرمان است که به تابع `main` پاس داده میشوند.
|
||||||
|
|
||||||
|
// متغیرهای محلی
|
||||||
// jsdec (دیکامپایلر Cutter) نامهای متغیرها را بر اساس آفست آنها از RSP (Stack Pointer) تعیین میکند.
|
// jsdec (دیکامپایلر Cutter) نامهای متغیرها را بر اساس آفست آنها از RSP (Stack Pointer) تعیین میکند.
|
||||||
|
// این نامها داخلی دیکامپایلر هستند و برای فهمیدن بهتر باید آنها را با نامهای معادل در IDA (مثل v4, v5, s و ...) مقایسه کنیم.
|
||||||
int64_t var_a8h;
|
int64_t var_a8h;
|
||||||
size_t * var_a0h;
|
size_t * var_a0h;
|
||||||
int64_t var_94h;
|
int64_t var_94h;
|
||||||
|
|
@ -220,151 +286,380 @@ int32_t main (int64_t arg_60h) { // تعریف تابع main، arg_60h احتم
|
||||||
size_t * var_80h;
|
size_t * var_80h;
|
||||||
int64_t var_74h;
|
int64_t var_74h;
|
||||||
int64_t var_70h;
|
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_60h; // معادل متغیرهای موقت برای ذخیره خروجی توابع (مثل scanf, printf)
|
||||||
int32_t var_5ch;
|
int32_t var_5ch;
|
||||||
int32_t var_58h;
|
int32_t var_58h;
|
||||||
int32_t var_54h;
|
int32_t var_54h;
|
||||||
int64_t var_50h; // معادل j در IDA: شمارنده حلقه دوم
|
int64_t var_50h; // معادل j در IDA: این متغیر به عنوان شمارنده حلقه برای مسیر "غلط" (else branch) استفاده میشود.
|
||||||
int64_t var_4ch; // معادل i در IDA: شمارنده حلقه اول
|
int64_t var_4ch; // معادل i در IDA: این متغیر به عنوان شمارنده حلقه برای مسیر "صحیح" (if branch) استفاده میشود.
|
||||||
const char * s; // معادل char s[68] در IDA: بافر ورودی کاربر. Cutter آن را به صورت اشارهگر به char نشان میدهد.
|
const char * s; // معادل char s[68] در IDA: این متغیر بافر (آرایه کاراکتری) است که ورودی کاربر در آن ذخیره میشود. jsdec آن را به صورت اشارهگر به char نشان میدهد.
|
||||||
int64_t var_4h; // معادل v10 در IDA: متغیر بازگشتی تابع
|
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";
|
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;
|
||||||
|
// `AL` را دوباره به 0 تنظیم میکند.
|
||||||
|
|
||||||
eax = printf (rdi);
|
eax = printf (rdi);
|
||||||
|
// تابع `printf` را برای چاپ رشته دوم فراخوانی میکند.
|
||||||
|
|
||||||
rdi = "flag: ";
|
rdi = "flag: ";
|
||||||
|
// آدرس رشته "flag: " را در `RDI` قرار میدهد.
|
||||||
|
|
||||||
var_58h = eax;
|
var_58h = eax;
|
||||||
|
// مقدار `EAX` (نتیجه `printf` قبلی) را در `var_58h` ذخیره میکند.
|
||||||
|
|
||||||
al = 0;
|
al = 0;
|
||||||
|
// `AL` را به 0 تنظیم میکند.
|
||||||
|
|
||||||
eax = printf (rdi);
|
eax = printf (rdi);
|
||||||
|
// تابع `printf` را برای چاپ "flag: " فراخوانی میکند.
|
||||||
|
|
||||||
// دریافت ورودی کاربر
|
// دریافت ورودی کاربر
|
||||||
// RSI برای آرگومان دوم (%64s) و RDI برای آرگومان اول (s) در scanf
|
// RSI برای آرگومان دوم (%64s) و RDI برای آرگومان اول (s) در scanf
|
||||||
rsi = &s; // آدرس بافر 's'
|
rsi = &s;
|
||||||
rdi = "%64s"; // فرمت ورودی
|
// آدرس بافر `s` (جایی که ورودی کاربر باید ذخیره شود) را در رجیستر `RSI` قرار میدهد.
|
||||||
|
// `RSI` رجیستر استاندارد برای آرگومان دوم توابع است.
|
||||||
|
|
||||||
|
rdi = "%64s";
|
||||||
|
// آدرس رشته فرمت `"%64s"` را در `RDI` قرار میدهد.
|
||||||
|
|
||||||
var_5ch = eax;
|
var_5ch = eax;
|
||||||
|
// مقدار `EAX` (نتیجه `printf` قبلی) را در `var_5ch` ذخیره میکند.
|
||||||
|
|
||||||
al = 0;
|
al = 0;
|
||||||
eax = isoc99_scanf (); // فراخوانی scanf
|
// `AL` را به 0 تنظیم میکند.
|
||||||
|
|
||||||
|
eax = isoc99_scanf ();
|
||||||
|
// تابع `isoc99_scanf` (نسخه استاندارد C99 از `scanf`) را فراخوانی میکند تا ورودی کاربر را بخواند.
|
||||||
|
// ورودی بر اساس فرمت `"%64s"` (حداکثر 64 کاراکتر رشتهای) خوانده شده و در `s` ذخیره میشود.
|
||||||
|
|
||||||
rdi = &s;
|
rdi = &s;
|
||||||
|
// آدرس `s` را دوباره در `RDI` قرار میدهد (آمادهسازی برای فراخوانی `strlen` بعدی).
|
||||||
|
|
||||||
var_60h = eax;
|
var_60h = eax;
|
||||||
|
// مقدار بازگشتی `scanf` (تعداد آیتمهای خوانده شده) را در `var_60h` ذخیره میکند.
|
||||||
|
|
||||||
// محاسبه طول رشته ورودی کاربر
|
// محاسبه طول رشته ورودی کاربر
|
||||||
rax = strlen (); // فراخوانی strlen روی 's' (آرگومان RDI)
|
rax = strlen ();
|
||||||
edi = what; // آرگومان RDI برای strlen دوم (رشته 'what')
|
// تابع `strlen` را روی رشتهای که آدرس آن در `RDI` (یعنی `s`) قرار دارد، فراخوانی میکند.
|
||||||
var_68h = rax; // ذخیره طول 's' در var_68h (معادل v6 در IDA)
|
// طول رشته `s` در `RAX` ذخیره میشود.
|
||||||
rax = strlen (); // فراخوانی strlen روی 'what' (آرگومان EDI)
|
|
||||||
|
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;
|
al = 0;
|
||||||
printf (rdi); // چاپ پیام خطا
|
// `AL` را به 0 تنظیم میکند.
|
||||||
var_4h = 1; // تنظیم مقدار بازگشتی به 1
|
|
||||||
goto label_0; // پرش به انتهای برنامه
|
printf (rdi);
|
||||||
|
// پیام خطا را چاپ میکند.
|
||||||
|
|
||||||
|
var_4h = 1;
|
||||||
|
// مقدار بازگشتی نهایی برنامه (`var_4h`) را به 1 (نشانگر خطا) تنظیم میکند.
|
||||||
|
|
||||||
|
goto label_0;
|
||||||
|
// برنامه به `label_0` پرش میکند که انتهای تابع `main` است.
|
||||||
}
|
}
|
||||||
|
|
||||||
// اگر طولها برابر باشند، تابع check(s) فراخوانی میشود
|
// اگر طولها برابر باشند، تابع check(s) فراخوانی میشود
|
||||||
rdi = &s; // آرگومان تابع check
|
rdi = &s;
|
||||||
eax = check (); // فراخوانی check()
|
// آدرس بافر `s` را در `RDI` قرار میدهد (به عنوان آرگومان برای تابع `check`).
|
||||||
|
|
||||||
if (eax == 0) { // اگر check() مقدار 0 (false) برگرداند (معادل else در IDA)
|
eax = check ();
|
||||||
goto label_1; // پرش به بلوک مربوط به حالت 'check' ناموفق
|
// تابع `check()` را فراخوانی میکند و مقدار بازگشتی آن را در `EAX` ذخیره میکند.
|
||||||
|
|
||||||
|
if (eax == 0) {
|
||||||
|
// شرط: اگر `check()` مقدار 0 (false) برگرداند.
|
||||||
|
// این همان `else` در کدهای دیکامپایل شده قبلی است.
|
||||||
|
|
||||||
|
goto label_1;
|
||||||
|
// برنامه به `label_1` پرش میکند که بلوک کد مربوط به حالت `check` ناموفق است.
|
||||||
}
|
}
|
||||||
|
|
||||||
// بلوک کد در صورت موفقیت check() (معادل if در IDA)
|
// بلوک کد در صورت موفقیت check() (معادل if در IDA)
|
||||||
var_4ch = 0; // شمارنده حلقه اول (معادل i در IDA)
|
var_4ch = 0;
|
||||||
do { // شروع حلقه for
|
// شمارنده حلقه `var_4ch` (معادل `i` در IDA) را به 0 تنظیم میکند.
|
||||||
|
|
||||||
|
do {
|
||||||
|
// شروع یک حلقه `do-while` (معادل حلقه `for` در سورس اصلی).
|
||||||
|
|
||||||
rdi = &s;
|
rdi = &s;
|
||||||
|
// آدرس `s` را در `RDI` قرار میدهد.
|
||||||
|
|
||||||
rax = (int64_t) var_4ch;
|
rax = (int64_t) var_4ch;
|
||||||
|
// مقدار شمارنده `var_4ch` را به `RAX` منتقل میکند.
|
||||||
|
|
||||||
var_70h = rax;
|
var_70h = rax;
|
||||||
rax = strlen (); // محاسبه strlen(s) در هر تکرار
|
// مقدار `RAX` را در `var_70h` ذخیره میکند.
|
||||||
|
|
||||||
|
rax = strlen ();
|
||||||
|
// `strlen` را روی `s` فراخوانی میکند (این `strlen` هر بار در حلقه تکرار میشود که در C کامپایل شده رایج است).
|
||||||
|
|
||||||
rcx = var_70h;
|
rcx = var_70h;
|
||||||
if (rcx >= rax) { // شرط خروج از حلقه (i < strlen(s))
|
// مقدار `var_70h` را در `RCX` قرار میدهد.
|
||||||
goto label_2; // پرش به بعد از حلقه
|
|
||||||
|
if (rcx >= rax) {
|
||||||
|
// شرط خروج از حلقه: اگر `var_4ch` (شمارنده) بزرگتر یا مساوی طول `s` شود.
|
||||||
|
// این معادل `i < strlen(s)` در `for` لوپ است، اما برای پرش.
|
||||||
|
|
||||||
|
goto label_2;
|
||||||
|
// پرش به `label_2` که بعد از حلقه رمزگشایی موفقیتآمیز است.
|
||||||
}
|
}
|
||||||
|
|
||||||
rax = (int64_t) var_4ch;
|
rax = (int64_t) var_4ch;
|
||||||
ecx = *((rsp + rax + 0x60)); // دسترسی به s[i] (rsp+0x60 آدرس شروع بافر s است)
|
// مقدار شمارنده `var_4ch` را به `RAX` منتقل میکند.
|
||||||
var_74h = ecx; // ذخیره s[i]
|
|
||||||
|
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;
|
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;
|
var_80h = rax;
|
||||||
rax = strlen (); // محاسبه strlen(secret)
|
// مقدار `RAX` را در `var_80h` ذخیره میکند.
|
||||||
|
|
||||||
|
rax = strlen ();
|
||||||
|
// `strlen` را روی `secret` فراخوانی میکند (که در `EDI` بود، اما اینجا اشاره به `secret` واقعی در حافظه دارد).
|
||||||
|
|
||||||
rdx = var_80h;
|
rdx = var_80h;
|
||||||
|
// مقدار `var_80h` را در `RDX` قرار میدهد.
|
||||||
|
|
||||||
var_88h = rax;
|
var_88h = rax;
|
||||||
|
// طول `secret` را در `var_88h` ذخیره میکند.
|
||||||
|
|
||||||
rax = rdx;
|
rax = rdx;
|
||||||
|
// `RDX` (که حاوی `var_4ch` بود) را به `RAX` منتقل میکند.
|
||||||
|
|
||||||
ecx = 0;
|
ecx = 0;
|
||||||
|
// `ECX` را به 0 تنظیم میکند.
|
||||||
|
|
||||||
edx = ecx;
|
edx = ecx;
|
||||||
|
// `EDX` را به `ECX` (0) تنظیم میکند.
|
||||||
|
|
||||||
rsi = var_88h;
|
rsi = var_88h;
|
||||||
rax = rdx:rax / rsi; // محاسبه i / strlen(secret) (در واقع همان i % strlen(secret) است)
|
// طول `secret` (`var_88h`) را به `RSI` منتقل میکند.
|
||||||
rdx = rdx:rax % rsi; // محاسبه i % strlen(secret)
|
|
||||||
ecx = *((rdx + secret)); // دسترسی به secret[i % strlen(secret)]
|
rax = rdx:rax / rsi;
|
||||||
r8d = var_74h; // r8d حاوی s[i] است
|
// این بخش محاسبات مربوط به `i / strlen(secret)` (تقسیم) را انجام میدهد.
|
||||||
r8d ^= ecx; // عملیات XOR: s[i] ^ secret[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;
|
rdx = (int64_t) var_4ch;
|
||||||
*((rsp + rdx + 0x60)) = r8b; // ذخیره نتیجه در s[i]
|
// مقدار شمارنده `var_4ch` را به `RDX` منتقل میکند.
|
||||||
eax = var_4ch;
|
|
||||||
eax++; // افزایش i
|
*((rsp + rdx + 0x60)) = r8b;
|
||||||
|
// نتیجه XOR را (که در `R8D` و به صورت `r8b` - بایت پایینتر از `R8D` - است) در `s[i]` ذخیره میکند.
|
||||||
|
// این `s[i]` اکنون حاوی یک کاراکتر از فلگ واقعی رمزگشایی شده است.
|
||||||
|
|
||||||
|
eax++;
|
||||||
|
// شمارنده `eax` را یک واحد افزایش میدهد (معادل `i++`).
|
||||||
|
|
||||||
var_4ch = eax;
|
var_4ch = eax;
|
||||||
} while (1); // حلقه بیپایان تا زمانی که به 'goto label_2' برسد.
|
// مقدار افزایش یافته را در `var_4ch` ذخیره میکند.
|
||||||
|
|
||||||
|
} while (1);
|
||||||
|
// حلقه بیپایان `do-while` که با دستورات `goto` کنترل میشود.
|
||||||
label_2:
|
label_2:
|
||||||
rsi = &s;
|
rsi = &s;
|
||||||
rdi = format; // آدرس رشته فرمت برای printf
|
// آدرس بافر `s` (که اکنون حاوی فلگ رمزگشایی شده است) را در `RSI` قرار میدهد.
|
||||||
al = 0;
|
|
||||||
printf (rdi); // چاپ نتیجه
|
rdi = format;
|
||||||
goto label_3; // پرش به انتهای موفقیتآمیز برنامه
|
// آدرس رشته فرمت برای `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;
|
rdi = &s;
|
||||||
|
// آدرس `s` را در `RDI` قرار میدهد.
|
||||||
|
|
||||||
rax = (int64_t) var_50h;
|
rax = (int64_t) var_50h;
|
||||||
|
// مقدار شمارنده `var_50h` را به `RAX` منتقل میکند.
|
||||||
|
|
||||||
var_90h = rax;
|
var_90h = rax;
|
||||||
rax = strlen (); // محاسبه strlen(s) در هر تکرار
|
// مقدار `RAX` را در `var_90h` ذخیره میکند.
|
||||||
|
|
||||||
|
rax = strlen ();
|
||||||
|
// `strlen` را روی `s` فراخوانی میکند.
|
||||||
|
|
||||||
rcx = var_90h;
|
rcx = var_90h;
|
||||||
if (rcx >= rax) { // شرط خروج از حلقه (j < strlen(s))
|
// مقدار `var_90h` را در `RCX` قرار میدهد.
|
||||||
goto label_4; // پرش به بعد از حلقه
|
|
||||||
|
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;
|
rax = (int64_t) var_50h;
|
||||||
edi = "B\n|_"; // artifact دیکامپایلر
|
// مقدار شمارنده `var_50h` را به `RAX` منتقل میکند.
|
||||||
var_94h = ecx; // ذخیره flag[j]
|
|
||||||
|
edi = "B\n|_";
|
||||||
|
// **نکته:** این هم یک artifact دیکامپایلر است. مربوط به `secret` واقعی نیست.
|
||||||
|
|
||||||
|
var_94h = ecx;
|
||||||
|
// مقدار `flag[j]` (جعلی و رمزنگاری شده) را در `var_94h` ذخیره میکند (معادل `v4` در IDA).
|
||||||
|
|
||||||
var_a0h = rax;
|
var_a0h = rax;
|
||||||
rax = strlen (); // محاسبه strlen(secret)
|
// مقدار `RAX` را در `var_a0h` ذخیره میکند.
|
||||||
|
|
||||||
|
rax = strlen ();
|
||||||
|
// `strlen` را روی `secret` فراخوانی میکند.
|
||||||
|
|
||||||
rdx = var_a0h;
|
rdx = var_a0h;
|
||||||
|
// مقدار `var_a0h` را در `RDX` قرار میدهد.
|
||||||
|
|
||||||
*(rsp) = rax;
|
*(rsp) = rax;
|
||||||
|
// طول `secret` را به صورت موقت در پشته ذخیره میکند.
|
||||||
|
|
||||||
rax = rdx;
|
rax = rdx;
|
||||||
|
// `RDX` (که حاوی `var_50h` بود) را به `RAX` منتقل میکند.
|
||||||
|
|
||||||
ecx = 0;
|
ecx = 0;
|
||||||
|
// `ECX` را به 0 تنظیم میکند.
|
||||||
|
|
||||||
edx = ecx;
|
edx = ecx;
|
||||||
|
// `EDX` را به `ECX` (0) تنظیم میکند.
|
||||||
|
|
||||||
rsi = *(rsp);
|
rsi = *(rsp);
|
||||||
rax = rdx:rax / rsi; // محاسبه j / strlen(secret)
|
// طول `secret` را از پشته بازیابی کرده و در `RSI` قرار میدهد.
|
||||||
rdx = rdx:rax % rsi; // محاسبه j % strlen(secret)
|
|
||||||
ecx = *((rdx + secret)); // دسترسی به secret[j % strlen(secret)]
|
rax = rdx:rax / rsi;
|
||||||
r8d = var_94h; // r8d حاوی flag[j] است
|
// محاسبه `j / strlen(secret)`.
|
||||||
r8d ^= ecx; // عملیات XOR: flag[j] ^ secret[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;
|
rdx = (int64_t) var_50h;
|
||||||
*((rsp + rdx + 0x60)) = r8b; // ذخیره نتیجه در s[j]
|
// مقدار شمارنده `var_50h` را به `RDX` منتقل میکند.
|
||||||
eax = var_50h;
|
|
||||||
eax++; // افزایش j
|
*((rsp + rdx + 0x60)) = r8b;
|
||||||
|
// نتیجه XOR را در `s[j]` ذخیره میکند.
|
||||||
|
// این `s[j]` اکنون حاوی یک کاراکتر از فلگ جعلی رمزگشایی شده است.
|
||||||
|
|
||||||
|
eax++;
|
||||||
|
// شمارنده `eax` را یک واحد افزایش میدهد (معادل `j++`).
|
||||||
|
|
||||||
var_50h = eax;
|
var_50h = eax;
|
||||||
} while (1); // حلقه بیپایان
|
// مقدار افزایش یافته را در `var_50h` ذخیره میکند.
|
||||||
|
|
||||||
|
} while (1);
|
||||||
|
// حلقه بیپایان `do-while`.
|
||||||
label_4:
|
label_4:
|
||||||
rsi = &s;
|
rsi = &s;
|
||||||
rdi = format; // آدرس رشته فرمت برای printf
|
// آدرس بافر `s` (که اکنون حاوی فلگ جعلی رمزگشایی شده است) را در `RSI` قرار میدهد.
|
||||||
|
|
||||||
|
rdi = format;
|
||||||
|
// آدرس رشته فرمت برای `printf` (احتمالاً `"%s"` برای چاپ فلگ) را در `RDI` قرار میدهد.
|
||||||
|
|
||||||
al = 0;
|
al = 0;
|
||||||
printf (rdi); // چاپ نتیجه
|
// `AL` را به 0 تنظیم میکند.
|
||||||
|
|
||||||
|
printf (rdi);
|
||||||
|
// محتوای `s` (فلگ جعلی) را چاپ میکند.
|
||||||
|
|
||||||
label_3:
|
label_3:
|
||||||
var_4h = 0; // تنظیم مقدار بازگشتی به 0 (موفقیت)
|
var_4h = 0;
|
||||||
|
// `var_4h` (کد خروج) را به 0 تنظیم میکند.
|
||||||
label_0:
|
label_0:
|
||||||
eax = 0; // این خط نیز مقدار بازگشتی را 0 تنظیم میکند
|
eax = 0;
|
||||||
return rax; // بازگرداندن وضعیت نهایی
|
// `EAX` را به 0 تنظیم میکند. این خط نهایی است که مقدار بازگشتی تابع `main` را تعیین میکند.
|
||||||
|
|
||||||
|
return rax;
|
||||||
|
// مقدار نهایی `RAX` (0) را به عنوان کد خروج برنامه بازمیگرداند.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -608,7 +903,6 @@ __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` است.
|
# متغیر `what` را تعریف میکند که حاوی بایتهای رشته ثابت `what` است.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue